Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatic xvalue optimization

Somewhat surprisingly (to me), the following two programs compile to different outputs, with the latter one having much better performance (tested with gcc and clang):

#include <vector>
int main()
{
    std::vector<int> a(2<<20);
    for(std::size_t i = 0; i != 1000; ++i) {
        std::vector<int> b(2<<20);
        a = b;
    }
}

vs.

#include <vector>
int main()
{
    std::vector<int> a(2<<20);
    for(std::size_t i = 0; i != 1000; ++i) {
        std::vector<int> b(2<<20);
        a = std::move(b);
    }
}

Could someone explain to me why the compiler does (or can) not automatically consider b an xvalue in the last assignment and apply move semantics without the explicit std::move cast?

Edit: Compiled with (g++|clang++) -std=c++11 -O3 -o test test.cpp

like image 399
Xoph Avatar asked Sep 24 '14 09:09

Xoph


2 Answers

Compilers can't break the as-if rule

As §1.9/1 states:

The semantic descriptions in this International Standard define a parameterized nondeterministic abstract machine. This International Standard places no requirement on the structure of conforming implementations. In particular, they need not copy or emulate the structure of the abstract machine. Rather, conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained below

i.e. a compiler can't change the observable behavior of the program. Automatically (even if with no repercussions) converting an assignment to a move assignment would break this statement.

Copy elisions can slightly alter this behavior, but that is regulated by §12.8/31.

If you want to use the move version, you'll have to explicitly ask for it as in the latter example.

like image 144
Marco A. Avatar answered Oct 20 '22 00:10

Marco A.


Let's look at the next sample (please ignore void return type from operator=):

#include <iostream>

struct helper
{
    void operator=(helper&&){std::cout<<"move"<<std::endl;}
    void operator=(const helper&){std::cout<<"copy"<<std::endl;}
};

void fun()
{
    helper a;
    {
        helper b;
        a = b;
    }
}

void gun()
{
    helper a;
    {
        helper b;
        a = std::move(b);
    }
}
int main()
{
    fun();
    gun();
}

The operator= has different behaviour depending on its arguments. The compiler is allowed to optimise the code only if it is able to maintain the observable behaviour the same.

Considering b from fun an xvalue, while it isn't an xvalue at the moment of the call it will change the observable behaviour of the program and this is not desired nor allowed by the standard.

like image 45
Mircea Ispas Avatar answered Oct 20 '22 01:10

Mircea Ispas