2014-01-15 23 views
6

私は、すべての繰り返しでいくつかのデータをファイルに書き込む必要がある長いループを持っています。問題は、ファイルへの書き込みが遅くなる可能性があるため、書き込みを非同期的に行うことで時間を短縮したいと考えています。C++ unixでファイルに非同期で書き込む

これを行うには良い方法がありますか?私はそれを書き出すことによって(この場合、単一のプロデューサ、単一のコンシューマ)バッファに入れられるものを消費するスレッドを作成するべきですか?

私は、標準ライブラリ(C++ 11)以外の何も関係しないソリューションに興味があります。

+2

それは標準ライブラリの一部ではないのですが、あなた場合は、[libuv](https://github.com/joyent/libuv)チェックアウトする必要があり最高のライブラリソリューションを好きにならないでください – tay10r

+0

@TaylorFlores:ありがとう!私はそれを見ていきますが、最初の赤面では、必要以上に多く見えます。 –

+1

現在、どの機能を読み書きに使用していますか?バッファされたI/Oを実行するstdioライブラリをまだ使用していない場合は、それを試してみてください。もしそうであれば、setvbufを呼び出してバッファサイズを増やすことができます。 – Eric

答えて

14

、あなたは入出力ストリームを使用している場合はstd::endlを使用してではなく、代わりに'\n'を使用してないにより、例えば、誤ってストリームをフラッシュする回避しようとする場合があります。 IOStreamsへの書き込みはバッファリングされているので、パフォーマンスがかなり向上する可能性があります。

これで十分でない場合、次の質問はデータの書き方です。多くの書式設定が行われている場合は、実際の書式設定にほとんど時間がかかる可能性があります。書式を別のスレッドにプッシュすることができるかもしれませんが、それは単純に2バイトを別のスレッドに渡すこととは全く異なります。フォーマットするデータを保持する適切なデータ構造を渡す必要があります。あなたが実際に何を書いているかによって、適切なものが決まります。

最後に、バッファをファイルに書き込むことが実際にボトルネックで、標準のC++ライブラリを利用したい場合は、適切なストリームバッファからバッファで満たされたキューをリッスンするライタースレッドを持つのが妥当かもしれませんstd::ofstreamにバッファを書き込みます。プロデューサインターフェイスはstd::ostreamになります。バッファがいっぱいになったり、ストリームがフラッシュされたときに(おそらく、std::flushを明示的に使用して)キューに固定サイズのバッファを送ります。もう一方の読書は聞きます。以下は、標準ライブラリの機能を使用して、そのアイデアの迅速な実装です:

#include <condition_variable> 
#include <fstream> 
#include <mutex> 
#include <queue> 
#include <streambuf> 
#include <string> 
#include <thread> 
#include <vector> 

struct async_buf 
    : std::streambuf 
{ 
    std::ofstream     out; 
    std::mutex     mutex; 
    std::condition_variable  condition; 
    std::queue<std::vector<char>> queue; 
    std::vector<char>    buffer; 
    bool       done; 
    std::thread     thread; 

    void worker() { 
     bool local_done(false); 
     std::vector<char> buf; 
     while (!local_done) { 
      { 
       std::unique_lock<std::mutex> guard(this->mutex); 
       this->condition.wait(guard, 
            [this](){ return !this->queue.empty() 
                || this->done; }); 
       if (!this->queue.empty()) { 
        buf.swap(queue.front()); 
        queue.pop(); 
       } 
       local_done = this->queue.empty() && this->done; 
      } 
      if (!buf.empty()) { 
       out.write(buf.data(), std::streamsize(buf.size())); 
       buf.clear(); 
      } 
     } 
     out.flush(); 
    } 

public: 
    async_buf(std::string const& name) 
     : out(name) 
     , buffer(128) 
     , done(false) 
     , thread(&async_buf::worker, this) { 
     this->setp(this->buffer.data(), 
        this->buffer.data() + this->buffer.size() - 1); 
    } 
    ~async_buf() { 
     std::unique_lock<std::mutex>(this->mutex), (this->done = true); 
     this->condition.notify_one(); 
     this->thread.join(); 
    } 
    int overflow(int c) { 
     if (c != std::char_traits<char>::eof()) { 
      *this->pptr() = std::char_traits<char>::to_char_type(c); 
      this->pbump(1); 
     } 
     return this->sync() != -1 
      ? std::char_traits<char>::not_eof(c): std::char_traits<char>::eof(); 
    } 
    int sync() { 
     if (this->pbase() != this->pptr()) { 
      this->buffer.resize(std::size_t(this->pptr() - this->pbase())); 
      { 
       std::unique_lock<std::mutex> guard(this->mutex); 
       this->queue.push(std::move(this->buffer)); 
      } 
      this->condition.notify_one(); 
      this->buffer = std::vector<char>(128); 
      this->setp(this->buffer.data(), 
         this->buffer.data() + this->buffer.size() - 1); 
     } 
     return 0; 
    } 

}; 

int main() 
{ 
    async_buf sbuf("async.out"); 
    std::ostream astream(&sbuf); 
    std::ifstream in("async_stream.cpp"); 
    for (std::string line; std::getline(in, line);) { 
     astream << line << '\n' << std::flush; 
    } 
} 
+0

std :: ofstream :: write(バイナリデータ書き込み用)はバッファされていますか? –

+1

AndrewSpott:ファイルストリームのデフォルト設定でバッファリングされます。 'stream.rdbuf() - > setbuf(0、0)'を呼び出すことで、ファイルストリームのバッファリングを無効にすることができます。 –

+0

バッファがいっぱいになったときにバッファをフラッシュしたい場合は、手動でフラッシュするか、他の何かを呼び出すべきですか? – zangw

1

ウェブで「ダブルバッファリング」を検索します。

一般に、1つのスレッドは1つ以上のバッファに書き込みます。別のスレッドがバッファから読み込み、書き込みスレッドを「追いかける」。

これは、プログラムが効率的になるとは限りません。ファイルの効率は、ドライブがスピンダウンする機会を得ることができないように、巨大なブロックを書き込むことによって達成されます。多くのバイトの書き込みは、数バイトの書き込みよりも効率的です。

これは、バッファの内容が1kのような何らかのしきい値を超えた場合にのみ書き込みスレッドに書き込ませることで実現できます。

また、「スプーリング」または「印刷スプーリング」のトピックについても調査します。

以前のバージョンでは標準ライブラリにスレッドサポートがないため、C++ 11を使用する必要があります。私はなぜあなたが自分自身を制限するのか分からない。非同期書き込みに入る前

関連する問題