web-dev-qa-db-ja.com

入力ファイルを読み取る、可能な限り最速の方法?

浮動小数点数の形式のデータのテキストファイルが多数あります。私はC++でそれらを読むための最速の方法を探しています。最速の場合は、ファイルをバイナリに変更できます。

ヒントを教えていただければ幸いです。または、完全な説明が記載されたWebサイトを紹介していただければ幸いです。速く動作するライブラリがあるかどうかはわかりません。仕事をするオープンソースソフトウェアがあったとしても、それは役に立ちます。

17
Kiarash

バイナリファイルを持つことが最速のオプションです。 1回の操作で生のistream::readを使用して配列で直接読み取ることができるだけでなく(非常に高速です)、OSがサポートしている場合は、ファイルをメモリにマップすることもできます。 POSIXシステムではopen/mmap、WindowsではCreateFile/CreateFileMapping/MapViewOfFile、さらにはBoostクロスプラットフォームを使用できます。解決策(それを指摘してくれた@Cory Nelsonに感謝します)。

ファイルにいくつかのfloatsの生の表現が含まれていると仮定した、簡単で汚い例:

「通常」の読み取り:

#include <fstream>
#include <vector>

// ...

// Open the stream
std::ifstream is("input.dat");
// Determine the file length
is.seekg(0, std::ios_base::end);
std::size_t size=is.tellg();
is.seekg(0, std::ios_base::beg);
// Create a vector to store the data
std::vector<float> v(size/sizeof(float));
// Load the data
is.read((char*) &v[0], size);
// Close the file
is.close();

共有メモリの使用:

#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>

using boost::interprocess;

// ....

// Create the file mapping
file_mapping fm("input.dat", read_only);
// Map the file in memory
mapped_region region(fm, read_only);
// Get the address where the file has been mapped
float * addr = (float *)region.get_address();
std::size_t elements  = region.get_size()/sizeof(float);
27
Matteo Italia

ボトルネックはI/Oにあります。最小限のI/O呼び出しで、プログラムがメモリにできるだけ多くのデータを読み込むようにします。たとえば、1つのfreadで256の数値を読み取る方が、1つの数値の256 freadよりも高速です。

可能であれば、ターゲットプラットフォームの内部浮動小数点表現、または少なくともプログラムの表現に一致するようにデータファイルをフォーマットします。これにより、テキスト表現を内部表現に変換するオーバーヘッドが削減されます。

可能であれば、OSをバイパスし、DMAコントローラーを使用してファイルデータを読み込みます。DMAチップは、データをメモリに読み込む負担を軽減します。プロセッサの。

データファイルを圧縮します。データファイルは、ディスク上の1つの連続したセクターのセットに含まれている必要があります。これにより、物理的なプラッターのさまざまな領域を探すために費やす時間が短縮されます。

ディスクリソースとプロセッサに対する排他的制御を要求するプログラムがありますか。他のすべての重要でないタスクをブロックします。プログラムの実行の優先度を上げます。

複数のバッファを使用して、ディスクドライブの回転を維持します。時間の大部分は、ハードドライブが加速および減速するのを待つために費やされます。他の何かがデータをバッファに保存している間に、プログラムがデータを処理している可能性があります。これにより、...

マルチスレッド。データを読み込むスレッドを1つ作成し、バッファーが空でないときに処理タスクに警告します。

これらはあなたをしばらく忙しくさせるはずです。他のすべての最適化では、パフォーマンスの向上はごくわずかです。 (ハードドライブコントローラーに直接アクセスして、バッファーの1つに転送するなど。)

8
Thomas Matthews

コンパイルモードへのもう1つの注意。 100万行のファイルを解析してみました。デバッグモードは、データを解析してコンテナに追加するのに50秒かかりました。リリースモードは少なくとも10倍速く、約4秒消費しました。以下のコードは、istringstreamを使用してデータを2Dポイント(、)として解析する前に、ファイル全体を読み取ることです。

vector <float> in_data;
string raw_data;

ifstream ifs;
ifs.open(_file_in.c_str(), ios::binary);
ifs.seekg(0, ios::end);
long length = ifs.tellg();
ifs.seekg(0, ios::beg);
char * buffer;
buffer = new char[length];
ifs.read(buffer, length);
raw_data = buffer;
ifs.close();
delete[]buffer;
cout << "Size: " << raw_data.length()/1024/1024.0 << "Mb" << endl;
istringstream _sstr(raw_data);
string _line;

while (getline(_sstr, _line)){
    istringstream _ss(_line);
    vector <float> record;
    //maybe using boost/Tokenizer is a good idea ...
    while (_ss)
    {
        string s;
        if (!getline(_ss, s, ',')) break;
        record.Push_back(atof(s.c_str()));
    }
    in_data.Push_back(record[0]);
}
2
Brian Ng