簡體   English   中英

在C ++中改進/優化文件寫入速度

[英]Improving/optimizing file write speed in C++

我一直遇到寫文件的一些問題 - 即無法寫得足夠快。

為了解釋,我的目標是捕獲通過千兆以太網傳輸的數據流,並將其保存到文件中。

原始數據以10MS / s的速率進入,然后將其保存到緩沖區並隨后寫入文件。

以下是相關的代碼部分:

    std::string path = "Stream/raw.dat";
    ofstream outFile(path, ios::out | ios::app| ios::binary);

    if(outFile.is_open())
        cout << "Yes" << endl;

    while(1)
    {
         rxSamples = rxStream->recv(&rxBuffer[0], rxBuffer.size(), metaData);
         switch(metaData.error_code)
         {

             //Irrelevant error checking...

             //Write data to a file
                std::copy(begin(rxBuffer), end(rxBuffer), std::ostream_iterator<complex<float>>(outFile));
         }
    } 

我遇到的問題是將樣本寫入文件需要很長時間。 大約一秒鍾后,發送樣本的設備報告其緩沖區已溢出。 在對代碼進行一些快速分析之后,幾乎所有的執行時間都花費在std::copy(...) (確切地說是99.96%)。 如果我刪除這一行,我可以運行該程序幾個小時而不會遇到任何溢出。

也就是說,我對如何提高寫入速度感到困惑。 我查看了這個網站上的幾個帖子,看起來最常見的建議(關於速度)是通過使用std::copy來實現我已經完成的文件寫入。

如果它有用,我在Ubuntu x86_64上運行這個程序。 任何建議,將不勝感激。

所以這里的主要問題是你嘗試在收到的同一個線程中寫入,這意味着你的recv()只能在復制完成后再次調用。 一些觀察:

  • 將寫入移動到另一個線程。 這是關於USRP的,所以GNU Radio可能真的是你選擇的工具 - 它本身就是多線程的。
  • 您的輸出迭代器可能不是最高性能的解決方案。 簡單地“寫()”到文件描述符可能會更好,但這是由你決定的性能測量
  • 如果您的硬盤驅動器/文件系統/ OS / CPU達不到USRP的速率,即使將接收與線程寫入分離,那么您無能為力 - 獲得更快的系統。
  • 嘗試寫入RAM磁盤

事實上,我不知道你是如何想出std::copy方法的。 UHD附帶rx_samples_to_file示例通過簡單的寫入完成此操作,您絕對應該支持復制; 在優秀的操作系統上,文件I / O通常可以減少一個副本,並且迭代所有元素可能非常慢。

我們來做一些數學運算。

您的樣本(顯然)類型為std::complex<std::float> 給定(典型的)32位浮點數,這意味着每個樣本是64位。 在10 MS / s時,這意味着原始數據大約是每秒80兆字節 - 這可以達到您可以期望寫入桌面(7200 RPM)硬盤的范圍,但是接近極限(通常大約為100)每秒-100兆字節左右)。

不幸的是,盡管有std::ios::binary ,你實際上是以文本格式編寫數據(因為std::ostream_iterator基本上是stream << data; )。

這不僅會失去一些精確度,而且會增加數據的大小,至少通常是這樣。 確切的增加量取決於數據 - 小的整數值實際上可以減少數據量,但對於任意輸入,大小增加接近2:1是相當普遍的。 隨着2:1的增加,您的傳出數據現在大約為160兆字節/秒 - 這比大多數硬盤驅動器可以處理的速度快。

改進的明顯起點是以二進制格式編寫數據:

uint32_t nItems = std::end(rxBuffer)-std::begin(rxBuffer);
outFile.write((char *)&nItems, sizeof(nItems));
outFile.write((char *)&rxBuffer[0], sizeof(rxBuffer));

目前我使用sizeof(rxBuffer)假設它是一個真正的數組。 如果它實際上是指針或向量,則必須計算正確的大小(您想要的是要寫入的總字節數)。

我還注意到,正如它現在所說,你的代碼有一個更嚴重的問題:因為它在寫入數據時沒有在元素之間指定分隔符,所以數據將被寫入而沒有任何東西將一個項目與下一個。 這意味着如果你寫了兩個(例如) 10.2 ,你讀回的內容不會是10.2 ,而是單個值10.2 將分隔符添加到文本輸出將為已經失敗的進程增加更多開銷(大約多15%的數據),因為它會生成太多數據。

以二進制格式寫入意味着每個浮點數將精確消耗4個字節,因此分隔符不必正確讀取數據。

之后的下一步是下降到較低級別的文件I / O例程。 根據具體情況,這可能會或可能不會產生太大影響。 在Windows上,您可以在使用CreateFile打開文件時指定FILE_FLAG_NO_BUFFERING 這意味着對該文件的讀取和寫入將基本繞過緩存並直接轉到磁盤。

在你的情況下,這可能是一個勝利 - 在10 MS / s時,你可能會在重讀相同的數據之前花費一段時間來使用緩存空間。 在這種情況下,讓數據進入緩存幾乎不會帶來任何好處,但是會花費一些數據將數據復制到緩存,然后稍后將其復制到磁盤。 更糟糕的是,它可能會使用所有這些數據污染緩存,因此它不再存儲更有可能從緩存中受益的其他數據。

暫無
暫無

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

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