8

私のアプリケーションでは、ソートされたファイルをマージしようとしているので(もちろん、それらをソートしたままにします)、両方のファイルの各要素を反復処理して、最小値を3番目のファイルに書き込む必要があります。これは、他に選択肢がない限り(反復を実行する必要があります)、大きなファイルではかなり遅く動作します。ファイルの読み込みを最適化しようとしています。バッファリングに使用できるRAMをある程度使用できます。つまり、100Mbのようなものを一度読み取って、その後、バッファに要素がなくなるまでそのバッファで作業できるようになるたびに、両方のファイルから4バイトを読み取る代わりに、バッファを再度補充します。しかし、ストリームがすでにそれを行っている場合、それは私にもっとパフォーマンスを与えるでしょうか、そして何か理由がありますか?fstreamがそうする場合、多分私はそのバッファのサイズを変更することができますか?

追加した

私の現在のコードはそのように見えます(擬似コード)

// this is done in loop
int i1 = input1.read_integer();
int i2 = input2.read_integer();
if (!input1.eof() && !input2.eof())
{
   if (i1 < i2)
   {
      output.write(i1);
      input2.seek_back(sizeof(int));
   } else
      input1.seek_back(sizeof(int));
      output.write(i2);
   }
} else {
   if (input1.eof())
      output.write(i2);
   else if (input2.eof())
      output.write(i1);
}

私がここで嫌いなのは

  • seek_back-4バイトをピークする方法がないため、前の位置にシークバックする必要があります
  • ファイルからの読み取りが多すぎます
  • ストリームの1つがEOFにある場合でも、別のストリームのコンテンツを直接出力するのではなく、そのストリームをチェックし続けますが、チャンクサイズはほとんど常に等しいため、これは大きな問題ではありません。

その改善を提案できますか?

ありがとう。

4

6 に答える 6

5

ストリームバッファの説明に入る必要はありませんが、次のようにすることで、を取り除き、seek_back一般的にコードをはるかに簡単にすることができます。

using namespace std;
merge(istream_iterator<int>(file1), istream_iterator<int>(),
           istream_iterator<int>(file2), istream_iterator<int>(),
           ostream_iterator<int>(cout));

編集:

バイナリ機能を追加

#include <algorithm>
#include <iterator>
#include <fstream>
#include <iostream>

struct BinInt
{
    int value;
    operator int() const { return value; }
    friend std::istream& operator>>(std::istream& stream, BinInt& data)
    {
        return stream.read(reinterpret_cast<char*>(&data.value),sizeof(int));
    }
};

int main()
{
    std::ifstream   file1("f1.txt");
    std::ifstream   file2("f2.txt");

    std::merge(std::istream_iterator<BinInt>(file1), std::istream_iterator<BinInt>(),
               std::istream_iterator<BinInt>(file2), std::istream_iterator<BinInt>(),
               std::ostream_iterator<int>(std::cout));
}
于 2010-12-30T08:57:39.223 に答える
3

パフォーマンスの降順 (最高の順):

  • メモリ マップド I/O
  • OS 固有ReadFileまたはread呼び出し。
  • fread大きなバッファに
  • ifstream.read大きなバッファに
  • ifstreamと抽出器
于 2010-12-29T22:28:58.703 に答える
2

このようなプログラムは I/O バウンドである必要があります。つまり、時間の少なくとも 80% をバッファーの読み取りまたは書き込みの完了を待つために費やす必要があり、バッファーが適度に大きい場合は、ディスク ヘッドをビジー状態に保つ必要があります。それがあなたが望むものです。

証拠がなければ、それが I/O バウンドであると仮定しないでください。それを証明する方法は、いくつかのスタックショットを撮ることです。そうである場合、ほとんどのサンプルは、プログラムが I/O の完了を待っていることを示しています。

I/O バウンドではない可能性があります。つまり、一部のサンプルで予想外のことが起こっていることに気付く場合があります。もしそうなら、あなたはそれをスピードアップするために何を修正すべきかを知っています. たとえば、ファイルの終わりのテスト、比較するデータの取得など、マージ ループで必要以上に多くの時間を費やしているこのようなコードを見てきました。

于 2010-12-30T02:26:33.667 に答える
0

データに非常に特別なものがない限り、std::fstream オブジェクトに組み込まれているバッファリングを改善することはまずありません。

std::fstream オブジェクトは、汎用ファイル アクセスに対して非常に効率的になるように設計されています。一度に 4 バイトのデータにアクセスすることで、何か特別なことをしているようには聞こえません。コードをプロファイリングして、実際の時間がコード内のどこで費やされているかをいつでも確認できます。

コードを ous と共有していただければ、重大な非効率性を発見できるかもしれません。

編集:

私はあなたのアルゴリズムが好きではありません。ストリーム上で前後にシークするのは難しい場合があります。特に、数値がバッファ境界を超えている場合はそうです。ループを介して毎回 1 つの数値のみを読み取ります。

これを試してください:
注:これは最適ではありません(そして、数値のストリーム入力を想定しています(バイナリに見えますが))しかし、出発点として使用できると確信しています。

#include <fstream>
#include <iostream>

// Return the current val (that was the smaller value)
// and replace it with the next value in the stream.
int getNext(int& val, std::istream& str)
{
    int result = val;
    str >> val;

    return result;
}

int main()
{
    std::ifstream   f1("f1.txt");
    std::ifstream   f2("f2.txt");
    std::ofstream   re("result");

    int v1;
    int v2;

    f1 >> v1;
    f2 >> v2;

    // While there are values in both stream
    // Output one value and replace it using getNext()
    while(f1 && f2)
    {
        re << (v1 < v2)? getNext(v1, f1) : getNext(v2, f2);
    }
    // At this point one (or both) stream(s) is(are) empty.
    // So dump the other stream.
    for(;f1;f1 >> v1)
    {
        // Note if the stream is at the end it will
        // never enter the loop
        re << v1;
    }
    for(;f2;f2 >> v2)
    {
        re << v2;
    }
}
于 2010-12-29T22:04:26.470 に答える
0

ifstream の read 関数を使用して、大きなブロックを読み取ることができます。

http://www.cplusplus.com/reference/iostream/istream/read/

2 番目のパラメーターはバイト数です。あなたの場合、これを 4 の倍数にする必要があります - おそらく 4096 ですか? :)

一度にチャンクを読み取って作業するだけです。

Martin-york が言ったように、これはあなたのパフォーマンスに良い影響を与えないかもしれませんが、試してみてください。

于 2010-12-29T22:23:51.957 に答える
0

大きなチャンクを読み取ることでパフォーマンスを向上できる可能性が非常に高いと思います。

ios::binary引数としてファイルを開いてから、 istream::readを使用してデータを読み取ります。

最大のパフォーマンスが必要な場合は、iostream を完全にスキップして、代わりにcstdioを使用することをお勧めします。しかし、これはあなたが望むものではないと思います。

于 2010-12-29T22:25:33.367 に答える