2017-08-24 123 views
0

我想讀取大型CSV文件並將其存儲到地圖中。我從閱讀文件開始,看看需要多長時間來處理。這是我的循環:在C++中讀取大型CSV文件(〜4GB)

while(!gFile.eof()){ 
    gFile >> data; 
} 

它需要我〜35分鐘來處理包含3500萬行和6列的csv文件。有什麼辦法可以加快速度嗎?對於SO來說很新,所以如果不能正確地問道歉,

+1

我很肯定有一些圖書館明確爲此目的。也就是說,35分鐘聽起來有點過長。你正在執行多少其他處理?確保你啓用了優化! – tambre

+0

如果你一遍又一遍地重複寫同一個變量,目前還不清楚爲什麼需要這麼長時間。複製文件需要多長時間? – tadman

+0

[將整個ASCII文件讀入C++ std :: string]可能重複(https://stackoverflow.com/questions/2602013/read-whole-ascii-file-into-c-stdstring) – smac89

回答

3

背景
文件是流設備或概念。讀取文件的最有效的用法是保持數據流(流動)。對於每個事務都有一個開銷。數據傳輸越大,開銷的影響就越小。所以,目標是保持數據流動。

內存比文件訪問速度更快
搜索內存比搜索一個文件快許多倍。因此,搜索「單詞」或分隔符將比逐字符地查找文件字符以查找分隔符更快。

方法1:通過線
使用std::getline線比使用operator>>快得多。儘管輸入代碼可能會讀取一塊數據;您只執行一個事務來讀取一個記錄而不是每列一個事務。請記住,保持數據流動和搜索內存的速度更快。

方法2:塊讀
在保持流動的流的精神,讀存儲器塊到一個緩衝器(大緩衝液)。處理來自緩衝區的數據。這比逐行讀取更有效,因爲您可以使用一個事務讀取多行數據,從而減少事務開銷。

一個需要注意的是,你可能有一個記錄交叉緩衝區的邊界,所以你需要想出一個算法來處理這個問題。執行懲罰很小,並且每個事務只發生一次(考慮事務的這部分開銷)。

方法3:多個線程
在保持數據流的精神,你可以創建多個線程。一個線程負責或將數據讀入緩衝區,而另一個線程處理來自緩衝區的數據。保持數據流動,這項技術會有更好的運氣。

方法4:雙緩衝&多個線程
這需要上述方法3和增加了多個緩衝器。讀線程可以填充一個緩衝區,然後開始填充第二個緩衝區。數據處理線程將在處理數據之前等待第一個緩衝區填滿。該技術用於更好地將讀取數據的速度與處理數據的速度相匹配。

方法5:內存映射文件
隨着內存映射文件,操作系統處理文件的需求讀入內存。您必須編寫的代碼越少,但是您對文件讀入內存時的控制權也不盡如人意。這比逐場閱讀還要快。

+0

Id避免多個線程,除非Im計算綁定,這往往是一堆額外的不必要的複雜性。Win32'CreateFile' +'ReadFile'或者nix'open' +'read'實際上並不難,並且可以以全磁盤速度讀取(甚至比內存映射更好,因爲您可以依次告訴操作系統您的讀取),somthing C++甚至沒有「專用」線程。對於簡單的CSV等,甚至可以保持SSD的潛在讀取速度而無需線程(每秒幾百MB)。 –

+1

Downvoters:請添加評論解釋你downvote。我親自實施了這些技術,並在處理大小超過1GB的數據文件時發現了顯着的性能提升。 –

1

讓我們從瓶頸開始吧。

  1. 從磁盤讀取
  2. 解碼數據
  3. 商店在地圖
  4. 顯存速度
  5. 的內存量從盤面看

  • 讀,直到你下降,如果你不能夠快速讀取磁盤上所有的帶寬,你可以加快速度。忽略所有其他步驟,只能閱讀。
  • 開始通過增加緩存到您的河道內
  • 組提示閱讀
  • 使用mmap
  • 4GB是一個平凡的大小,如果你不這樣做已經32 GB升級
  • 太慢買M.2磁盤。
  • 還在慢慢變得更加奇特,更換磁盤驅動,轉儲操作系統。鏡像磁盤,只有你$£€是限制。

解碼數據

  • 如果數據是在哪裏都是相同的長度,那麼所有解碼可以並行地完成線,僅由存儲器帶寬的限制。
  • 如果行長度只有小心,可以並行完成行的查找結束,然後進行並行解碼。
  • 如果行的順序對於最終映射無關緊要,只需將文件拆分爲#hardwarethreads部分,並讓每個進程處理它們的部分,直到下一個線程部分的第一個換行符。
  • 內存帶寬很可能會在CPU反正用完之前達到很遠。在地圖

    商店希望你已經事先想過這個地圖是沒有性病地圖都是線程安全的。

  • 如果您不關心訂單,則可以使用std :: array,並且您可以在全內存帶寬下運行。
  • 可以說你想要使用std :: unordered_map,有一個問題就是它需要在每次寫入後更新大小,所以實際上你的寫操作只限於1個線程。
  • 您可以一次使用1個線程進行更新,而另一個預先計算記錄的散列。具有一個線程寫入的
  • 具有幾乎每個寫入都會成爲高速緩存未命中嚴重限制速度的問題。
  • 所以如果速度不夠快,請滾動您自己的hash_map,而不必在每次寫入時都更新一個大小。
  • 爲了確保線程安全,您還需要保護寫操作,使用一個互斥鎖可以使您比單個寫入器慢或慢。
  • 你可以嘗試鎖定並等待免費...如果你不是專家,你會得到嚴重的頭痛。
  • 如果您爲散列選擇了存儲桶設計,那麼您可以使X倍數量的寫入器線程互斥量,使用散列值來選擇互斥量。額外的互斥量增加了兩個線程不會相互碰撞的可能性。

內存速度

  • 每條線將在存儲器總線從磁盤中傳送的至少4倍,一旦到RAM(至少一次,如果駕駛員是不好),一旦當數據被解碼,一次當地圖發出讀取請求時,以及另一次地圖寫入時。
  • 如果驅動程序寫入緩存,並且因此解碼不會導致LLC未命中,一個好的設置可以節省多一次內存訪問。

的內存

  • 金額,你應該有足夠的內存來存放總文件,數據結構和一些中間數據。
  • 檢查RAM是否比編程時間便宜。