Good day!
I ask you to tell me.
Question: What code will be executed faster and why?
Or there will be no difference in speed, because the link to the area in the heap is transmitted.

Suppose there is some type:

class MyClass { internal int a; public MyClass(int a) { this.a = a; } } 

Option 1

 class Program { static void Main(string[] args) { MyClass myClass = new MyClass(5); Work(myClass.a); } static void Work(int a) { int b = a + 5; Console.WriteLine(b); } } 

Option 2

 class Program { static void Main(string[] args) { MyClass myClass = new MyClass(5); Work(myClass); } static void Work(MyClass myClass) { int b = myClass.a + 5; Console.WriteLine(b); } } 
  • 3
    With such an example as your difference will not be any. But, if in the Work method there were, say, a cycle where, as in Option 2, in each iteration, it would be to retrieve data by reference, then such an option would be slower than Option 1. - Bulson
  • 3
    @ K.Oleg The more inexperienced the programmer, the more he is concerned with the speed sucked from the finger. :) - Vlad from Moscow
  • @Bulson, thanks, that's what I wanted to understand! - K.Oleg
  • 3
    @VladfromMoscow, it’s not a shame not to know, it’s a shame not to learn :) And I care about speed because there are events of 1000+ pcs / s that need to be processed. And when I supplement the processing code with more functionality, I understand that the processing speed is falling in general, so I ask for your comments in order to improve my skills! - K.Oleg
  • 2
    Just keep in mind, to notice such a difference will need cycles with millions of iterations. Therefore, in 99% of cases we can say that this problem is not worth a damn. - Bulson

2 answers 2

All values ​​of variables are passed by value: in reference types, value is a link, and in meaningful types - value.

Specifically in your case:

  • Example 1 Work(myClass.a); - You access the heap and copy the value from the heap, then you are already working with a local variable that will be stored on the stack
  • Example 2 Work(myClass); - You access the myClass variable and copy its value, i.e. reference to the memory area where the object is stored. In this case, the object is a class, so it is stored on the heap. Later in the method, you already access the local variable stored on the heap to retrieve the memory address and retrieve the value of the object.

At first glance, the difference is obvious: it is simpler to pass by value, but imagine a situation where you need to transfer a million values, and use only 1. Then it will be faster to transfer an array of references than an array of values.

If you look at your case, the difference is almost invisible, but the first method will work faster.

You can read about where variables are stored here.

  • Thanks for the detailed answer! - K.Oleg
  • @ K.Oleg this answer will be true only if the compiler does not decide to run the Work directly into Main (i.e., he will most likely decide not to do this, but it’s impossible to say for sure :) - PashaPash
  • @PashaPash, of course, to be sure, you need to take measurements - Vadim Prokopchuk

It will be more honest to answer your question - "it is impossible to predict, take and measure on the real code." And that's why:

Here is the IL into which your examples are compiled:

IL1:

 Main IL_0006: ldc.i4.5 IL_0007: newobj instance void ConsoleApplication15.MyClass::.ctor(int32) IL_000c: ldfld int32 ConsoleApplication15.MyClass::a IL_0011: call void ConsoleApplication15.Program::Work(int32) Work IL_0000: ldarg.0 IL_0001: ldc.i4.5 IL_0002: add IL_0003: call void [mscorlib]System.Console::WriteLine(int32) IL_0008: ret 

IL2:

 Main IL_0006: ldc.i4.5 IL_0007: newobj instance void ConsoleApplication15.MyClass::.ctor(int32) IL_000c: call void ConsoleApplication15.Program::Work(class ConsoleApplication15.MyClass) Work IL_0000: ldarg.0 IL_0001: ldfld int32 ConsoleApplication15.MyClass::a IL_0006: ldc.i4.5 IL_0007: add IL_0008: call void [mscorlib]System.Console::WriteLine(int32) IL_000d: ret 

Ok, what is clear from this code? The compiler has already slightly optimized execution. For example, in IL1.Main, it uses a successful coincidence - the parameters in the function are passed through the stack, the result of the constructor call is also added to the stack - which means that you don't need to make any gestures to pass myClass as the Work parameter. Those. what is visible in C # code:

You access the myClass variable and copy its value, i.e. reference to the memory area where the object is stored

IL does not occur. But all the same - it is clearly visible that in the first code example it is less. So it can be concluded that it works faster. But in fact, it is impossible to draw conclusions from IL. And that's why:


IL code is not executed directly. It is compiled into machine code for a specific platform. And in a specific platform, in addition to the stack, other mechanisms for passing parameters can be used.

In reality, parameters may not be passed through an iron stack. Moreover, in the CLR on x86 / x64, the first two parameters are transmitted through the rcx / rdx registers (ecx / edx). And the result from the method is returned via rax / eax. That's what really compiles your x64 code (on my machine):

ASM1:

 Main MyClass myClass = new MyClass(5); 00007FF8F6F20489 mov rcx,7FF8F6E15A88h 00007FF8F6F20493 call 00007FF956512510 00007FF8F6F20498 mov dword ptr [rax+8],5 Work(myClass.a); 00007FF8F6F2049F mov ecx,dword ptr [rax+8] 00007FF8F6F204A2 call 00007FF8F6F20080 Work: 00007FF8F6F204D4 add ecx,5 00007FF8F6F204D7 call 00007FF954497BE0 

ASM2:

 Main: MyClass myClass = new MyClass(5); 00007FF8F6EF0489 mov rcx,7FF8F6DE5A98h 00007FF8F6EF0493 call 00007FF956512510 00007FF8F6EF0498 mov rcx,rax 00007FF8F6EF049B mov dword ptr [rcx+8],5 Work(myClass); 00007FF8F6EF04A2 call 00007FF8F6EF0088 Work 00007FF8F6F204D4 mov ecx,dword ptr [rcx+8] 00007FF8F6F204D7 add ecx,5 00007FF8F6F204DA call 00007FF954497BE0 

Ok, how does this differ from IL? That the stack is not used. Totally. The optimizer stuffed everything into registers.

What is the difference between the options? One instruction:

  00007FF8F6EF0498 mov rcx,rax 

Mov from register to register takes 1 processor clock. Those. if any of the examples will work faster on x64 - you probably won't notice this.

Performance depends on too many parameters — the platform, the JIT version, the type of compilation — and it is impossible to take all of them into account when writing code. Do not bother. Solve problems as they arise.

  • thanks for your reply! - K.Oleg