Search code examples
c++c++11constantsmoveconst-reference

C++, const reference is actually faster than move?


After testing this code:

#include <iostream>
#include <chrono>
#include <vector>
#include <string>


void x(std::vector<std::string>&& v){ }

void y(const std::vector<std::string>& v) { }

int main() {
    std::vector<std::string> v = {};

    auto tp = std::chrono::high_resolution_clock::now();

    for (int i = 0; i < 1000000000; ++i)
        x(std::move(v));

    auto t2 = std::chrono::high_resolution_clock::now();

    auto time = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - tp);

    std::cout << "1- It took: "  << time.count() << " seconds\n";

    tp = std::chrono::high_resolution_clock::now();

    for (int i = 0; i < 1000000000; ++i)
        y(v);

    t2 = std::chrono::high_resolution_clock::now();

    time = std::chrono::duration_cast<std::chrono::duration<double>>(t2 - tp);

    std::cout << "2- It took: " << time.count() << " seconds\n";

    std::cin.get();
}

I get that using const-reference is actually ~15s faster than using move semantics, why is that? I thought that move semantics were faster, else, why would they add them? What did I get wrong about move semantics? thanks


Solution

  • Your code makes no sense. Here is a simpler version of your code, substituted with int and cleaned up. Here is the assembly version of the code, compiled with -std=c++11 -02:

    https://goo.gl/6MWLNp

    There is NO difference between the assembly for the rvalue and lvalue functions. Whatever is the cause doesn't matter because the test itself doesn't make use of move semantics.

    The reason is probably because the compiler optimizes both functions to the same assembly. You're not doing anything with either, so there's no point in doing anything different in the assembly than a simple ret.

    Here is a better example, this time, swapping the first two items in the vector:

    https://goo.gl/Sp6sk4

    Ironically, you can see that the second function actually just calls the rvalue reference version automatically as a part of its execution.

    Assuming that a function A which calls B is slower than just executing the function B, the speed of x() should outperform y().


    std::move() itself has an additional cost. All things else being constant, calling std::move() is more costly than not calling std::move(). This is why the "move semantics" is slower in the code you gave us. In reality, the code is slower because you're not actually doing anything--both functions simply return as soon as they execute. You can also see that one version appears to call std::move() while the other doesn't.

    Edit: The above doesn't appear to be true. std::move() is not usually a true function call; it is mainly a static_cast<T&&> that depends on some template stuff.

    In the example I gave you, I'm actually making use of the move semantics. Most of the assembly is more important, but you can see that y() calls x() as a part of its execution. y() should therefore be slower than x().

    tl;dr: You're not actually using move semantics because your functions don't need to do anything at all. Make the functions use copying/moving, and you'll see that even the assembly uses part of the "move semantics" code as a part of its copying code.