50

printfいくつかのテストを実行した後、私はそれがよりもはるかに速いことに気づきましたcout。実装に依存していることは知っていますが、私のLinuxボックスprintfでは8倍高速です。したがって、私の考えは、2つの印刷方法を混合することです。単純な印刷に使用したいのですが、巨大な出力(通常はループ)を生成するためcoutに使用する予定です。printf他の方法に切り替える前にフラッシュすることを忘れない限り、安全だと思います。

cout << "Hello" << endl;
cout.flush();

for (int i=0; i<1000000; ++i) {
    printf("World!\n");
}
fflush(stdout);

cout << "last line" << endl;
cout << flush;

そんなに大丈夫ですか?

更新:すべての貴重なフィードバックに感謝します。回答の要約:トリッキーな解決策を避けたい場合は、バッファを暗黙的にフラッシュする(プロセスを遅くする)ため、cout使用しないでください。代わりにendl使用してください。大きな出力"\n"を生成する場合は興味深い場合があります。

4

9 に答える 9

74

直接の答えはそうです、それは大丈夫です。

多くの人がスピードを上げる方法についてさまざまなアイデアを投げかけていますが、どれが最も効果的かについてはかなりの意見の相違があるようです。私は、どの手法が何を実行したかについて少なくともいくつかのアイデアを得るために、簡単なテストプログラムを作成することにしました。

#include <iostream>
#include <string>
#include <sstream>
#include <time.h>
#include <iomanip>
#include <algorithm>
#include <iterator>
#include <stdio.h>

char fmt[] = "%s\n";
static const int count = 3000000;
static char const *const string = "This is a string.";
static std::string s = std::string(string) + "\n";

void show_time(void (*f)(), char const *caption) { 
    clock_t start = clock();
    f();
    clock_t ticks = clock()-start;
    std::cerr << std::setw(30) << caption 
        << ": " 
        << (double)ticks/CLOCKS_PER_SEC << "\n";
}

void use_printf() {
    for (int i=0; i<count; i++)
        printf(fmt, string);
}

void use_puts() {
    for (int i=0; i<count; i++) 
        puts(string);        
}

void use_cout() { 
    for (int i=0; i<count; i++)
        std::cout << string << "\n";
}

void use_cout_unsync() { 
    std::cout.sync_with_stdio(false);
    for (int i=0; i<count; i++)
        std::cout << string << "\n";
    std::cout.sync_with_stdio(true);
}

void use_stringstream() { 
    std::stringstream temp;
    for (int i=0; i<count; i++)
        temp << string << "\n";
    std::cout << temp.str();
}

void use_endl() { 
    for (int i=0; i<count; i++)
        std::cout << string << std::endl;
}

void use_fill_n() { 
    std::fill_n(std::ostream_iterator<char const *>(std::cout, "\n"), count, string);
}

void use_write() {
    for (int i = 0; i < count; i++)
        std::cout.write(s.data(), s.size());
}

int main() { 
    show_time(use_printf, "Time using printf");
    show_time(use_puts, "Time using puts");
    show_time(use_cout, "Time using cout (synced)");
    show_time(use_cout_unsync, "Time using cout (un-synced)");
    show_time(use_stringstream, "Time using stringstream");
    show_time(use_endl, "Time using endl");
    show_time(use_fill_n, "Time using fill_n");
    show_time(use_write, "Time using write");
    return 0;
}

VC ++ 2013(x86バージョンとx64バージョンの両方)でコンパイルした後、これをWindowsで実行しました。1回の実行からの出力(出力はディスクファイルにリダイレクトされます)は次のようになります。

          Time using printf: 0.953
            Time using puts: 0.567
   Time using cout (synced): 0.736
Time using cout (un-synced): 0.714
    Time using stringstream: 0.725
            Time using endl: 20.097
          Time using fill_n: 0.749
           Time using write: 0.499

予想通り、結果はさまざまですが、興味深い点がいくつかあります。

  1. NULデバイスに書き込む場合、printf/putsはcoutよりもはるかに高速です
    • しかし、実際のファイルに書き込むとき、coutは非常にうまく追いつきます
  2. かなりの数の提案された最適化はほとんど達成しません
    • 私のテストでは、fill_nは他の何よりも高速です
  3. 圧倒的に最大の最適化はendlを回避することです
  4. cout.writeが最速の時間を与えました(おそらく大幅な差はありませんが)

最近、コードを編集して、を強制的に呼び出しましたprintf。Anders Kaseorgは親切にも指摘してくれました。これはg++、特定のシーケンスprintf("%s\n", foo);がと同等であることを認識しputs(foo);、それに応じてコードを生成します(つまり、putsの代わりに呼び出すコードを生成しますprintf)。フォーマット文字列をグローバル配列に移動し、それをフォーマット文字列として渡すと、同じ出力が生成されますが、printfの代わりに経由で生成されputsます。もちろん、いつか最適化される可能性もありますが、少なくとも今のところ(g ++ 5.1)のテストでは、g++ -O3 -S実際に呼び出していることを確認してprintfいます(前のコードが呼び出しにコンパイルされていますputs)。

