Why returning a vector by reference is much faster than returning by move?

Viewed 187

I have two similar methods, both of which take a std::vector by reference and then one of them tries to return it by reference(since lifetime is satisfied) and another one returns by move. In both cases, I don't need the passed vector afterwards, it's just to satisfy syntactical requirement.

Both functions look like -

std::vector<unique_ptr<Item>> func_returns_move(std::vector<unique_ptr<Item>> &items) {
    items.erase(std::remove_if(items.begin(), items.end(),
    [&](const std::unique_ptr<Item> &item) {
        return item->age > 20;
    }), items.end());
    return move(items);
}


std::vector<unique_ptr<Item>>& func_returns_ref(std::vector<unique_ptr<Item>> &items) {
    items.erase(std::remove_if(items.begin(), items.end(),
    [&](const std::unique_ptr<Item> &item) {
        return item->age > 20;
    }), items.end());
    return items;
}

From my assumption, there shouldn't be much difference here as the only differing line is the last one - one returns by ref and another directly moves. At first I thought maybe the difference was due to move creating one temporary object which might malloc by default or something, that will be the result of function like result = func_returns_move(...) but it should be constant time which is not the case here. The time spent is proportional to number of elements in vector. So I'm failing to understand what is causing more execution in return by std::move case.


Timings are as follows -

FUNC_RETURNS_MOVE: 50
FUNC_RETURNS_REF: 5

Testing program here - https://godbolt.org/z/5Y8Mcw

1 Answers

In the second case, the vector is destroyed at scope exit (the only vector object is items2). This is outside your timed area.

In the first case however, the returned move-constructed vector temporary is destroyed at the end of the full expression, which is within the timed section. items1 is still destroyed at scope exit, but it's empty at that point.

The actual time cost of both cases is essentially identical. But in the first case, the destruction of all the elements is inside the timed region, whereas in the second case it is outside.

If we keep the destruction of the returned vector object outside of the timing, we get consistent results between the two (and can see that the move is indeed "free"): https://godbolt.org/z/z-wKVG

(Due to technical licensing reasons I will not copy the relevant godbolt code here - it would need to be in the question.)

Related