2017-01-26 116 views
5

我正在處理我的第一個C++項目,它是一個CSV解析器(full source code here)。它正在工作,現在我想要進行基本的重構/提高性能。向量移動構造函數比複製構造函數要慢

目前解析器的工作方式是通過返回每一行作爲std::vector<std::string>,我認爲,而不是分配一個新的向量和一個新的字符串,每次我只有一個內部向量和內部字符串保留內存我會一再清除。

這工作,我開始尋找其他地方,我可能會做內存分配,我看到這個函數拷貝內部矢量,然後將其清除:

auto add_row() -> std::vector<std::string> { 
    auto row(m_bufvec); 
    m_bufvec.clear(); 
    return row; 
} 

我想,如果我而不是改變了這一行

auto row(m_bufvec); 

auto row(std::move(m_bufvec)); 

它會導致以s因爲根據http://en.cppreference.com/w/cpp/container/vector/vector,它需要一定的時間而不是線性的。令我驚訝的是,它使解析器明顯變慢(根據我的真實粗糙基準運行time ./main.o而不是this file)。

我完全不熟悉優化,基準測試和調整C++代碼所帶來的一切。也許這種優化是無用的,即使它的工作,但不管,我很好奇爲什麼std::move導致放緩。我錯過了什麼嗎?

+2

嘗試'自動行{move(m_bufvec)}; m_bufvec.reserve(row.size());返回行;'。基本上,與複製相比,避免每元素複製,所以應該更快。 –

+0

@ Cheersandhth.-Alf很好地證實了這是造成它清空的能力。速度恢復正常,最好是2-3%。 – m0meni

回答

8

複製bufvec時,其容量不變,但移動時容量將被清除。因此,稍後當您填寫bufvec時,會進行對數分配以再次擴大其容量,這種分配可能很容易成爲您的性能瓶頸。

移動版本使功能更快。但它使其他代碼變慢。微型優化不能可靠地使程序更快。


編輯由OP:

通過在m_bufvec.reserve(row.size())移動後的評論Cheers and hth. - Alf提出的解決方案解決了這個問題,並確認上述推理是正確的。此外,它更有效率,(儘管只是略微),因爲

你避免複製項目[在bufvec]。如果這些項目是簡單的整數值,那並不重要。如果物品是例如字符串,動態分配,那麼它確實很重要。

+0

啊,我不知道'移動'重置容量!我很快可能會接受這一點,但我想保持這個問題的公開,以防萬一有其他答案可以學習。 – m0meni

1

事實上,第一個版本預計會更快。其原因是:

auto row(m_bufvec); 

調用拷貝constuctor,剛剛在一次分配必要的內存rowbufvec也保留其分配的內存。因此,每個元素的分配被最小化,並且這很重要,因爲它們涉及一定數量的重定位

在第二個版本,auto row(std::move(m_bufvec));bufvec的記憶變成由row擁有的,這種操作比拷貝構造函數更快。但是由於bufvec已經失去了分配的內存,當你以後逐元素地填充它時,它會做很多重新分配和(昂貴的)重定位。重新分配的數量通常與矢量的最終大小成對數。

編輯

上面解釋了主要問題中的「意外」結果。最後,事實證明,「理想」這個操作間移動,然後馬上預約:

auto row(std::move(m_bufvec); 
m_bufvec.reserve(row.size()); 
return row; 

這實現了三個目標:

  • 沒有元素乘元素分配

  • 沒有無用的初始化bufvec

  • 沒有無用的從m_bufvec元素複製到row

+0

「從中移動並不是一個好主意」,那麼您尚未討論如何避免您已識別的問題。正如我所看到的那樣,一個'reserve'調用應該解決'move'代碼的問題。 –

+1

@ Cheersandhth.-Alf好點。我想到了這一點,但實際上它更復雜,因爲理想情況下我們希望保留避免無用的初始化,所以它不知何故超越了現在的代碼和主要問題。 –

+0

'reserve'確實如此(如果我理解正確的話):它保留容量而不用初始化項目。所以,它實際上更簡單。 :) –