簡體   English   中英

std::accumulate 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::moveaccumulate

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM