4

Why does the compiler do to optimize my code?

I have 2 functions:

public void x1() {
  x++;
  x++;
}
public void x2() {
  x += 2;
}
public void x3() {
  x = x + 2;
}
public void y3() {
  x = x * x + x * x;
}

And that is what I can see with ILSpy after compiling in Release mode:

// test1.Something
public void x1()
{
    this.x++;
    this.x++;
}

// test1.Something
public void x2()
{
    this.x += 2;
}
// test1.Something
public void x3()
{ 
    this.x += 2;
}
// test1.Something
public void y3()
{
    this.x = this.x * this.x + this.x * this.x;
}

x2 and x3 might be ok. But why is x1 not optimized to the same result? There is not reason to keep it a 2 step increment? And why is y3 not x=2*(x*x)? Shouldn't that be faster than x*x+x*x?

That leads to the question? What kind of optimization does the C# compiler if not such simple things?

When I read articles about writing code you often hear, write it readable and the compiler will do the rest. But in this case the compiler does nearly nothing.


Adding one more example:

public void x1() {
  int a = 1;
  int b = 1;
  int c = 1;
  x = a + b + c;
}

and using ILSpy:

// test1.Something
public void x1()
{
    int a = 1;
    int b = 1;
    int c = 1;
    this.x = a + b + c;
}

Why is it not this.x = 3?

6
  • 1
    What makes you believe that x=2*(x*x) is more optimized than x = x * x + x * x? Readability or reduction in terms does not mean the formula operations are more optimized, sometimes expanding it means its more optimized. Commented Jul 30, 2015 at 18:18
  • 1
    1. Most optimizations are performed by the JIT compiler at run-time and not by the C# compiler. 2. Your suggested optimization for x1 is not functionally equivalent in the face of multithreading (if the field were volatile, for example, the compiler is definitely disallowed from performing the suggested optimization). Here's an admittedly old article on the optimizations the C# compiler does perform: blogs.msdn.com/b/ericlippert/archive/2009/06/11/… Commented Jul 30, 2015 at 18:19
  • Less code does not mean better performance Commented Jul 30, 2015 at 18:20
  • Also, x++ is special case because it involves caching the value to be returned and then incrementing it. Commented Jul 30, 2015 at 18:21
  • Are 2 multiplications not faster than 2 multipications and a addition? Commented Jul 30, 2015 at 18:21

2 Answers 2

6

The compiler cannot perform this optimization without making an assumption that variable x is not accessed concurrently with your running method. Otherwise it risks changing the behavior of your method in a detectable way.

Consider a situation when the object referenced by this is accessed concurrently from two threads. Tread A repeatedly sets x to zero; thread B repeatedly calls x1().

If the compiler optimizes x1 to be an equivalent of x2, the two observable states for x after your experiment would be 0 and 2:

  • If A finishes before B, you get 2
  • If B finishes before A, you get 0

If A pre-empts B in the middle, you would still get a 2.

However, the original version of x1 allows for three outcomes: x can end up being 0, 1, or 2.

  • If A finishes before B, you get 2
  • If B finishes before A, you get 0
  • If B gets pre-empted after the first increment, then A finishes, and then B runs to completion, you get 1.
Sign up to request clarification or add additional context in comments.

Comments

4

x1 and x2 are NOT the same:

if x were a public field and was accessed in a multi-threaded environment, it's entirely possible that a second thread mutates x between the two calls, which would not be possible with the code in x2.

For y2, if + and/or * were overloaded for the type of x then x*x + x*x could be different than 2*x*x.

The compiler will optimize things like (not an exhaustive list my any means):

  • removing local variables that are not used (freeing up registers)
  • removing code that does not affect the logic flow or the output.
  • inlining calls to simple methods

Compiler optimization should NOT change the behavior of the program (although it does happen). So reordering/combining math operations are out of scope of optimization.

write it readable and the compiler will do the rest.

Well, the compiler may do some optimization, but there is still a LOT that can be done to improve performance at design-time. Yes readable code is definitely valuable, but the compiler's job is to generate working IL that corresponds with your source code, not to change your source code to be faster.

6 Comments

I added a new example that has nothing to do with multithreading? --- By the way, if the compiler can reorder instruction to optimize performance and you need to take special actions like MemoryFences to prevent that, why is combining x++ and x++ different? Without locks or MemoryBarriear I would expect that the compiler does not care about multithreading.
I think you're expecting too much of the compiler. Its job is to turn your source code into working IL, not to find every potential place that your code could be faster and re-code it for you. If you feel that this is a valuable feature of the next C# compiler then post it on connect.microsoft.com and see what happens.
Plus, why stop there? Why not change your List to an array? Why not change your series of if statements to a switch? Or remove an if block altogether if the condition could never be true? That's a lot of logic to build into a compiler that many would complain isn't fast enough already.
The base answer to our question is: any feature has to be designed, coded, tested, documented, and supported, all of which costs time any money that many other features are competing for. If the benefit of such a feature does not outweigh the costs (or if other features provide more benefit) then it does not get produced. My guess the benefit of such an optimization is minimal.
@bebo The compiler generates IL, which it doesn't even try to optimize. It's the JIT's job to perform platform-dependent optimization on this. It's possible some architectures will execute x++; x++; faster, and other architectures will execute x += 2; faster. That being said, I doubt the JIT would perform this particular optimization, it's not something that would show up in real code. BTW the compiler doesn't inline calls, but the JIT definitely does it.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.