Pass by value faster than pass by reference

后端 未结 8 2036
不思量自难忘°
不思量自难忘° 2020-12-23 02:48

I made a simple program in c++ to compare performance between two approaches - pass by value and pass by reference. Actually pass by value performed better than pass by refe

相关标签:
8条回答
  • 2020-12-23 02:49

    A good way to find out why there are any differences is to check the disassembly. Here are the results I got on my machine with Visual Studio 2012.

    With optimization flags, both functions generate the same code:

    009D1270 57                   push        edi  
    009D1271 FF 15 D4 30 9D 00    call        dword ptr ds:[9D30D4h]  
    009D1277 8B F8                mov         edi,eax  
    009D1279 FF 15 D4 30 9D 00    call        dword ptr ds:[9D30D4h]  
    009D127F 8B 0D 48 30 9D 00    mov         ecx,dword ptr ds:[9D3048h]  
    009D1285 2B C7                sub         eax,edi  
    009D1287 50                   push        eax  
    009D1288 E8 A3 04 00 00       call        std::operator<<<std::char_traits<char> > (09D1730h)  
    009D128D 8B C8                mov         ecx,eax  
    009D128F FF 15 2C 30 9D 00    call        dword ptr ds:[9D302Ch]  
    009D1295 33 C0                xor         eax,eax  
    009D1297 5F                   pop         edi  
    009D1298 C3                   ret  
    

    This is basically equivalent to:

    int main ()
    {
        clock_t start, stop ;
        start = clock () ;
        stop = clock () ;
        cout << "time: " << stop - start ;
        return 0 ;
    }
    

    Without optimization flags, you will probably get different results.

    function (no optimizations):

    00114890 55                   push        ebp  
    00114891 8B EC                mov         ebp,esp  
    00114893 81 EC C0 00 00 00    sub         esp,0C0h  
    00114899 53                   push        ebx  
    0011489A 56                   push        esi  
    0011489B 57                   push        edi  
    0011489C 8D BD 40 FF FF FF    lea         edi,[ebp-0C0h]  
    001148A2 B9 30 00 00 00       mov         ecx,30h  
    001148A7 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
    001148AC F3 AB                rep stos    dword ptr es:[edi]  
    001148AE 8B 45 08             mov         eax,dword ptr [ptr]  
    001148B1 8B 08                mov         ecx,dword ptr [eax]  
    001148B3 6B C9 05             imul        ecx,ecx,5  
    001148B6 8B 55 08             mov         edx,dword ptr [ptr]  
    001148B9 89 0A                mov         dword ptr [edx],ecx  
    001148BB 5F                   pop         edi  
    001148BC 5E                   pop         esi  
    001148BD 5B                   pop         ebx  
    001148BE 8B E5                mov         esp,ebp  
    001148C0 5D                   pop         ebp  
    001148C1 C3                   ret 
    

    function2 (no optimizations)

    00FF4850 55                   push        ebp  
    00FF4851 8B EC                mov         ebp,esp  
    00FF4853 81 EC C0 00 00 00    sub         esp,0C0h  
    00FF4859 53                   push        ebx  
    00FF485A 56                   push        esi  
    00FF485B 57                   push        edi  
    00FF485C 8D BD 40 FF FF FF    lea         edi,[ebp-0C0h]  
    00FF4862 B9 30 00 00 00       mov         ecx,30h  
    00FF4867 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
    00FF486C F3 AB                rep stos    dword ptr es:[edi]  
    00FF486E 8B 45 08             mov         eax,dword ptr [val]  
    00FF4871 6B C0 05             imul        eax,eax,5  
    00FF4874 89 45 08             mov         dword ptr [val],eax  
    00FF4877 5F                   pop         edi  
    00FF4878 5E                   pop         esi  
    00FF4879 5B                   pop         ebx  
    00FF487A 8B E5                mov         esp,ebp  
    00FF487C 5D                   pop         ebp  
    00FF487D C3                   ret  
    

    Why is pass by value faster (in the no optimization case)?

    Well, function() has two extra mov operations. Let's take a look at the first extra mov operation:

    001148AE 8B 45 08             mov         eax,dword ptr [ptr]  
    001148B1 8B 08                mov         ecx,dword ptr [eax]  
    001148B3 6B C9 05             imul        ecx,ecx,5
    

    Here we are dereferencing the pointer. In function2 (), we already have the value, so we avoid this step. We first move the address of the pointer into register eax. Then we move the value of the pointer into register ecx. Finally, we multiply the value by five.

    Let's look at the second extra mov operation:

    001148B3 6B C9 05             imul        ecx,ecx,5  
    001148B6 8B 55 08             mov         edx,dword ptr [ptr]  
    001148B9 89 0A                mov         dword ptr [edx],ecx 
    

    Now we are moving backwards. We have just finished multiplying the value by 5, and we need to place the value back into the memory address.

    Because function2 () does not have to deal with referencing and dereferencing a pointer, it gets to skip these two extra mov operations.

    0 讨论(0)
  • 2020-12-23 02:56

    When you pass by value, you are telling the compiler to make a copy of the entity you are passing by value.

    When you are passing by reference, you are telling the compiler that it must use the actual memory that the reference is pointing to. The compiler does not know if you are doing this in an attempt to optimize, or because the referenced value might be changing in some other thread (for example). It has to use that area of memory.

    Passing by reference means the processor has to access that specific memory block. That may or may not be the most efficient process, depending on the state of the registers. When you pass by reference, the memory on the stack can be used, which increases the chance of accessing cache (much faster) memory.

    Finally, depending on the architecture of your machine and the type you are passing, a reference may actually be larger than the value you are copying. Copying a 32 bit integer involves copying less than passing a reference on a 64 bit machine.

    So passing by reference should only be done when you need a reference (to mutate the value, or because the value might be mutated elsewhere), or when copying the referenced object is more expensive than dereferencing the necessary memory.

    While that last point is non-trivial, a good rule of thumb is to do what Java does: pass fundamental types by value, and complex types by (const) reference.

    0 讨论(0)
  • 2020-12-23 02:57

    Passing by value is often very quick for small types since most of them are smaller than the pointer on modern systems (64bit). There may also be certain optimizations done when passed by value.

    As a general rule, pass builtin-types by value.

    0 讨论(0)
  • 2020-12-23 02:58

    In this case, the compiler probably realized that the result of the multiply wasn't being used in the pass-by-value case and optimized it out entirely. Without seeing the disassembled code it's impossible to be sure.

    0 讨论(0)
  • 2020-12-23 03:03

    Quite often executing 32 bit memory manipulation instructions is slower on a native 64 bit platform, because the processor has to run 64 bit instructions regardless. If it is done correctly by the compiler, 32 bit instructions get "paired" at the instruction cache, but if a 32 bit read is executed with a 64 bit instruction 4 additional bytes are copied as filling and then discarded. In short, value being smaller than pointer size does not necessarily mean it's faster. It depends on the situation and on the compiler, and should absolutely not be taken into consideration for performance except for composite types where the value is definitely larger than the pointer by a magnitude of 1, or in cases where you need the absolute best performance for one particular platform without regards to portability. The choice between passing by reference or by value should depend only on whether or not you want the called procedure to be able to modify the object passed. If it's only a read for a type smaller than 128 bits, pass by value, it's safer.

    0 讨论(0)
  • 2020-12-23 03:05

    Overhead with passing by reference:

    • each access needs a dereference, i.e., there is one more memory read

    Overhead with passing by value:

    • the value needs to be copied on stack or into registers

    For small objects, such as an integer, passing by value will be faster. For bigger objects (for example a large structure), the copying would create too much overhead so passing by reference will be faster.

    0 讨论(0)
提交回复
热议问题