1

Do these 4 ways to write 1 + 1 all have the same performance?

1.

return 1 + 1;

2.

int x = 1 + 1;
return x;

3.

var x = 1 + 1;
return x;

4.

int getInt()
{ return 1 + 1;}
return getInt();
  • 4
    You are never going to be a situation where a performance difference between these syntactic forms is relevant to your application. If you're just curious, look at what the compiler generates. – Asad Saeeduddin Mar 10 '20 at 07:29
  • 1
    The compiler would most likely inline the last example, and in jit all the above to the same – TheGeneral Mar 10 '20 at 07:30
  • 1
    The optimizer will probably generate the same code for all these options – Dmitry Bychenko Mar 10 '20 at 07:31
  • See [this](https://stackoverflow.com/q/356846/1997232) regarding `var`. To compare IL code yourself see [this](https://stackoverflow.com/q/3326571/1997232). Compiler is [smart](https://stackoverflow.com/q/13135759/1997232), so don't rely on results of abstract test, [benchmark](https://stackoverflow.com/q/1622440/1997232) your real code. – Sinatr Mar 10 '20 at 08:07

3 Answers3

10

When you write a C# application it's compiled into an intermediate language, then at run-time further compiled into CPU code. Both these situations allows for optimizations to happen to the program instructions.

In the examples you have given they are basically all compiled out to the same instructions as an afforded optimization taken for your benefit.

Given

public int Method1()
{
   return 1 + 1;
}

public int Method2()
{
   int x = 1 + 1; 
   return x;
}

public int Method3()
{
   var x = 1 + 1;   
   return x;
}

int getInt()
{
   return 1 + 1;
}

public int Method4()
{
   return getInt();
}

Your code would likely be converted to the following instructions

C.Method1()
    L0000: mov eax, 0x2
    L0005: ret

C.Method2()
    L0000: mov eax, 0x2
    L0005: ret

C.Method3()
    L0000: mov eax, 0x2
    L0005: ret

C.getInt()
    L0000: mov eax, 0x2
    L0005: ret

C.Method4()
    L0000: mov eax, 0x2
    L0005: ret

Note : The above is only a representation of how it may all convert to the same instruction set, in reality each method would be inlined in the sharp io example to mov ecx, 0x2 as Hans Passant rightly pointed out in the comments

In short (and in this situation), there is no appreciable difference with your code variants, so it comes down to the way you like to write your code. However in other situations, the framework uses a complex array of rules to govern optimizations that might not be so clear-cut.

On saying all that: it's best not to worry about such optimizations prematurely and focus on writing good clean code that is easily read and maintainable, and if you have a performance problem, then spend the time to profile. Though this seems like a wasteful approach, the time wasted prematurely optimizing is wasted time (unless you have a performance problem). There are bigger fish to fry, and more money to be made.

halfer
  • 19,824
  • 17
  • 99
  • 186
TheGeneral
  • 79,002
  • 9
  • 103
  • 141
  • That's quite misleading. Just as getInt() gets inlined, all of these test methods get inlined as well. In fact the code posted here never even gets generated. Only `mov ecx, 0x2` in C.M() is accurate. – Hans Passant Mar 10 '20 at 09:44
4

As a side note, because it seems to imply you don't unserstand C#'s var correctly, these two examples:

int x = 1 + 1;
return x;

And,

var x = 1 + 1;
return x;

Are exactly the same. var in C# means, "you, compiler, figure out the type of x all by yourself, I've given you enough information to make it possible".

var is not a special type that can hold anything (VB's Variant). The compiler will figure out that var is int because 1 + 1 is an integer constant. If you are using VS (and I'm sure other IDEs), if you hover the cursor over var you'll actually see a popup telling you what type the compiler has managed to infer.

InBetween
  • 32,319
  • 3
  • 50
  • 90
  • Exactly. This is one point that people like to overlook. Var is no a bytecode feature, it is a compiler feature that results in the smallest specific type being used (in this case int) by the compiler. – TomTom Mar 10 '20 at 08:09
3

You will get the exact same results for all methods in Release mode,

for this ->

    private static int SimpleAdd()
    {
        return 1 + 1;
    }

    private static int SimpleAddWithInt()
    {
        int x = 1 + 1;
        return x;
    }
    private static int SimpleAddWithVar()
    {
        var x = 1 + 1;
        return x;
    }
    private static int SimpleAddWithFunc()
    {
        int getInt()
        { return 1 + 1; }
        return getInt();
    }

you'll get this:

private static int SimpleAdd()
{
    return 2;
}

private static int SimpleAddWithInt()
{
    return 2;
}

private static int SimpleAddWithVar()
{
    return 2;
}

private static int SimpleAddWithFunc()
{
    return <SimpleAddWithFunc>g__getInt|4_0();
}

[CompilerGenerated]
private static int <SimpleAddWithFunc>g__getInt|4_0()
{
    return 2;
}

Jitted:

TestPerformace.Program.SimpleAdd()
L0000: mov eax, 0x2
L0005: ret

TestPerformace.Program.SimpleAddWithInt()
L0000: mov eax, 0x2
L0005: ret

TestPerformace.Program.SimpleAddWithVar()
L0000: mov eax, 0x2
L0005: ret

TestPerformace.Program.SimpleAddWithFunc()
L0000: mov eax, 0x2
L0005: ret

Jitted

SharpLab

Ive
  • 1,321
  • 2
  • 17
  • 25
  • This is not accurate. yeah sure the compiler generates a method for the local method, however the jitter is still likely to optimize it, hence they will be the same – TheGeneral Mar 10 '20 at 08:04
  • Look at jitter, they are not the same – Ive Mar 10 '20 at 08:07
  • 1
    For starters you are in debug mode, and secondly when you check the actual method in release it has the same asm :) – TheGeneral Mar 10 '20 at 08:08
  • 1
    Yeah. In debug mode a lot of jitter optimizations are off - hard to put a breakpoint on code that has been optimized away. – TomTom Mar 10 '20 at 08:10