13

I used to be a C++ programer on Windows. I know that the compiler will optimizes the ternary operator in C++.

C++ code:

#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
    int result = argc > 3 ? 1 : 5;
    printf("%d", result);
    return 0;
}

Because of the pipeline stuff, the generated native code is shown as below (of course Release model):

int result = argc > 3 ? 1 : 5;
00B21003  xor         eax,eax  
00B21005  cmp         dword ptr [argc],3
00B21009  setle       al  
00B2100C  lea         eax,[eax*4+1]

C# code:

namespace TernaryOperatorCSharp
{
    static void Main(string[] args)
    {
        int argc = args.Length;
        int result = argc > 1 ? 2 : 5;
        System.Console.WriteLine(result);
    }
}

I looked up the native code JIT generated, but there is no optimization at all (still two jump instructions).

int result = argc > 1 ? 2 : 5;
0000002f  cmp         dword ptr [ebp-4],1 
00000033  jg          0000003F 
00000035  nop 
00000036  mov         dword ptr [ebp-0Ch],5 
0000003d  jmp         00000046 
0000003f  mov         dword ptr [ebp-0Ch],2 
00000046  mov         eax,dword ptr [ebp-0Ch] 
00000049  mov         dword ptr [ebp-8],eax 
System.Console.WriteLine(result);
0000004c  mov         ecx,dword ptr [ebp-8] 
0000004f  call        6A423CBC 

Why doesn't the C# JIT compiler make the same optimization as C++ compiler does?

What's the story behind this?

Any information will be appreciated.


Hi there, I have modified the C# program and run it with release model.

Before

int result = args.Length > 1 ? 2 : 5;

Now

int argc = args.Length;
int result = argc > 1 ? 2 : 5;

But the result is still the same. There still two jump instruction exist. I will appreciate it if there is any further more information.

Michael
  • 203
  • 2
  • 10
  • 1
    The first conditional is presumably dealing with the case where `args == null`, or something like that. – Oliver Charlesworth Dec 29 '13 at 16:26
  • 3
    It's also highly likely that the JIT sees no reason to optimise this code, because it only gets run once. – Oliver Charlesworth Dec 29 '13 at 16:27
  • Well, that would be easy to check, stick the code in a loop. – hyde Dec 29 '13 at 17:36
  • Maybe anwer is that C++ is hybrid language, have structuiral types, and optimisation for ? operator is taken from C. And C# doesen't have classical types, int is class. So operators are overloaded in that class definition. And when you write int result = args.Length > 1 ? 2 : 5; It is calling for method in class int – RockLegend Dec 29 '13 at 18:41
  • 3
    @RockLegend `int` is not a class and the operators are not defined in that class (how would they even be defined?), `int` is Special Magic and has MSIL instructions for its operators (not calls). – harold Dec 29 '13 at 18:47

2 Answers2

11

You're not compiling with optimizations - the nop instruction is indicative of this (the compiler inserts these to use as anchors, so that you can put a breakpoint on a brace).

Visual Studio won't always produce optimized code, even if you have the 'optimize code' checkbox checked. Often when you launch in the debugger, it will disable optimizations anyway so that the debugging session behaves moreso as you would expect.

Furthermore, you're not comparing apples to apples, which accounts for a lot more of the cruft.

string[].Length is a property in C#, not a public variable, and furthermore, not a local variable. Properties are often treated like public variables by consuming code, but in reality can exist as full-blown get/set methods. The compiler has to emit code to handle this, especially when the Property is defined in a separate assembly.

Try the example with a local int variable, and with compiler optimizations turned on (build with optimizations, launch the program, attach the debugger after launching, view disassembly).

antiduh
  • 11,853
  • 4
  • 43
  • 66
  • I think it's more correct to that the jitter won't produce optimized code when the program is being run under the debugger. – Michael Burr Dec 29 '13 at 18:53
  • 4
    Note also that jitting is a balance between compile time and run time. The more optimizations you add to the jitter, the more time your program spends jitting and not doing actual work. – Raymond Chen Dec 29 '13 at 19:08
  • And to that effect, if the program is installed in the gac, the ngen program can do more aggressive optimizations. – antiduh Dec 29 '13 at 19:33
  • @antiduh Thank you for your reply. :) But I do made the C# compiler build code with release model. – Michael Dec 30 '13 at 16:04
  • 2
    @Michael IL does not have a way of expressing the concept of `setX`. Converting a `comparison + jump conditional + load 1 + jump + load 2` sequence into `comparison + setX + weird math` is done by the jitter, and the jitter is very time-constrained. Given that this optimization works only for integers, it is likely that the authors of the jitter decided that the cost of the optimization (the time cost of detecting that the optimization is possible) does not outweigh its runtime benefit. – Raymond Chen Dec 30 '13 at 16:29
4

You are looking at the Debug build of the program. Switch to the Release build.

And you'll have to change an option so the optimizer doesn't get disabled when you use the debugger to look at the disassembly. Tools + Options, Debugging, General, untick the "Suppress JIT optimization on module load" option.

You'll now see more compact code. The x86 jitter does perform branch elimination and uses the AGU for math, you can see it being done in this answer, just not here. You are going to be disappointed if you expect exact parity with the optimizer of a C or C++ compiler, the jitter optimizer operates under pretty strict time constraints since it operates at runtime. You'll find an outline of optimizations it performs in this answer.

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • Thank you Hans, I forgot how to force it to always enable optimizations and didn't have VS handy. – antiduh Dec 29 '13 at 22:01
  • @Hans I have done what you told, but the compiler can not debug the code any more. – Michael Dec 30 '13 at 16:35
  • 1
    You should use to debugger instead, compilers just compile. Debugging optimized code is certainly possible, just awkward. Just like it is when you use a C++ compiler. – Hans Passant Dec 30 '13 at 17:08