Som*_*ken 4 c++ optimization return-value-optimization c++11 visual-studio-2015
我最近读过如何std::move通过移动值而不是复制它来加速代码.所以我制作了一个测试程序来比较速度std::vector.
代码:
#include <iostream>
#include <vector>
#include <stdint.h>
#ifdef WIN32
#include <Windows.h>
#else
#include <sys/time.h>
#include <ctime>
#endif
#undef max
// Returns the amount of milliseconds elapsed since the UNIX epoch. Works on both
// windows and linux.
uint64_t GetTimeMs64()
{
#ifdef _WIN32
// Windows
FILETIME ft;
LARGE_INTEGER li;
// Get the amount of 100 nano seconds intervals elapsed since January 1, 1601 (UTC) and copy it
// to a LARGE_INTEGER structure.
GetSystemTimeAsFileTime(&ft);
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
uint64_t ret = li.QuadPart;
ret -= 116444736000000000LL; // Convert from file time to UNIX epoch time.
ret /= 10000; // From 100 nano seconds (10^-7) to 1 millisecond (10^-3) intervals
return ret;
#else
// Linux
struct timeval tv;
gettimeofday(&tv, NULL);
uint64 ret = tv.tv_usec;
// Convert from micro seconds (10^-6) to milliseconds (10^-3)
ret /= 1000;
// Adds the seconds (10^0) after converting them to milliseconds (10^-3)
ret += (tv.tv_sec * 1000);
return ret;
#endif
}
static std::vector<std::string> GetVec1()
{
std::vector<std::string> o(100000, "abcd");
bool tr = true;
if (tr)
return std::move(o);
return std::move(std::vector<std::string>(100000, "abcd"));
}
static std::vector<std::string> GetVec2()
{
std::vector<std::string> o(100000, "abcd");
bool tr = true;
if (tr)
return o;
return std::vector<std::string>(100000, "abcd");
}
int main()
{
uint64_t timer;
std::vector<std::string> vec;
timer = GetTimeMs64();
for (int i = 0; i < 1000; ++i)
vec = GetVec1();
std::cout << GetTimeMs64() - timer << " timer 1(std::move)" << std::endl;
timer = GetTimeMs64();
for (int i = 0; i < 1000; ++i)
vec = GetVec2();
std::cout << GetTimeMs64() - timer << " timer 2(no move)" << std::endl;
std::cin.get();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我得到了以下结果:
发布(x86)/ O2. tr = true
4376计时器1(std :: move)
4191计时器2(不动)
发布(x86)/ O2. tr = false
7311计时器1(std :: move)
7301计时器2(不动)
两个定时器之间的结果非常接近,并没有太大差别.我已经假设这是因为返回值优化(RVO)这意味着我在没有我知道的情况下已经按编译器移动了我的返回值,对吧?
所以我在没有任何优化的情况下运行新测试以确保我是对的.结果:
发布(x86)/ Od. tr = true
40860计时器1(std :: move)
40863计时器2(不动)
发布(x86)/ Od. tr = false
83567计时器1(std :: move)
82075计时器2(不动)
现在即使/ O2和/ Od之间的差异非常显着,但是无移动或std::move(甚至在tr被true或之间false)之间的差异是最小的.
这是否意味着即使禁用了优化,编译器也可以应用RVO或std::move不像我想象的那样快?
您缺少一个基本的信息:标准明确强制执行当return语句(以及一些其他不太常见的上下文)指定函数局部变量(例如o在您的情况下)时,重载决策以构造返回值首先执行参数,就好像参数是一个rvalue(即使它不是).只有在失败的情况下才能使用左值再次完成重载决策.这由C++ 14 12.8/32涵盖; C++ 11中存在类似的措辞.
12.8/32当满足复制/移动操作的省略标准时,但不满足异常声明,并且要复制的对象由左值指定,或者当
return语句中的表达式为(可能带有括号)时id-expression,用于在最内层封闭函数或lambda-expression的body或parameter-declaration-clause中声明的具有自动存储持续时间的对象的命名 ,首先执行重载决策以选择复制的构造函数,就好像该对象是由一个右值.如果第一个重载决策失败或未执行,或者所选构造函数的第一个参数的类型不是对象类型的rvalue引用(可能是cv-qualified),则再次执行重载决策,将对象视为左值.[ 注意:无论是否发生复制省略,都必须执行此两阶段重载决策.如果未执行elision,它将确定要调用的构造函数,并且即使调用被省略,也必须可以访问所选的构造函数.- 注意 ] ......
(强调我的)
因此,实际上,在返回函数范围自动变量时,每个语句中都存在一个不可避免的隐式std::move存在return.
使用std::movereturn语句中,如果有的话,一个pessimisation.它会阻止NRVO,并且由于"隐式尝试右值优先"规则而无法获得任何结果.
| 归档时间: |
|
| 查看次数: |
455 次 |
| 最近记录: |