![](/img/trans.png)
[英]Why has std::accumulate not been made constexpr in C++20?
[英]std::accumulate C++20 version
我試圖理解這段代碼,但我不知道為什么這個版本
for (; first != last; ++first)
init = std::move(init) + *first;
比這更快
for (; first != last; ++first)
init += *first;
我確實從 std::accumulate 拿走了它們。 第一個版本的匯編代碼比第二個版本長。 即使第一個版本創建了 init 的右值引用,它總是通過添加 *first 然后將其分配給 init 來創建臨時值,這與在第二種情況下創建臨時值然后將其分配給 init 的過程相同。 那么,為什么使用 std::move 比使用 += 運算符的“附加值”更好?
編輯
我在看c++20版本的accumulate的代碼,他們說c++20之前的accumulate是這個
template<class InputIt, class T>
T accumulate(InputIt first, InputIt last, T init)
{
for (; first != last; ++first) {
init = init + *first;
}
return init;
}
在 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;
}
我只是想知道,使用 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;
}
如果你運行它,你會發現 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));
對於沒有std::move
的accumulate
,
std::string operator+(const std::string&, const std::string&);
將會被使用。 在每次迭代中,它將在堆上為結果字符串分配存儲空間,以便在下一次迭代中將其丟棄。
對於使用std::move
accumulate
,
std::string operator+(std::string&&, const std::string&);
將會被使用。 與前一種情況相比,第一個參數的緩沖區可以重復使用。 如果初始字符串有足夠的容量,則在累積期間不會分配額外的 memory。
without std::move
n_allocs = 199
with std::move
n_allocs = 0
對於像int
這樣的內置類型, move 只是一個副本——沒有什么可以移動的。 對於優化的構建,您很可能會得到完全相同的匯編代碼。 如果您的基准測試顯示任何速度提高/下降,很可能您沒有正確執行(沒有優化、噪音、代碼優化等)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.