37

何百万行ものファイルがあり、各行にはスペースで区切られた 3 つのフロートがあります。ファイルの読み込みに時間がかかるので、メモリマップファイルを使って読み込もうとしたところ、問題は IO の速度ではなく、解析の速度にあることがわかりました。

私の現在の解析は、ストリーム(ファイルと呼ばれる)を取得し、次のことを行うことです

float x,y,z;
file >> x >> y >> z;

Stack Overflow の誰かが Boost.Spirit の使用を推奨していましたが、その使用方法を説明する簡単なチュートリアルが見つかりませんでした。

次のような行を解析するためのシンプルで効率的な方法を見つけようとしています。

"134.32 3545.87 3425"

助けていただければ幸いです。strtok を使用して分割したかったのですが、文字列を浮動小数点数に変換する方法がわかりません。また、それが最善の方法であるかどうかもわかりません。

解決策がBoostになるかどうかは気にしません。これまでで最も効率的なソリューションでなくてもかまいませんが、速度を 2 倍にすることは可能だと確信しています。

前もって感謝します。

4

8 に答える 8

45

アップデート

Spirit X3 がテスト用に利用可能になったので、ベンチマークを更新しました。一方、 Noniusを使用して、統計的に健全なベンチマークを取得しました。

以下のすべてのチャートは、オンラインでインタラクティブに利用できます

ベンチマーク CMake プロジェクト + 使用したテストデータは github にあります: https://github.com/sehe/bench_float_parsing

ここに画像の説明を入力

概要:

Spirit パーサーは最速です。C++14 を使用できる場合は、実験的なバージョンの Spirit X3 を検討してください。

ここに画像の説明を入力

上記はメモリマップファイルを使用した対策です。IOstream を使用すると、全面的に遅くなりますが、

ここに画像の説明を入力

scanfただし、 C/POSIXFILE*関数呼び出しを使用するほど遅くはありません。

ここに画像の説明を入力


以下は、古い回答の一部です


Spirit バージョンを実装し、他の提案された回答と比較してベンチマークを実行しました。

これが私の結果です。すべてのテストは、同じ入力本体 (515Mb のinput.txt) で実行されます。正確な仕様については、以下を参照してください。


(秒単位の壁時計時間、2 回以上の実行の平均)

驚いたことに、Boost Spirit は最も速く、最もエレガントであることがわかりました。

  • エラーを処理/報告する
  • +/-Inf と NaN および可変空白をサポート
  • 入力の終わりを検出するのにまったく問題はありません(他のmmapの回答とは対照的に)
  • いい感じ:

    bool ok = phrase_parse(f,l,               // source iterators
         (double_ > double_ > double_) % eol, // grammar
         blank,                               // skipper
         data);                               // output attribute
    

boost::spirit::istreambuf_iterator言いようのないほど遅いことに注意してください(15秒以上)。これが役立つことを願っています!

ベンチマークの詳細

すべての解析vectorは of に行われstruct float3 { float x,y,z; }ます。

を使用して入力ファイルを生成します

od -f -A none --width=12 /dev/urandom | head -n 11000000

これにより、次のようなデータを含む 515Mb ファイルが生成されます。

     -2627.0056   -1.967235e-12  -2.2784738e+33
  -1.0664798e-27  -4.6421956e-23   -6.917859e+20
  -1.1080849e+36   2.8909405e-33   1.7888695e-12
  -7.1663235e+33  -1.0840628e+36   1.5343362e-12
  -3.1773715e-17  -6.3655537e-22   -8.797282e+31
    9.781095e+19   1.7378472e-37        63825084
  -1.2139188e+09  -5.2464635e-05  -2.1235992e-38
   3.0109424e+08   5.3939846e+30  -6.6146894e-20

以下を使用してプログラムをコンパイルします。

g++ -std=c++0x -g -O3 -isystem -march=native test.cpp -o test -lboost_filesystem -lboost_iostreams

を使用して壁時計の時間を測定する

time ./test < input.txt 

環境:

  • Linux デスクトップ 4.2.0-42-generic #49-Ubuntu SMP x86_64
  • Intel(R) Core(TM) i7-3770K CPU @ 3.50GHz
  • 32GiB RAM

完全なコード

古いベンチマークの完全なコードはこの投稿の編集履歴にあります。最新バージョンはgithub にあります。

于 2013-07-05T00:49:59.243 に答える
13

開始する前に、これがアプリケーションの遅い部分であることを確認し、改善を測定できるようにテスト ハーネスを取得します。

boost::spirit私の意見では、これはやり過ぎです。試すfscanf

FILE* f = fopen("yourfile");
if (NULL == f) {
   printf("Failed to open 'yourfile'");
   return;
}
float x,y,z;
int nItemsRead = fscanf(f,"%f %f %f\n", &x, &y, &z);
if (3 != nItemsRead) {
   printf("Oh dear, items aren't in the right format.\n");
   return;
}
于 2013-07-04T08:12:38.917 に答える
2

この関連記事Using ifstream to read floatsまたはHow do I tokenize a string in C++特に C++ String Toolkit Library に関連する投稿をチェックします。私は C strtok、C++ ストリーム、Boost トークナイザーを使用しましたが、使いやすく使いやすいのは C++ String Toolkit Library です。

于 2016-07-07T19:49:35.247 に答える
0

C を使用するのが最速のソリューションになります。を使用してトークンに分割しstrtok、 で float に変換しますstrtof。または、正確な形式がわかっている場合は、fscanf.

于 2013-07-04T08:13:26.477 に答える