Pass by Reference More Expensive Than Pass by Value

Pass by reference more expensive than pass by value

Prefer passing primitive types (int, char, float, ...) and POD structs that are cheap to copy (Point, complex) by value.

This will be more efficient than the indirection required when passing by reference.

See Boost's Call Traits.

The template class call_traits<T> encapsulates the "best" method to pass a parameter of some type T to or from a function, and consists of a collection of typedefs defined as in the table below. The purpose of call_traits is to ensure that problems like "references to references" never occur, and that parameters are passed in the most efficient manner possible.

Performance cost of passing by value vs. by reference or by pointer?

It depends on what you mean by "cost", and properties of the host system (hardware, operating system) with respect to operations.

If your cost measure is memory usage, then the calculation of cost is obvious - add up the sizes of whatever is being copied.

If your measure is execution speed (or "efficiency") then the game is different. Hardware (and operating systems and compiler) tend to be optimised for performance of operations on copying things of particular sizes, by virtue of dedicated circuits (machine registers, and how they are used).

It is common, for example, for a machine to have an architecture (machine registers, memory architecture, etc) which result in a "sweet spot" - copying variables of some size is most "efficient", but copying larger OR SMALLER variables is less so. Larger variables will cost more to copy, because there may be a need to do multiple copies of smaller chunks. Smaller ones may also cost more, because the compiler needs to copy the smaller value into a larger variable (or register), do the operations on it, then copy the value back.

Examples with floating point include some cray supercomputers, which natively support double precision floating point (aka double in C++), and all operations on single precision (aka float in C++) are emulated in software. Some older 32-bit x86 CPUs also worked internally with 32-bit integers, and operations on 16-bit integers required more clock cycles due to translation to/from 32-bit (this is not true with more modern 32-bit or 64-bit x86 processors, as they allow copying 16-bit integers to/from 32-bit registers, and operating on them, with fewer such penalties).

It is a bit of a no-brainer that copying a very large structure by value will be less efficient than creating and copying its address. But, because of factors like the above, the cross-over point between "best to copy something of that size by value" and "best to pass its address" is less clear.

Pointers and references tend to be implemented in a similar manner (e.g. pass by reference can be implemented in the same way as passing a pointer) but that is not guaranteed.

The only way to be sure is to measure it. And realise that the measurements will vary between systems.

C++: Why pass-by-value is generally more efficient than pass-by-reference for built-in (i.e., C-like) types

A compiler vendor would typically implement a reference as a pointer. Pointers tend to be the same size as or larger than many of the built-in types. For these built-in types the same amount of data would be passed whether you passed by value or by reference. In the function, in order to get the actual data, you would however need to dereference this internal pointer. This can add an instruction to the generated code, and you will also have two memory locations that may not be in cache. The difference won't be much - but it could be measured in tight loops.

A compiler vendor could choose to disregard const references (and sometimes also non-const references) when they're used on built-in types - all depending on the information available to the compiler when it deals with the function and its callers.

Is it better in C++ to pass by value or pass by reference-to-const?

It used to be generally recommended best practice1 to use pass by const ref for all types, except for builtin types (char, int, double, etc.), for iterators and for function objects (lambdas, classes deriving from std::*_function).

This was especially true before the existence of move semantics. The reason is simple: if you passed by value, a copy of the object had to be made and, except for very small objects, this is always more expensive than passing a reference.

With C++11, we have gained move semantics. In a nutshell, move semantics permit that, in some cases, an object can be passed “by value” without copying it. In particular, this is the case when the object that you are passing is an rvalue.

In itself, moving an object is still at least as expensive as passing by reference. However, in many cases a function will internally copy an object anyway — i.e. it will take ownership of the argument.2

In these situations we have the following (simplified) trade-off:

  1. We can pass the object by reference, then copy internally.
  2. We can pass the object by value.

“Pass by value” still causes the object to be copied, unless the object is an rvalue. In the case of an rvalue, the object can be moved instead, so that the second case is suddenly no longer “copy, then move” but “move, then (potentially) move again”.

For large objects that implement proper move constructors (such as vectors, strings …), the second case is then vastly more efficient than the first. Therefore, it is recommended to use pass by value if the function takes ownership of the argument, and if the object type supports efficient moving.


A historical note:

In fact, any modern compiler should be able to figure out when passing by value is expensive, and implicitly convert the call to use a const ref if possible.

In theory. In practice, compilers can’t always change this without breaking the function’s binary interface. In some special cases (when the function is inlined) the copy will actually be elided if the compiler can figure out that the original object won’t be changed through the actions in the function.

But in general the compiler can’t determine this, and the advent of move semantics in C++ has made this optimisation much less relevant.


1 E.g. in Scott Meyers, Effective C++.

2 This is especially often true for object constructors, which may take arguments and store them internally to be part of the constructed object’s state.

Which is faster? Pass by reference vs pass by value C++

The answer to "Which is faster" is usually "It depends".

If instead of passing four bytes of data you are passing an eight byte pointer to data, then you can't really expect that to make things faster. If instead of passing 100 bytes of data you are passing an eight byte pointer to data, that's different.

