2008-11-06 25 views
6

我必須處理非常大的文本文件(2 GB),它是逐行讀/寫它們的必需。要使用ofstream編寫23百萬行非常慢,所以我開始時嘗試加速在存儲器緩衝區(例如256 MB或512 MB)中寫入大塊行的過程,然後將緩衝區寫入文件。這沒有奏效,表現差不多。閱讀文件時遇到同樣的問題。我知道I/O操作由STL I/O系統緩衝,這也取決於磁盤調度程序策略(由操作系統管理,在我的Linux中)。何時爲I/O(C++)構建自己的緩衝區系統?

有關如何提高性能的任何想法?我一直在考慮在程序正在處理數據時使用後臺子進程(或線程)讀/寫數據塊,但我不知道(主要是在子進程的情況下),如果這將是值得的。

+0

看到這個答案從讀文件直接到串流緩衝區:http://stackoverflow.com/questions/132358/how-to-read-file-content-into-istringstream#138645 – 2008-11-07 17:13:34

+0

你沒有提到你看到的速度。根據物理驅動器的不同,不要指望持續寫入超過40-60 MB/s。使用dd來衡量寫入吞吐量。使用條帶驅動器(例如RAID 0,RAID 1 + 0)來提高吞吐量(在總線關閉之前,最多可驅動約4個驅動器)。 – 2008-11-08 13:28:06

回答

10

一個2GB的文件是相當大的,你需要了解所有可以充當瓶頸的可能領域:

  • 的硬盤驅動器本身
  • 的HDD接口(IDE/SATA/RAID/USB?)
  • 操作系統/文件系統
  • C/C++庫
  • 您的代碼

我會做一些測試開始:

  • 多久你的代碼需要讀/寫一個2GB的文件,
  • 能夠以多快「dd」命令讀取和寫入到磁盤?例如...

    dd if=/dev/zero bs=1024 count=2000000 of=file_2GB

  • 多長時間需要寫/只用大fwrite()將讀/ FREAD()調用

假設你的磁盤能夠讀取/寫入約40Mb/s(這可能是一個現實的數字),您的2GB文件無法運行速度超過50秒。

實際需要多長時間?

羅迪您好,使用fstream的閱讀方法與 1.1 GB文件和大 緩衝器(128255或512 MB)花費大約 43-48秒,它使用的fstream函數getline(逐行)是相同的 。 CP需要將近2分鐘才能複製 文件。

在這種情況下,您的硬件綁定。 cp必須讀取和寫入,並且會在磁盤表面上來回尋找,如同它發生時一樣。所以它會(如你所見)比簡單的'閱讀'情況差兩倍多。

爲了提高速度,我想嘗試的第一件事是更快的硬盤或SSD。

你還沒有說過什麼是磁盤接口? SATA幾乎是最簡單/最快的選擇。另外(明顯的一點,這...)確保磁盤在物理上位於您的代碼運行的同一臺機器上,否則您的網絡綁定...

+1

如果遇到硬件限制,轉向速度稍微快一點的驅動器將無助於轉向條形驅動器。另外,爲什麼使用_cp_來代替 - 使用dd if =/dev/zero of =/path來測試寫吞吐量。試驗塊大小(bs = 4K bs = 32K),看看它是如何影響速度的。 – 2008-11-08 13:23:34

5

也許你應該看看內存映射文件。

檢查他們在這個庫:Boost.Interprocess

+0

MMFs也是我的建議。 +1提及Boost對它的支持。 – OregonGhost 2008-11-06 10:03:08

8

我也建議內存映射文件,但如果你要使用升壓,我認爲boost::iostreams::mapped_file比的boost ::進程間更好的匹配。

+0

我不知道那個。 – 2008-11-06 12:45:03

0

如果您要自行緩衝文件,我建議使用無緩衝的I/O進行一些測試(在您打開的文件上的setvbuf可以關閉庫緩衝)。

基本上,如果你要緩衝自己,你想禁用庫的緩衝,因爲它只會讓你痛苦。我不知道是否有任何方法可以實現STL I/O,所以我建議下降到C級I/O。

3

只是一個想法,但避免使用std :: endl,因爲這將在緩衝區滿之前強制刷新。使用'\ n'代替換行符。

+0

是的,你是對的。好點:) – Bocaballena 2008-11-06 13:22:09

2

不要使用新分配的緩衝區這樣的:

嘗試:性病::矢量<>

unsigned int  buffer_size = 64 * 1024 * 1024; // 64 MB for instance. 
std::vector<char> data_buffer(buffer_size); 
_file->read(&data_buffer[0], buffer_size); 

而且閱讀using underscore in identifier names:文章。注意你的代碼是好的,但是。

+0

我使用新的和char *只是爲了使其儘可能快。 這段代碼是在一個類方法中,以我的個人風格,我使用下劃線來標​​識類成員變量,而方法的局部變量沒有前綴。 – Bocaballena 2008-11-06 13:21:21

1

使用getline()可能效率不高,因爲字符串緩衝區可能需要重新調整大小,因爲數據從流緩衝區中附加到它。你可以讓這個更有效的預上漿的字符串:

您也可以設置輸入輸出流的大小緩衝區要麼非常大或NULL(無緩衝)

// Unbuffered Accesses: 
fstream file; 
file.rdbuf()->pubsetbuf(NULL,0); 
file.open("PLOP"); 

// Larger Buffer 
std::vector<char> buffer(64 * 1024 * 1024); 
fstream   file; 
file.rdbuf()->pubsetbuf(&buffer[0],buffer.size()); 
file.open("PLOP"); 

std::string line; 
line.reserve(64 * 1024 * 1024); 

while(getline(file,line)) 
{ 
    // Do Stuff. 
}