我最近读到了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;
}我得到了以下结果:
tr = true (x86) /O2.
4376定时器1(std::move) 4191定时器2(不动)
tr = false (x86) /O2.
7311定时器1(std::move) 7301定时器2(不动)
两个定时器之间的结果非常接近,并且没有太大的差别。我已经假定这是因为返回值优化(RVO),这意味着编译器已经移动了我的返回值,对吗?
因此,我运行新的测试,没有任何优化,以确保我是正确的。结果:
tr = true (x86) /Od.
40860计时器1(std::move) 40863定时器2(不动)
tr = false (x86) /Od.
83567计时器1(std::move) 82075定时器2(不动)
现在,尽管/O2和/Od之间的差异确实很大,但no或std::move (甚至tr是true或false)之间的差异是最小的。
这是否意味着即使禁用了优化,编译器也可以应用RVO,还是std::move没有我想象的那么快?
发布于 2015-09-29 13:26:47
有一个基本的信息是你缺少的:当一个return语句(和其他一些不那么常见的上下文)指定一个函数-局部变量(在您的例子中是o )时,从参数中构造返回值的重载解析首先被执行,就像参数是rvalue一样(尽管它不是)。只有当此操作失败时,才会使用lvalue再次执行重载解析。C++14 12.8/32涵盖了这一点;C++11中也有类似的措辞。
如果符合复制/移动操作的省略条件,但不满足异常声明的条件,并且要复制的对象由lvalue、指定,或者当
return语句中的表达式为(可能是括号内的),该命名为在主体中声明的具有自动存储持续时间的对象或内部最封闭函数的 parameter-declaration-clause 或E 219E 120λ-表达式时,则为12.8/32,首先执行重载解析以选择副本的构造函数,就好像该对象是由rvalue指定的一样。如果第一个重载解析失败或没有执行,或者所选构造函数的第一个参数的类型不是对对象类型(可能是cv限定的)的rvalue引用,则将再次执行重载解析,将对象视为lvalue。注意:无论复制是否会发生,这种两阶段过载解决方案都必须执行.如果不执行省略,它将确定要调用的构造函数,即使调用被省略,所选的构造函数也必须是可访问的。-end笔记..。
(强调地雷)
因此,实际上,在返回函数范围自动变量时,每个return语句中都有一个不可撤销的隐式return。
如果说在返回语句中使用std::move是悲观的。由于“隐式尝试rvalue优先”规则,它可以防止NRVO,也不会给您带来任何东西。
发布于 2015-09-29 13:22:47
即使您指定了/Od,编译器也会执行RVO。C++标准允许这样做(正如Kerrek所指出的那样,第12.8/31,32节)
如果您真的想看到差异,可以将变量声明为volatile。这将不允许编译器在其上执行RVO。(第12.8/31号决议项目1)
https://stackoverflow.com/questions/32844948
复制相似问题