1

たとえば、空白で区切られた ASCII テキストとして格納された約 1.5 Gb 相当の浮動小数点数を含むデータ ファイルがあります1.2334 2.3456 3.4567

このような数値を処理する前に、まず元のファイルをバイナリ形式に変換します。floatこれは、またはを使用するかどうかを選択しdouble、ファイル サイズを ( の場合は約 800 MB にdouble、 の場合は 400 MB にfloat) 削減し、データの処理中に適切なサイズのチャンクで読み取ることができるため、便利です。

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 で問題ありません) に依存したいと思います。

ありがとうございました。

編集:

@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;
      }
    }
  }

しかし、データを書き込んでいないようです!私はそれに取り組んでいます...

4

2 に答える 2

3

フィールドがタブで区切られていることと、各行の末尾にある数値以外のコメントと、データが散在するヘッダー行も処理する必要があることを除いて、まったく同じことを行いました。'\t'

これが私のユーティリティのドキュメントです。

また、速度の問題もありました。パフォーマンスを約 20 倍向上させるために私が行ったことは次のとおりです。

  • 明示的なファイル読み取りをメモリ マップト ファイルに置き換えます。一度に 2 つのブロックをマップします。ラインを処理した後、2 番目のブロックにいる場合は、2 番目と 3 番目のブロックで再マップします。このようにして、ブロック境界にまたがる行はメモリ内で連続しています。(行がブロックよりも大きいと仮定すると、おそらくこれを保証するためにブロックサイズを増やすことができます。)
  • _mm_cmpeq_epi8行末やその他の区切り文字を検索するなどの SIMD 命令を使用します。私の場合、文字を含む行はすべて、'='別の処理が必要なメタデータ行でした。
  • 最低限の数値解析関数を使用します (私は HH:MM:SS 形式で時刻を解析するためにカスタム関数を使用しましたstrtodが、strtol通常の数値を取得するのに最適です)。istreamこれらは、フォーマットされた抽出関数よりもはるかに高速です。
  • 標準 C++ API の代わりに OS ファイル書き込み API を使用します。

300,000 行/秒の範囲のスループットを夢見ている場合は、同様のアプローチを検討する必要があります。

C++ 標準ストリームを使用しないと、実行可能ファイルも縮小されます。グラフィカル インターフェイスを含めて 205KB あり、Windows に同梱されている DLL のみに依存しています (MSVCRTxx.dll は必要ありません)。もう一度考えてみると、私はまだステータス レポートに C++ ストリームを使用しています。

于 2013-02-07T21:43:40.210 に答える
0

ofを使用して、書き込みを固定バッファーに集約します。ロジックは次のように機能するはずです。std::vectorRealType

  1. std::vector<RealType>65,536 個のデフォルトで構築されたエントリを割り当てます。

  2. ベクトルに最大 65,536 エントリを読み取り、既存のエントリを置き換えます。

  3. 読み込めた数だけ記入してください。

  4. 正確に 65,536 エントリを読み込んだ場合は、ステップ 2 に進みます。

  5. やめて、あなたは終わった。

これにより、2 つの異なるファイルへの読み取りと書き込みを交互に行うことができなくなり、シーク アクティビティが大幅に最小限に抑えられます。writeまた、呼び出しをはるかに少なくして、コピーとバッファリングのロジックを減らすこともできます。

于 2013-02-07T20:57:09.820 に答える