ala*_*rge 205 c++ performance move-semantics c++11 c++14
有时声称即使仅编译C++ 98代码,C++ 11/14也可以提高性能.理由通常是移动语义,因为在某些情况下,rvalue构造函数是自动生成的,或者现在是STL的一部分.现在我想知道这些案例以前是否已经由RVO或类似的编译器优化处理过了.
那么我的问题是,如果你能给我一个C++ 98代码的实际例子,使用支持新语言特性的编译器,无需修改就可以更快地运行.我确实理解标准符合编译器不需要执行复制省略,因此移动语义可能会带来速度,但我希望看到一个较少病态的案例,如果你愿意的话.
编辑:为了清楚,我不是在问新的编译器是否比旧的编译器更快,而是如果有代码将-std = c ++ 14添加到我的编译器标志它会运行得更快(避免副本,但如果你除了移动语义之外还能提出其他任何东西,我也会感兴趣)
Yak*_*ont 221
我知道重新编译C++ 03编译器的5个常规类别,因为C++ 11可以导致无限的性能提升,实际上与实现质量无关.这些都是移动语义的变体.
std::vector 重新分配struct bar{
std::vector<int> data;
};
std::vector<bar> foo(1);
foo.back().data.push_back(3);
foo.reserve(10); // two allocations and a delete occur in C++03
Run Code Online (Sandbox Code Playgroud)
每次时间foo的缓冲器被在C++ 03重新分配它复制每个vector中bar.
在C++ 11中它改为移动bar::datas,这基本上是免费的.
在这种情况下,这依赖于std容器内的优化vector.在下面的每种情况下,std容器的使用仅仅是因为它们是C++对象,move在升级编译器时"自动" 地在C++ 11 中具有高效的语义.不阻止包含std容器的对象也会继承自动改进的move构造函数.
当NRVO(名为返回值优化)失败时,在C++ 03中它回落到副本上,在C++ 11上它又回落到移动状态.NRVO的失败很简单:
std::vector<int> foo(int count){
std::vector<int> v; // oops
if (count<=0) return std::vector<int>();
v.reserve(count);
for(int i=0;i<count;++i)
v.push_back(i);
return v;
}
Run Code Online (Sandbox Code Playgroud)
甚至:
std::vector<int> foo(bool which) {
std::vector<int> a, b;
// do work, filling a and b, using the other for calculations
if (which)
return a;
else
return b;
}
Run Code Online (Sandbox Code Playgroud)
我们有三个值 - 返回值,以及函数中的两个不同值.Elision允许函数中的值与返回值"合并",但不能彼此合并.它们都不能与返回值合并而不相互合并.
基本问题是NRVO缺陷是脆弱的,并且具有不在return站点附近的变化的代码可以突然在该点处具有大量性能降低而没有诊断发射.在大多数NRVO失败的情况下,C++ 11最终得到一个move,而C++ 03最终得到一个副本.
Elision在这里也是不可能的:
std::set<int> func(std::set<int> in){
return in;
}
Run Code Online (Sandbox Code Playgroud)
在C++ 11中这很便宜:在C++ 03中没有办法避免副本.函数的参数不能用返回值来省略,因为参数和返回值的生命周期和位置由调用代码管理.
但是,C++ 11可以从一个移动到另一个.(在较少玩具的例子中,可能会做一些事情set).
push_back 要么 insert最后,不会发生对容器的省略:但C++ 11会重载rvalue移动插入操作符,从而节省了副本.
struct whatever {
std::string data;
int count;
whatever( std::string d, int c ):data(d), count(c) {}
};
std::vector<whatever> v;
v.push_back( whatever("some long string goes here", 3) );
Run Code Online (Sandbox Code Playgroud)
在C++ 03 whatever中创建临时,然后将其复制到向量中v.std::string分配2个缓冲区,每个缓冲区具有相同的数据,并丢弃一个缓冲区.
在C++ 11 whatever中创建了一个临时的.然后whatever&& push_back超载move暂时进入向量v.std::string分配一个缓冲区,并移动到向量中.空的std::string被丢弃.
从@ Jarod42的答案中偷来了.
分配时不能进行Elision,但移动可以.
std::set<int> some_function();
std::set<int> some_value;
// code
some_value = some_function();
Run Code Online (Sandbox Code Playgroud)
这里some_function返回一个候选者来自,但因为它不是用来直接构造一个对象,所以不能省略它.在C++ 03中,上面的结果是临时被复制到的内容some_value.在C++ 11中,它被移入some_value,基本上是免费的.
为了达到上述目的,您需要一个能够为您合成移动构造函数和赋值的编译器.
MSVC 2013在std容器中实现移动构造函数,但不会在类型上合成移动构造函数.
所以包含std::vectors和类似的类型在MSVC2013中没有得到这样的改进,但是它们将开始在MSVC2015中获得它们.
clang和gcc早就实现了隐式移动构造函数.如果你通过,英特尔的2013编译器将支持隐式生成移动构造函数-Qoption,cpp,--gen_move_operations(默认情况下它们并不是为了与MSVC2013交叉兼容).
Jar*_*d42 46
如果你有类似的东西:
std::vector<int> foo(); // function declaration.
std::vector<int> v;
// some code
v = foo();
Run Code Online (Sandbox Code Playgroud)
你在C++ 03中得到了一个副本,而你在C++ 11中得到了一个移动赋值.所以你可以在这种情况下进行免费优化.