簡體   English   中英

用C ++處理簡單但大型文件的有效方法

[英]Efficient Way to Process Simple but Large Files in C++

我正在開展一個項目,讓我在性能方面略勝一籌。 我的任務是讀取大(50MB左右)的粒子坐標文件並顯示它們。 我想使用C ++,因為我已經在學習它了。

文件中的坐標結構很簡單,只有很多(比如一百萬左右):

1234.5667 5234.1566 //coordinate 1  
8532.6123 5152.6612 //coordinate 2  
....

作為一個菜鳥,我只想逐行閱讀文件並將它們存儲在向量中,這是錯的嗎? 也許我應該首先讀取整個文件(緩沖?),然后解析值?

工作范例:

clock_t c1 = clock();
vector<double> coords;
double coord;
ifstream fin("file.txt");
while(fin >> coord) {
    coords.push_back(coord);
}
cout << "done. " << coords.size()/2 << " coords read.\n";
cout << "took " << (clock() - c1)/(double)CLOCKS_PER_SEC << " seconds." << endl;

並在具有200萬坐標的40MB文件上輸出相應的輸出:

done. 2000000 coords read.
took 1.74 seconds.

這在我看來很快,但我認為我的思想不是一個好的判斷。

如果您知道“平均”文件有多大,則可能需要使用.reserve預先分配矢量。

效率是一個棘手的游戲。 不要在早期玩弄技巧,並設計一個好的基本算法。 如果它不夠快,你就開始查看IO例程,無論你是創建任何“額外”對象(顯式或隱式,特別是如果你傳遞參數)。

在您的示例中,您可能希望在打印摘要輸出之前再次調用clock() - 獲得稍微更精確的計時! :)

如果輸入文件具有固定的行寬(每行字符數),則將數據拖入緩沖區變得更容易。 對於可變行寬文件,確定何時重新填充緩沖區變得棘手。

單緩沖方法

分配char的緩沖區,size =(線寬)*要存儲的行數。 或者您可以向后工作:緩沖區中的最大行=(緩沖區大小)/(行寬)。 請記住,線寬可能包含'\\n', '\\r'或兩者。

使用freadistream::read入整個緩沖區,也稱為塊讀取。 保持指向緩沖區中“下一行”的指針。 讀取后遞增指針,如果超出緩沖區末尾,則讀取另一個塊。 根據需要重復。

雙緩沖方法

分配兩個緩沖區。 讀一個緩沖區。 開始處理第一個緩沖區中的數據 首先處理時讀入第二個緩沖區。 第一次緩沖處理完成后,開始處理第二個緩沖區並先讀入。 根據需要重復。

該方法依賴於多處理:在處理數據時讀取。 這可以實現為兩個線程,或非阻塞讀取(讀取操作不會在返回之前等待完成)。 使用線程,一個線程處理,而另一個線程讀取。 使用信號量來處理單個結束和緩沖就緒。

多緩沖方法

與雙緩沖區類似,但分配了更多緩沖區。 考慮這是一場比賽。 閱讀任務試圖保持領先於處理任務。 處理任務應該等到啟動前讀取N個緩沖區。 目標是分配足夠的緩沖區,以便處理任務不等待讀取器任務。

優化:循環展開

您可能還需要將循環展開視為另一種速度優化。 多次復制循環中的代碼以減少迭代次數。

我通常會等到程序正常工作之后再應用這些技術,除非程序速度非常慢,例如程序需要1小時才能處理2GB文件(它優化到2分鍾)。

優化應從測量開始。 描述的代碼做了三件事:

  1. 從文件中讀取一些字節
  2. 將它們解析為雙打的文本表示
  3. 將它們存儲在矢量中

應測量每個部件的時間份額。

對於#1,它可以將文件內容預先讀入內存(50M應該適合並且不會導致交換),然后運行帶有時間測量的算法。 差異將顯示從磁盤讀取多少。

對於#2文件結構,暫時可以切換到雙精度二進制表示的序列。 差異將顯示解析需要多少。

對於#3 std :: vector :: reserve的測量可以使用(實際上已經建議,這不僅可以是測量工具,還可以是優化)。

就個人而言,我不會指望#3需要很長時間,但測量比猜測更好。

在檢測到大部分耗時的部分后,可能會更加集中精力,從而更加有效......

您可以想到改進它的一種方法是,不是逐行讀取,而是可以在主存儲器中讀取文件的塊,並從該緩沖區中逐行讀取。 緩沖區的大小可以是計算機中群集的大小。

在某些平台上,通過回退到C Standard Libaray I / O( <cstdio> ),您將獲得更好的性能。

但是,如果您知道從文件中讀取了多少個值(或者您可以在文件中包含計數),則可以通過初始化適當大小的向量並直接編寫,以可測量的數量提高性能。到迭代器,像這樣:

std::vector<double> coords(4000000); 
std::vector<double>::iterator it = coords.begin();
ifstream fin("file.txt");
while (fin >> *(it++)) {}

只要確保你可以依賴計數,否則你可能會在向量的末尾迭代。

除了已經給出的答案,您可能會考慮如何顯示坐標。 在進一步處理之前,您可能不需要將它們存儲在矢量中?

暫無
暫無

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

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