Sam*_*Sam 5 c++ std accumulate c++11 c++20
我试图理解这段代码,但我不明白为什么这个版本
for (; first != last; ++first)
init = std::move(init) + *first;
Run Code Online (Sandbox Code Playgroud)
比这更快
for (; first != last; ++first)
init += *first;
Run Code Online (Sandbox Code Playgroud)
我确实从 std::accumulate 中取出了它们。第一个版本的汇编代码比第二个版本长。即使第一个版本创建了 init 的右值引用,它也总是通过添加 *first 来创建临时值,然后将其分配给 init,这与第二种情况下创建临时值然后将其分配给 init 的过程相同。那么,为什么使用 std::move 比使用 += 运算符“附加值”更好?
编辑
我在看C++20版本accumulate的代码,他们说在C++20accumulate之前是这个
template<class InputIt, class T>
T accumulate(InputIt first, InputIt last, T init)
{
for (; first != last; ++first) {
init = init + *first;
}
return init;
}
Run Code Online (Sandbox Code Playgroud)
在 C++20 之后它变成了
template<class InputIt, class T>
constexpr // since C++20
T accumulate(InputIt first, InputIt last, T init)
{
for (; first != last; ++first) {
init = std::move(init) + *first; // std::move since C++20
}
return init;
}
Run Code Online (Sandbox Code Playgroud)
我只是想知道,通过使用 std::move 是否有任何真正的改进。
编辑2
好的,这是我的示例代码:
#include <utility>
#include <chrono>
#include <iostream>
using ck = std::chrono::high_resolution_clock;
std::string
test_no_move(std::string str) {
std::string b = "t";
int count = 0;
while (++count < 100000)
str = std::move(str) + b; // Without std::move
return str;
}
std::string
test_with_move(std::string str) {
std::string b = "t";
int count = 0;
while (++count < 100000) // With std::move
str = str + b;
return str;
}
int main()
{
std::string result;
auto start = ck::now();
result = test_no_move("test");
auto finish = ck::now();
std::cout << "Test without std::move " << std::chrono::duration_cast<std::chrono::microseconds>(finish - start).count() << std::endl;
start = ck::now();
result = test_with_move("test");
finish = ck::now();
std::cout << "Test with std::move " << std::chrono::duration_cast<std::chrono::microseconds>(finish - start).count() << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
如果您运行它,您会注意到 std::move 版本确实比另一个版本快,但是如果您使用内置类型尝试它,您会发现 std::move 版本比另一个版本慢。
所以我的问题是,由于这种情况可能与 std::accumulate 相同,为什么他们说带有 std::move 的 C++20 累积版本比没有它的版本快?为什么将 std::move 与字符串之类的东西一起使用我会得到类似的改进,但不使用诸如 int 之类的东西?为什么所有这些,如果在这两种情况下,程序创建一个临时字符串 str + b (或 std::move(str) + b)然后移动到 str?我的意思是,这是相同的操作。为什么第二个更快?
谢谢你的耐心。希望这次我说清楚了。
对于具有非平凡移动语义的类型,它可能更快。考虑std::vector<std::string>足够长的字符串的累积:
std::vector<std::string> strings(100, std::string(100, ' '));
std::string init;
init.reserve(10000);
auto r = accumulate(strings.begin(), strings.end(), std::move(init));
Run Code Online (Sandbox Code Playgroud)
对于accumulate没有std::move,
std::string operator+(const std::string&, const std::string&);
Run Code Online (Sandbox Code Playgroud)
将会被使用。在每次迭代时,它会在堆上为结果字符串分配存储空间,以便在下一次迭代时将其丢弃。
对于accumulate有std::move,
std::string operator+(std::string&&, const std::string&);
Run Code Online (Sandbox Code Playgroud)
将会被使用。与前一种情况相反,第一个参数的缓冲区可以重复使用。如果初始字符串有足够的容量,则在累积过程中不会分配额外的内存。
without std::move
n_allocs = 199
with std::move
n_allocs = 0
Run Code Online (Sandbox Code Playgroud)
对于像 那样的内置类型int,move 只是一个副本——没有什么可以移动的。对于优化的构建,您很可能会得到完全相同的汇编代码。如果您的基准测试显示任何速度改进/降级,很可能您没有正确执行(没有优化、噪音、代码优化等)。