But now the function doesn't have the data, it only has a reference. So whenever it needs to read the data, it has to do that indirectly through the reference. That takes longer. If you pass a 100 byte object and only read eight byte of it, you still are likely to win. But if you actually read all the data, and maybe multiple times, then it could easily be faster to pass the value even for large objects.

The real difference comes when you pass an object, and passing by value means a more or less complex constructor will be called. Passing by reference means no constructor. But int has no constructor anyway.

And then there is optimisation. Passing by value means the compiler knows your function is the only one with access to the data. Pass by reference means the data could be anywhere. If you have two int& parameters, I could pass the some int twice. So increasing row might increase pos. Or it might not. That kills optimisations.

And then there is the rule of optimisation: "Measure it". You measured it and found what's faster. Sometimes things are faster or slower for no good reason whatsoever.

Pass by value faster than pass by reference

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.

Advantages of pass-by-value and std::move over pass-by-reference

  1. Did I understand correctly what is happening here?

Yes.


  1. Is there any upside of using std::move over passing by reference and just calling m_name{name}?

An easy to grasp function signature without any additional overloads. The signature immediately reveals that the argument will be copied - this saves callers from wondering whether a const std::string& reference might be stored as a data member, possibly becoming a dangling reference later on. And there is no need to overload on std::string&& name and const std::string& arguments to avoid unnecessary copies when rvalues are passed to the function. Passing an lvalue

std::string nameString("Alex");
Creature c(nameString);

to the function that takes its argument by value causes one copy and one move construction. Passing an rvalue to the same function

std::string nameString("Alex");
Creature c(std::move(nameString));

causes two move constructions. In contrast, when the function parameter is const std::string&, there will always be a copy, even when passing an rvalue argument. This is clearly an advantage as long as the argument type is cheap to move-construct (this is the case for std::string).

But there is a downside to consider: the reasoning doesn't work for functions that assign the function argument to another variable (instead of initializing it):

void setName(std::string name)
{
m_name = std::move(name);
}

will cause a deallocation of the resource that m_name refers to before it's reassigned. I recommend reading Item 41 in Effective Modern C++ and also this question.

Pass by value vs Pass by reference(difference in space allocation of memory between the two)

In C++ where we use pass by reference we reference the address of whatever it is that we passed
from the argument to the parameter of the function which is essentially a pointer right?

No. A reference is an alias (ie an alternative name) for an existing variable.

But at the assembly level your implementation may put the address of the referenced variable in an address register (or something similar) for the called function to use (if that is what you mean).

But for simplification you can think of it as a pointer that is automatically de-referenced (that is what I did when I first started). But when you get into the language references are actually fundamentally different from pointers.

So while they are essentially the same thing, alias and all, doesnt a pointer require memory space as well?

A pointer at the C++ level requies space (as it is addressable). You can take the address of a pointer. Fundamentally a reference does not require space (as you can't take its address). At an implementation level it may or may not have a physical memory location depending on how the compiler is implementing it.

So shouldnt whatever we have in a parameter function let us call B point to the memory location of whatever the argument was that was passed let us call A

It would have been nice if you had explained the above with a code example. But I think I understand. Assuming the function is not inlined then any parameter passed as reference needs some form of link back to the original object (as references always fundamentally refer to live objects). So how does it do. Compiler implementation detail (so you should not care). But probably a pointer on the stack or maybe just an address in an address register.

which in turn is the memory location of our value( since A passed the memory location of our value as the argument)?

Maybe or not. References do not have a physical location at the language level. So the compiler can play lots of nice little tricks with that.

In java where we use pass by value we make a copy of the address of whatever it was we passed(the reference to the object for example).

In Java you pass references by value. But Java references are fundamentally just pointers at a memory location. So you are passing a pointer by value. Which is one technique to use. Luckily C++ does not limit you to a single technique.

You can pass parameters by value or reference. You can even pass a pointer to an object by value or reference. So a couple of interesting techniques to use depending on the situation.

So in the end i'm not truely seeing the difference between pass by value and pass by reference.

Maybe that is because you are thinking about java references (which are passed by value).

In C++. If you pass by value, you are creating a new object that is passed as the parameter (which means you make a copy of the original, which can cost). If you pass by reference you are passing an alias to an object. So when you interact with the object you are modifying the original object.

 int inc(int val)     // pass by value
{
return ++val; // increment the passed value and return as a result.
}

int incref(int& val) // pass by reference
{
return ++val; // increment the reference.
// Since the reference is an alias this increment affects the
// original object. The result is returned.
}

void code()
{
int x = 5;
int y = inc(x); // x =5 and y = 6

int a = 8;
int b = incref(a); // a = 9 and b = 9
}

Pass by value allocates space in memory for the original passed argument and the copy that both point to the value and pass by reference passes the memory location of our value as the argument which the parameter(the pointer which allocates space in the memory ) in our function uses to point to the value.

Sorry I lost that.



Related Topics



Leave a reply



Submit