11

ファイルへの書き込みでいくつかの問題が発生しました。つまり、十分な速度で書き込めません。

説明すると、私の目標は、ギガビット イーサネット経由で受信するデータ ストリームをキャプチャし、単純にファイルに保存することです。

生データは 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));
         }
    } 

私が直面している問題は、サンプルをファイルに書き込むのに時間がかかりすぎることです。約 1 秒後、サンプルを送信するデバイスは、バッファがオーバーフローしたことを報告します。std::copy(...)コードの簡単なプロファイリングの後、実行時間のほぼすべて(正確には時間の 99.96%)が費やされます。この行を削除すると、オーバーフローが発生することなく、プログラムを何時間も実行できます。

とはいえ、書き込み速度を向上させる方法についてはかなり困惑しています。このサイトのいくつかの投稿に目を通しましたが、(速度に関して) 最も一般的な提案は、私が既に行ったように、std::copy.

お役に立てれば、このプログラムを Ubuntu x86_64 で実行しています。任意の提案をいただければ幸いです。

4

2 に答える 2

5

少し計算してみましょう。

あなたのサンプルは(明らかに)タイプstd::complex<std::float>です。(典型的な) 32 ビット float を指定すると、各サンプルが 64 ビットであることを意味します。10 MS/s では、生データが 1 秒あたり約 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)、実数配列であるという前提で使用しています。それが実際にポインターまたはベクトルである場合は、正しいサイズを計算する必要があります (必要なのは、書き込まれる合計バイト数です)。

また、現状では、コードにはさらに深刻な問題があることにも注意してください。データを書き込むときに要素間のセパレーターを指定していないため、データは 1 つの項目を区切るものなしで書き込まれます。次。つまり、(たとえば)1との 2 つの値を書き込んだ場合0.2、読み返すのは10.2ではなく、 の 1 つの値になり10.2ます。テキスト出力にセパレーターを追加すると、生成されるデータが多すぎるために既に失敗しているプロセスに、さらに多くのオーバーヘッドが追加されます (データが約 15% 増加します)。

バイナリ形式で書き込むということは、各フロートが正確に 4 バイトを消費することを意味するため、データを正しく読み取るために区切り記号は必要ありません。

その後の次のステップは、下位レベルのファイル I/O ルーチンに降りることです。状況に応じて、これは大きな違いになる場合とそうでない場合があります。Windows では、FILE_FLAG_NO_BUFFERINGでファイルを開くタイミングを指定できますCreateFile。これは、そのファイルへの読み取りと書き込みが基本的にキャッシュをバイパスし、ディスクに直接移動することを意味します。

あなたの場合、それはおそらく勝利です.10MS / sでは、同じデータを再度読み取る前に、キャッシュスペースをかなり使い果たすでしょう。このような場合、データをキャッシュに入れておいても実質的には何も得られませんが、データをキャッシュにコピーし、後でそれをディスクにコピーするためのデータのコストがかかります。さらに悪いことに、このすべてのデータでキャッシュが汚染される可能性が高いため、キャッシュの恩恵を受ける可能性がはるかに高い他のデータが格納されなくなります。

于 2015-08-05T17:59:11.097 に答える