于 2009-12-18T05:18:07.843 に答える
21

ストリームに送信std::endlすると、が追加され、ストリームがnewlineフラッシュされます。その後のの呼び出しcout.flush()は不要です。これがタイミングcout対のときに行われた場合printf、あなたはリンゴをリンゴと比較していませんでした。

于 2009-12-17T21:05:03.183 に答える
13

デフォルトでは、CとC ++の標準出力ストリームが同期されているため、一方に書き込むと他方がフラッシュされるため、明示的なフラッシュは必要ありません。

于 2009-12-17T21:09:48.870 に答える
12

また、C++ストリームはCストリームに同期されていることに注意してください。
したがって、同期を維持するために余分な作業が行われます。

注意すべきもう1つのことは、ストリームを同じ量だけフラッシュすることを確認することです。一方のシステムでストリームを継続的にフラッシュし、もう一方のシステムではフラッシュしない場合、テストの速度に確実に影響します。

一方が他方よりも速いと想定する前に、次のことを行う必要があります。

  • CI/OからC++I / Oの同期を解除します(sync_with_stdio()を参照)。
  • フラッシュの量が同等であることを確認してください。
于 2009-12-17T21:14:25.567 に答える
10

printf次のバッファサイズを増やすことで、のパフォーマンスをさらに向上させることができますstdout

setvbuf (stdout, NULL, _IOFBF, 32768);  // any value larger than 512 and also a
                  // a multiple of the system i/o buffer size is an improvement

I / Oを実行するためのオペレーティングシステムへの呼び出しの数は、ほとんどの場合、最も高価なコンポーネントおよびパフォーマンスリミッターです。

もちろん、cout出力がと混合されているstdout場合、バッファフラッシュは、バッファサイズを増やす目的を無効にします。

于 2009-12-17T21:10:58.003 に答える
5

sync_with_stdioC++IOを高速化するために使用できます。

cout.sync_with_stdio(false);

で出力パフォーマンスを向上させる必要がありますcout

于 2009-12-17T21:08:07.930 に答える
3

printfとの間のパフォーマンスについて心配する必要はありませんcout。パフォーマンスを向上させたい場合は、フォーマットされた出力をフォーマットされていない出力から分離します。

puts("Hello World\n")よりもはるかに高速ですprintf("%s", "Hellow World\n")(主にフォーマットのオーバーヘッドが原因です)。フォーマットされたテキストをプレーンテキストから分離したら、次のようなトリックを実行できます。

const char hello[] = "Hello World\n";
cout.write(hello, sizeof(hello) - sizeof('\0'));

フォーマットされた出力を高速化するための秘訣は、文字列に対してすべてのフォーマットを実行してから、文字列(またはバッファ)でブロック出力を使用することです。

const unsigned int MAX_BUFFER_SIZE = 256;
char buffer[MAX_BUFFER_SIZE];
sprintf(buffer, "%d times is a charm.\n", 5);
unsigned int text_length = strlen(buffer) - sizeof('\0');
fwrite(buffer, 1, text_length, stdout);

プログラムのパフォーマンスをさらに向上させるには、出力の量を減らします。出力するものが少なければ少ないほど、プログラムは速くなります。副作用として、実行可能ファイルのサイズも縮小します。

于 2009-12-17T22:08:19.580 に答える
1

正直に言うと、実際にcoutを使う理由は考えられません。すべてのファイルに含まれるような単純なことを行うための巨大でかさばるテンプレートを持つことは完全に正気ではありません。また、入力ができるだけ遅くなるように設計されているようで、<<<<を入力してから、その間に値を入力して、偶然に何かlik> variableName >>>を取得した後、二度とやりたくありません。 。

言うまでもなく、std名前空間を含めると、世界は最終的に崩壊します。含まない場合、入力の負担はさらにばかげたものになります。

ただし、printfもあまり好きではありません。私にとっての解決策は、独自の具象クラスを作成し、その中で必要なioのものを呼び出すことです。そうすれば、本当にシンプルなioを好きなように、好きな実装で、好きなフォーマットなどで作成できます(通常、フロートは常に一方向にしたいのですが、理由もなく800通りにフォーマットしたくないので、すべての呼び出しでフォーマットするのは冗談です)。

したがって、私が入力するのはdout+"のようなものです。これは"+debugIoType+"の"+cPlusPlusMethod+"よりも正気です。少なくともIMO"; dout ++;

しかし、あなたはあなたが望むものを何でも持つことができます。ファイルがたくさんあるので、これによってコンパイル時間がどれだけ改善されるかも驚くべきことです。

また、CとC ++の混合には何の問題もありません。慎重に行う必要があります。そもそも、Cの使用で問題を引き起こすものを使用している場合は、CとCの混合による問題が最も少ないと言っても過言ではありません。 C++。

于 2009-12-18T06:31:44.840 に答える
0

私のC++の本、FYIは、C++とCiomethodsの混合を推奨していました。私は、C関数がC++によって期待/保持されている状態を踏みにじると確信しています。

于 2009-12-17T21:08:04.147 に答える