2012-01-26 7 views
3

私は自分自身にLinuxのパイプラインのコマンドを書こうとしていました。 stdinから入力を受け取り、処理してstdoutに書き込む、gnu 'cat'やsedのレプリカと考えてください。std :: cin really slow

私は元々AWKスクリプトを書きましたが、私は次のC++のコードを使用し、より高いパフォーマンスを求めていました:

std::string crtLine; 
crtLine.reserve(1000); 
while (true) 
{ 
    std::getline(std::cin, crtLine); 
    if (!std::cin) // failbit (EOF immediately found) or badbit (I/O error) 
     break; 

    std::cout << crtLine << "\n"; 
} 

これはまさに(ない任意のパラメータなし)猫です。 このプログラムはawkと同じくらい遅く、catほど速くはありません。

1GBのファイルでのテスト:

$time cat 'file' | cat | wc -l 
real 0m0.771s 

$time cat 'file' | filter-range.sh | wc -l 
real 0m44.267s 

代わりのgetline(はistream、文字列)の私が(バッファサイズ)が、無改良cin.getlineを試してみました。これは恥ずかしいです、それはバッファリングの問題ですか?私は一行ではなく、一度に100KBを取り出してみました。何か案は?

編集: あなたの意見は理にかなっていますが、犯人は文字列の作成/コピーではなく、改行のスキャンもありません。 (また、バッファのサイズもどちらでもない)。これらの2つのプログラムを見てみましょう:

char buf[200]; 
while (fgets(buf, 200, stdin)) 
    std::cout << buf; 

$time cat 'file' | ./FilterRange > /dev/null 
real 0m3.276s 




char buf[200]; 
while (std::cin.getline(buf, 200)) 
    std::cout << buf << "\n"; 

$time cat 'file' | ./FilterRange > /dev/null 
real 0m55.031s 

それらのどちらもが、文字列を操作し、それらの両方が改行スキャンを行う、しかし、一方が他方よりも17倍遅いです。彼らはcinの使用によってのみ異なる。 私は、cinがタイミングを悪化させることは間違いないと思います。

+0

に基づくものになると思いますrange.sh'?なぜC++プログラムを直接呼び出さないのですか?また、そのループの典型的なパターンは 'while(std :: getline(std :: cin、crtLine)){std :: cout << crtLine <<" \ n "; } '、しかしあなたの質問には影響しません。 –

+0

パフォーマンスをお探しの場合は、cin/coutではなくCスタイルのI/O関数を試してください; – LihO

+0

最適化でコンパイルしましたか? -O2または-O3?それはおそらく44秒を切ることはないでしょうが、あなたがタイミングを心配するならば、それは間違いなく行われるべきです。 – SaulBack

答えて

4

これはまさにcatです(パラメータはありません)。

実際はありません。これは/ bin/catとまったく同じ効果がありますが、同じ方法は使用しません。

/bin/catがより次のようになります。/bin/catはその入力に何も処理を行いません

while((readSize = read(inFd, buffer, sizeof buffer)) > 0) 
    write(outFd, buffer, readSize); 

ていることに注意してください。それはを構築せず、\nのためにそれをスキャンせず、別のシステムコールを1回だけ実行します。

あなたのプログラムは、他の一方で、string Sを構築し、それらのコピー、\nためのスキャンなどを作るなど

この小さな、完全なプログラムは、/ binに/より遅く2〜3桁を実行します猫:

#include <string> 
#include <iostream> 

int main (int ac, char **av) { 
    std::string crtLine; 
    crtLine.reserve(1000); 
    while(std::getline(std::cin, crtLine)) { 
    std::cout << crtLine << "\n"; 
    } 
} 

私はこのようにそれをタイムアウト:

$ time ./x <inputFile> /dev/null 
$ time /bin/cat <inputFile> /dev/null 


EDIT このプログラムは、ビン/猫/性能の50%以内に取得します。要するに

#include <string> 
#include <iostream> 
#include <vector> 

int main (int ac, char **av) { 
    std::vector<char> v(4096); 
    do { 
    std::cin.read(&v[0], v.size()); 
    std::cout.write(&v[0], std::cin.gcount()); 
    } while(std::cin); 
} 

、あなたの条件は、入力のライン・バイ・ライン分析を実行する場合、その後、あなたがする必要があります書式付き入力を使用するには価格を支払う一方、バイト単位の解析を実行する必要がある場合は、書式なしの入力を使用して高速化できます。

+0

私はこれを答えとしてマークしていますが、元の質問の中のEDITも読んでいます – haelix

11

あなたは、標準I/Oストリームのための良好なパフォーマンスを得るためにやりたい最初ことは、それが標準のCストリームオブジェクトとの同期をオフにするオブジェクト:

std::ios_base::sync_with_stdio(false); 

あなたは、あなたがすべきこれを行った後はるかに良いパフォーマンスを得る。あなたが良いパフォーマンスを得るかどうかは別の質問です。

一部の人々はcatが内側どうなるのかについてのおかしい事を主張しているので、ここでは別のストリームをコピーするための最速アプローチすることになっているものです。

std::cout << std::cin.rdbuf(); 

あなたが正しくできれば、私は大好きだstd::copy()互いへのストリームが、これは、ほとんどのI/Oストリームの実装とあまりうまく動作しません。

std::copy(std::istreambuf_iterator<char>(std::cin), std::istreambuf_iterator<char>(), 
      std::ostreambuf_iterator<char>(std::cout)); 

私は最終的に...

最善であることこれに願っています0
+1

実際、sync_with_stdioは非常に役に立ちそうです。これは7倍の速度を上げます – haelix

+1

実際には、これは金です。これはcinをfgetsよりも2倍遅くし、最大行の長さを知る必要がないという追加の利点があります。これ以上の「最適化」 ? :) – haelix

+1

いいえ、これほど簡単なものはありません。私はIOStreamsの独自の実装をいくつか持っていますが、かなりの数の興味深い最適化が行われていますが、不完全な状態は今のところあなたを助けません。私はまだいくつかの点でそれを得ることを望んでいます...;) –

0

あなたが本当に純粋C.

vector<char> line(0x1000); 
while(!feof(stdin)) 
    fgets(&line.front(), line.size(), stdin); 
0

を使用してみてください標準入力とはるかに優れた性能を持っているしたい場合、私はより高速なソリューションが `するフィルターである他に何sendfile

関連する問題