web-dev-qa-db-ja.com

C ++のストリームファイル書き込みはバッファを使用しますか?

以下は、ファイルに50,000,000バイトを書き込む2つのプログラムです。

Cで書かれた最初のプログラムはバッファを利用し、任意の値が入力されるとディスクに書き込み、50,000,000バイトがすべて書き込まれるまでそのプロセスを繰り返します。バッファのサイズを大きくすると、プログラムの実行時間が短くなることに気付きました。たとえば、BUFFER_SIZE = 1の場合、プログラムは約88.0463秒かかりましたが、BUFFER_SIZE = 1024の場合、プログラムは約1.7773秒しかかかりませんでした。私が記録した最高の時間は、BUFFER_SIZE = 131072のときでした。BUFFER_SIZEがそれより大きくなると、実際には少し長くかかるようになりました。

C++で記述された2番目のプログラムは、ofstreamを使用して一度に1バイトを書き込みます。驚いたことに、このプログラムの実行には1.87秒しかかかりませんでした。 BUFFER_SIZE = 1のCプログラムのように、1分ほどかかると予想しました。明らかに、C++のストリームの処理は、ファイルの書き込みが思ったよりも異なっています。私のデータによると、それはBUFFER_SIZE = 512のCファイルとほぼ同じように実行されています。ある種の舞台裏バッファーを使用していますか?

Cプログラムは次のとおりです。

const int NVALUES = 50000000; //#values written to the file
const char FILENAME[] = "/tmp/myfile";
const int BUFFER_SIZE = 8192; //# bytes to fill in buffer before writing

main()
{
    int fd;  //File descriptor associated with output file
    int i;
    char writeval = '\0';
    char buffer[BUFFER_SIZE];

    //Open file for writing and associate it with the file descriptor
    //Create file if it does not exist; if it does exist truncate its size to 0
    fd = open(FILENAME, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);

    for(i=0;i<NVALUES;i++)
    {
        //Package bytes into BUFFER_SIZE chunks 
                //and write a chunk once it is filled
        buffer[i%BUFFER_SIZE] = writeval;
        if((i%BUFFER_SIZE == BUFFER_SIZE-1 || i == NVALUES-1))
            write(fd, buffer, i%BUFFER_SIZE+1);

    }

    fsync(fd);

    close(fd);
}

以下はC++プログラムです。

int main()
{
    ofstream ofs("/tmp/iofile2");
    int i;

    for(i=0; i<50000000; i++)
        ofs << '\0';

    ofs.flush();
    ofs.close();

    return 0;
}

お時間をいただきありがとうございます。

28
user1077685

はい、ostreamは、テンプレートbasic_streambufのインスタンス化のサブクラスであるストリームバッファーを使用します。 basic_streambufのインターフェースは、実装にバッファリングができるように設計されています。

ただし、これは実装の品質の問題です。実装はこれを行う必要はありませんが、有能な実装は必要です。

ISO標準の第27章ですべてについて読むことができますが、より読みやすいソースはC++標準ライブラリ:チュートリアルとリファレンス(- google search )。

11
bames53

はい、すべてのストリーム操作はバッファリングされますが、デフォルトでは標準入力、出力、およびエラー出力はバッファリングされないため、C IOとの相互作用はそれほど驚くべきことではありません。

すでに触れたように、舞台裏で使用される基本クラス streambuf があります。それはそれ自身のバッファを備えており、そのサイズは実装の詳細です。

streambuf::in_avail を使用して、このバッファの量を(実験的に)チェックできます。入力ファイルストリームと出力ファイルストリームが同じバッファサイズで設定されていると仮定します...

ここで実行できる他に興味深い操作が2つあります。

  • ストリームで使用されるstreambufオブジェクトを変更して、カスタムバージョンに切り替えることができます
  • streambufオブジェクトが使用するバッファを変更できます

どちらも、ストリームの作成直後またはflushの後に実行する必要があります。データが失われないようにするためです...

バッファの変更を説明するために、チェックアウト streambuf::putsetbuf

#include <fstream>
#include <vector>

int main () {
  std::vector<char> vec(512);

  std::fstream fs;
  fs.rdbuf()->pubsetbuf(&vec.front(), vec.size());

  // operations with file stream here.
  fs << "Hello, World!\n";

  // the stream is automatically closed when the scope ends, so fs.close() is optional
  // the stream is automatically flushed when it is closed, so fs.flush() is optional

  return 0;
}

これで、Cで行った実験を繰り返して、スイートスポットを見つけることができます。

12
Matthieu M.

this ごとに、ofstreamには内部filebufポインターがあり、rdbuf関数を介して読み取ることができ、streambufを指しますこれは次のオブジェクトです。

streambufオブジェクトは通常、1つの特定の文字シーケンスに関連付けられており、そこから内部メモリバッファーを介してデータを読み書きします。バッファーは必要なときに関連する文字シーケンスの物理的な内容と同期することが期待されるメモリ内の配列。

私は重要な部分を太字にしました、それはバッファを利用しているようですが、それがどのような種類のバッファであるのかわかりません。

2
Tony The Lion