Daniel A. White
Daniel A. White

Reputation: 190976

Who pays the cost for not passing by const reference

Given:

void foo(std::vector<int> v);

void bar()
{
    std::vector<int> v = ...; // many items
    foo(v);
}

What in a profiling tool would show as the hot path? Would it be the std::vector<T>'s copy constructor, the runtime or the operating system? I remember in school (I'm not a C++ dev, just work with some) that this will copy v and that can take time. I do know that a signature like:

void foo(const std::vector<int>& v);

avoids this potentially costly copy operation.

Upvotes: 5

Views: 223

Answers (4)

bobah
bobah

Reputation: 18864

Copying std::vector<T> by value does, potentially, three things:

  1. Memory manager (C++ runtime or custom allocator) searches for available memory block for the new vector in its stashes. If it can find it, then it goes to step #3.
  2. Memory manager (C++ runtime or custom allocator) requests more memory from the OS. This call itself is relatively cheap (as much as a syscall can be), because the OS gives memory in lazy mode - actual allocation is happening on the first write to the requested VM page.
  3. Compiler generated code (application) does in-place new with copy c-tor for each element in the new vector if T is not trivially copy-constructible. Otherwise, memcpy() or it's optimized vectorized (in sse sense) counterpart is called. Just before the first time the new memory is written, if it was acquired from the OS in #2, the OS will need to actually allocate the new VM page and that may trigger RAM access (hardware), TLB lookup (hardware + OS) swapping in/out (OS).

In your specific example T is trivially copy-constructible so the worst case overhead would be C++ runtime memory block lookup + sbrk() syscall + memcpy() call.

Upvotes: 5

nate
nate

Reputation: 1871

The decision to take an argument by value versus by const reference should take into account what the function plans on doing with the value. If the function only plans on reading from the existing value, then a const reference is the most efficient choice. If the called function would have made a copy of the referenced object as part of its implementation, then passing by value has a potential to be faster as the caller can move construct the argument if it makes sense, and remove the need for a copy.

void foo(std::vector<int> v);

void bar()
{
    std::vector<int> v = ...; // many items
    foo(std::move(v)); // no copy needed here
}

Upvotes: 2

Surt
Surt

Reputation: 16109

The compiler only have to emit code that acts as-if it runs your code, that is the observable behavior is the same.

The likely hotspot should be either the assignment operator in bar as you need to create the original vector or a copy constructor.

In your example the compiler can rewrite it in different ways, if it can reason out that v is never used in bar again so it might implement the call foo(v); as foo(std::move(v)) (like @MSalters already mentioned). This leaves v as a dead object in bar() which is then destroyed.

Alternatively the compiler might just create the vector on the call stack and save itself a call to the destructor, effectively making the call-by-value free.

Upvotes: 1

Bathsheba
Bathsheba

Reputation: 234785

You can expect the copy constructor of std::vector<int> to be called.

A clever compiler is permitted to optimise out the value copy if there are absolutely no side effects in its doing to. Futhermore, if the function definition is marked inline then a value copy will not be taken if the compiler honours your request (it doesn't have to).

You're best bet is indeed to pass v by const reference. Not only does this obviate the value copy, but it also means that the function being called cannot modify the argument passed.

Upvotes: 1

Related Questions