c – ファイルの読み込み中、ファイル全体の読み込み中にgetlineを実行し、その後改行文字に基づいて分割する

今ハードディスク上のファイルの各行を処理したいです。ファイル全体をロードしてから(boostを使用して)改行文字に基づいて分割するのが良いのでしょうか、それともgetline()を使用するのが良いですか?私の質問はgetl​​ine()が呼ばれた時に単一行を読む(複数のハードディスクアクセスをもたらす)か、それともファイル全体を読み、一行ずつ与えるかです。
ベストアンサー
getlineはCライブラリの一番奥のどこかにあるシステムコールとしてread()を呼びます。正確に何回呼び出されるか、そしてどのように呼び出されるかはCライブラリの設計に依存します。しかし、ほとんどの場合、ファイル全体に対する行の読み取りに明確な違いはありません。最下層のOSは一度に(少なくとも)1ディスクブロックを読み取るため、おそらく少なくとも1ページのページを読み取ることになります。 “(4KB)、それ以上でなければ。

さらに、文字列を読んだ後でその文字列にほとんど何もしない(たとえば、「grep」のようなものを書いているので、ほとんどの場合、単に文字列を見つけるために読んでいる)。あなたが過ごす時間の大部分です。

しかし、「ファイル全体を一度にロードする」には、いくつかの明確な問題があります。

>ファイル全体を読むまで処理を始めません。
>ファイル全体をメモリに読み込むのに十分なメモリが必要です – ファイルのサイズが数百GBの場合はどうなりますか?あなたのプログラムはそれから失敗しますか?

プロファイリングを使用して、コードの実行速度が遅くなる原因の一部であることを証明していない限り、何かを最適化しないでください。あなたは自分自身のためにより多くの問題を引き起こしているだけです。

編集:だから、私はそれが非常に面白いと思うので、私はこれを測定するためのプログラムを書きました。

比較結果を公平にするために、1つの1297984192バイトの大きなファイルを3つ作成しました(ディレクトリ内のすべてのソースファイルを約12種類の異なるソースファイルでコピーし、次にこのファイルを何度かコピーして「乗算」します)。テストを実行するのに1.5秒以上かかるまで、それはタイミングがランダムな「ネットワークパケットが入ってきた」またはタイムアウトをとる他の何らかの外部の影響をあまり受けないようにするために物事を実行する必要があると思いますプロセスの)。

私はまたプロセスによってシステムとユーザー時間を測定することにしました。

$ ./bigfile
Lines=24812608
Wallclock time for mmap is 1.98 (user:1.83 system: 0.14)
Lines=24812608
Wallclock time for getline is 2.07 (user:1.68 system: 0.389)
Lines=24812608
Wallclock time for readwhole is 2.52 (user:1.79 system: 0.723)
$ ./bigfile
Lines=24812608
Wallclock time for mmap is 1.96 (user:1.83 system: 0.12)
Lines=24812608
Wallclock time for getline is 2.07 (user:1.67 system: 0.392)
Lines=24812608
Wallclock time for readwhole is 2.48 (user:1.76 system: 0.707)

ここにファイルを読むための3つの異なる関数があります(もちろん時間とものを測定するためのいくつかのコードがあります、しかしこの記事のサイズを減らすために、私はそれを全部投稿しないことにします – 違いがあるので、上記の結果はここの関数と同じ順序ではありません。

void func_readwhole(const char *name)
{
    string fullname = string("bigfile_") + name;
    ifstream f(fullname.c_str());

    if (!f) 
    {
        cerr << "could not open file for " << fullname << endl;
        exit(1);
    }

    f.seekg(0, ios::end);
    streampos size = f.tellg();

    f.seekg(0, ios::beg);

    char* buffer = new char[size];
    f.read(buffer, size);
    if (f.gcount() != size)
    {
        cerr << "Read failed ...\n";
        exit(1);
    }

    stringstream ss;
    ss.rdbuf()->pubsetbuf(buffer, size);

    int lines = 0;
    string str;
    while(getline(ss, str))
    {
        lines++;
    }

    f.close();


    cout << "Lines=" << lines << endl;

    delete [] buffer;
}

void func_getline(const char *name)
{
    string fullname = string("bigfile_") + name;
    ifstream f(fullname.c_str());

    if (!f) 
    {
        cerr << "could not open file for " << fullname << endl;
        exit(1);
    }

    string str;
    int lines = 0;

    while(getline(f, str))
    {
        lines++;
    }

    cout << "Lines=" << lines << endl;

    f.close();
}

void func_mmap(const char *name)
{
    char *buffer;

    string fullname = string("bigfile_") + name;
    int f = open(fullname.c_str(), O_RDONLY);

    off_t size = lseek(f, 0, SEEK_END);

    lseek(f, 0, SEEK_SET);

    buffer = (char *)mmap(NULL, size, PROT_READ, MAP_PRIVATE, f, 0);


    stringstream ss;
    ss.rdbuf()->pubsetbuf(buffer, size);

    int lines = 0;
    string str;
    while(getline(ss, str))
    {
        lines++;
    }

    munmap(buffer, size);
    cout << "Lines=" << lines << endl;
}

転載記事の出典を記入してください: c – ファイルの読み込み中、ファイル全体の読み込み中にgetlineを実行し、その後改行文字に基づいて分割する - コードログ