簡體   English   中英

為什么 is.push_back(x) 比 .push_back(std::move(x)) 快

[英]Why is .push_back(x) faster than .push_back(std::move(x))

我有一個 large.txt 文件,需要加載並存儲在向量中。 該文件大小約為 5MB,有 500 000 行,每行大約 10-20 個字符,以 '\n' 分隔。 我正在使用下面的示例代碼對讀取整個文件所需的時間進行一些基准測試:

#include<iostream>
#include<vector>
#include<fstream>

int main()
{
    std::fstream input("words.txt");
    std::vector<std::string> vector;
    std::string line;

    while(input >> line){
        vector.push_back(line);
    }
}

我很好奇將字符串作為右值引用傳遞是否會更快,但它會慢約 10 毫秒。

#include<iostream>
#include<vector>
#include<fstream>

int main()
{
    std::fstream input("words.txt");
    std::vector<std::string> vector;
    std::string line;

    while(input >> line){
        vector.push_back(std::move(line));
    }
}

第一個代碼示例的平均加載時間約為 58 毫秒,第二個示例的平均加載時間為 68-70 毫秒。 我在想移動總是更快或等於復制,這就是為什么這對我來說似乎不正確。

有誰知道為什么會發生這種情況的原因? 基准測試是使用以下方法完成的:

perf stats -r 100 ./a.out

在 Arch Linux 上,代碼已使用 GCC 10.2、C++17 標准編譯。

此外,如果有人知道更優化的方法來做到這一點,我們將不勝感激。

如果您調用g++ -E您可以查看相關代碼:

復制構造:

  basic_string(const basic_string& __str)
    : _M_dataplus(_M_local_data(),
                  _Alloc_traits::_S_select_on_copy(__str._M_get_allocator()))
  {
      _M_construct(__str._M_data(), __str._M_data() + __str.length());
  }

搬家施工:

# 552 "/usr/local/include/c++/10.2.0/bits/basic_string.h" 3
basic_string(basic_string&& __str) noexcept
  : _M_dataplus(_M_local_data(), std::move(__str._M_get_allocator()))
{
    if (__str._M_is_local())
    {
        traits_type::copy(_M_local_buf, __str._M_local_buf,
                          _S_local_capacity + 1);
    }
    else
    {
        _M_data(__str._M_data());
        _M_capacity(__str._M_allocated_capacity);
    }
    _M_length(__str.length());
    __str._M_data(__str._M_local_data());
    __str._M_set_length(0);
}

值得注意的是,(為了支持短字符串優化)移動構造函數需要查看._M_is_local()來確定是復制還是移動(因此有一個要預測的分支),並且它清除了移動的字符串 /將其長度設置為 0。額外的工作 = 額外的時間。


@Manuel 發表了一個有趣的評論:

當您移動行時,它會變空,因此下一次迭代需要分配空間。 std::string 有一個小的緩沖區作為優化(大多數實現,如果不是全部的話)並且副本只是復制字符,沒有 memory 分配。 那可能就是區別。

這並不像所說的那樣加起來,但這里有細微的差別。 對於足夠長以需要動態分配的輸入空格分隔的單詞,則:

a) move 版本可能在最后一個單詞之后清除了line的動態緩沖區,因此需要重新分配; 如果輸入足夠長,它可能必須重新分配一次或多次以增加容量

b) 復制版本可能有一個足夠大的緩沖區(它的容量將根據需要增加,實際上是看到單詞的高水位線),但是當在push_back內部構建復制時需要動態分配。 該分配的確切大小是預先知道的 - 它不需要調整大小來增加容量。

這確實表明當輸入字長有很多變化時,復制可能會更快。


此外,如果有人知道更優化的方法來做到這一點,我們將不勝感激。

如果您真的關心性能,我建議您對 memory 進行基准測試,映射文件並在其中創建一個string_view vector :這可能會快得多。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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