2013-02-07 51 views
1

我有大約1.5 Gb的浮點數以ASCII空格分隔的數據文件,例如1.2334 2.3456 3.4567等等。讀取大量的ASCII數字並以二進制格式寫入

在處理這些數字之前,我首先將原始文件轉換爲二進制格式。這很有幫助,因爲我可以選擇是使用float還是double,減小文件大小(對於double約爲800 MB,對於float約爲400 MB),並在處理數據後以適當大小讀取塊。

我寫了下面的功能,使ASCII到二進制翻譯:

template<typename RealType=float> 
void ascii_to_binary(const std::string& fsrc, const std::string& fdst){  
RealType value; 
std::fstream src(fsrc.c_str(), std::fstream::in | std::fstream::binary); 
std::fstream dst(fdst.c_str(), std::fstream::out | std::fstream::binary); 

while(src >> value){ 
    dst.write((char*)&value, sizeof(RealType)); 
} 
// RAII closes both files 
} 

我想來加速acii_to_binary,我似乎無法拿出任何東西。我嘗試以8192字節的塊讀取文件,然後嘗試在另一個子例程中處理緩衝區。這看起來非常複雜,因爲緩衝區中的最後幾個字符可能是空白(在這種情況下一切都很好),或者截斷的數字(這非常糟糕) - 處理可能的截斷的邏輯似乎不值得。

你會怎麼做才能加快這個功能?我寧願依賴於標準的C++(C++ 11是OK),而不需要額外的依賴,比如boost。

謝謝。

編輯:

@DavidSchwarts:

我試圖實現您的建議如下:

template<typename RealType=float> 
    void ascii_to_binary(const std::string& fsrc, const std::string& fdst{  
    std::vector<RealType> buffer; 
    typedef typename std::vector<RealType>::iterator VectorIterator; 
    buffer.reserve(65536); 

    std::fstream src(fsrc, std::fstream::in | std::fstream::binary); 
    std::fstream dst(fdst, std::fstream::out | std::fstream::binary); 

    while(true){ 
     size_t k = 0; 
     while(k<65536 && src >> buffer[k]) k++;  
     dst.write((char*)&buffer[0], buffer.size()); 
     if(k<65536){ 
    break; 
     } 
    } 
    } 

但它似乎並沒有被寫入數據!我的工作就可以了...

+1

你的意思是說文件大小減小到800 * M * B不是800 * G * B? –

+3

我想你會發現使用經典的C'FILE *'和'fscanf()'會比使用'fstream'快一點點。 [也不要認爲你應該在你的輸入文件中使用'fstream :: binary' - 它可能對性能沒有任何影響,但是正確性會受到影響。 –

+0

@丹弗 - 是的。謝謝! – Escualo

回答

3

我做完全相同同樣的事情,不同的是我的領域是由標籤'\t'分離,我只好也處理與數據穿插每一行和標題行結束非數字的意見。

Here是我公用程序的文檔。

而且我也有速度問題。以下是我所做的改進性能的20倍左右:

  • 用內存映射文件替換顯式文件讀取。一次映射兩個塊。處理完一條線後,如果您在第二個塊中,則使用第二個和第三個塊重新映射。這樣,跨越塊邊界的線在內存中仍然是連續的。 (假定沒有行大於塊,可以增加塊大小來保證這一點。)
  • 使用SIMD指令(如_mm_cmpeq_epi8)來搜索行結束符或其他分隔符。就我而言,任何包含'='字符的行都是需要不同處理的元數據行。
  • 使用準系統號碼解析功能(我用HH:MM:SS格式解析時間的定製解析函數,strtodstrtol非常適合抓取普通數字)。這些比istream格式化的提取功能快得多。
  • 使用OS文件寫入API而不是標準C++ API。

如果你夢想在300,000線/秒範圍內的吞吐量,那麼你應該考慮類似的方法。

當您不使用C++標準流時,您的可執行文件也會縮小。我有205KB,包括一個圖形界面,只依賴於Windows附帶的DLL(不需要MSVCRTxx.dll)。再看,我仍然在使用C++流進行狀態報告。

+0

謝謝 - 我正在調查內存映射文件... – Escualo

+0

順便說一句,只是有一個理由來處理文件。結果:'在55.244秒(吞吐量:534426行/秒)完成處理2508MB「。這是3.87 GB的磁盤I/O,如果其中包括源和目標。 –

0

總結了寫入成固定的緩衝區,使用RealTypestd::vector。你的邏輯應該像這樣工作:

  1. 分配一個std::vector<RealType> 65536缺省構造的條目。

  2. 最多可讀取65,536個條目到載體中,替換現有的條目。

  3. 寫出儘可能多的條目,你能夠閱讀。

  4. 如果你恰好65536項的讀,轉到步驟2

  5. 停止,你做。

這會阻止您將讀取和寫入交替到兩個不同的文件,從而極大地減少查找活動。它還可以讓您撥打更少的電話write,減少複製和緩衝邏輯。

+1

這裏使用'std :: vector'沒有什麼優勢,因爲它是一個固定大小的數組。 –

相關問題