C ++プログラムでscanf()を使用すると、cinを使用するよりも高速ですか?


126

これが本当かどうかはわかりませんが、問題を提供しているサイトの1つに関するFAQを読んでいたときに、何かに気づきました。

入出力メソッドを確認してください。C ++では、cinおよびcoutの使用は遅すぎます。これらを使用すると、適切な量の入力または出力で問題を解決できないことが保証されます。代わりにprintfとscanfを使用してください。

誰かがこれを明確にしてもらえますか?C ++プログラムで実際にscanf()を使用するのは、cin >>を使用するよりも速いですか?はいの場合、それをC ++プログラムで使用するのは良い習慣ですか?私はC ++を学習しているだけですが、C固有のものだと思いました...


14
私の推測:悪いプログラマーは、標準ライブラリーのパフォーマンスが悪いと非難します。いつも滑稽な「私はGCCのバグを見つけたと思います」のようなものです。
John Kugelman、

11
@eclipse:私がコンテストで取り組んだACMの問題にはかなりの量の入出力があり、プログラムは60秒程度の時間で問題を解決する必要があります...これは本当の問題になります。
mpen 2009年

19
---とはいえ、余分なパフォーマンス向上のためにscanf()に依存する必要がある場合、問題は間違った方向に進んでいます:)
mpen

4
観察と同じように-私はそれを試し、2番目の問題(PRIME1)で同じアルゴリズムを使用しました。一度はcin / coutを使用し、もう1つはscanf / printfを使用し、最初のバージョンは2番目よりも高速でした(ただし統計的に無関係であるほど十分に近い)。これは、入出力集約型としてマークされている問題の1つであり、入出力の方法によって統計的な差異はまったく生じませんでした。
Eclipseの

4
@Eclipse-両方の方法のテストに関する情報をありがとう。私は悲しいですが、私はcinとcoutのせいにしようとしましたが、私のアルゴリズムには問題があることがわかりました:)
zeroDivisible

回答:


209

これは、単純なケースの簡単なテストです。標準入力から数値のリストを読み取り、すべての数値のXORを行うプログラムです。

iostreamバージョン:

#include <iostream>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  while (std::cin >> x)
    parity ^= x;
  std::cout << parity << std::endl;

  return 0;
}

scanfバージョン:

#include <stdio.h>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  while (1 == scanf("%d", &x))
    parity ^= x;
  printf("%d\n", parity);

  return 0;
}

結果

3番目のプログラムを使用して、33,280,276個の乱数を含むテキストファイルを生成しました。実行時間は次のとおりです。

iostream version:  24.3 seconds
scanf version:      6.4 seconds

コンパイラーの最適化設定を変更しても、結果はほとんど変わりません。

したがって、実際には速度の違いがあります。


編集:ユーザーclyfish は、速度の違いは主にiostream I / O関数がCI / O関数との同期を維持しているためであると指摘しています。これをオフにするには、次を呼び出しますstd::ios::sync_with_stdio(false);

#include <iostream>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  std::ios::sync_with_stdio(false);

  while (std::cin >> x)
    parity ^= x;
  std::cout << parity << std::endl;

  return 0;
}

新しい結果:

iostream version:                       21.9 seconds
scanf version:                           6.8 seconds
iostream with sync_with_stdio(false):    5.5 seconds

C ++ iostreamが勝利しました! この内部同期/フラッシュは、通常、iostream I / Oの速度を低下させるものであることがわかりました。stdioとiostreamを混在させない場合は、オフにすることができ、iostreamが最も高速になります。

コード:https : //gist.github.com/3845568


6
「endl」を使用すると実行が遅くなると思います。
クリシュナモハン

2
std :: endlの使用はループにありません。
nibot

同期のオン/オフに違いはありません。libc ++のせいにします。libstdc ++のブーストのみ
iBug

<cstdio>と<stdio.h>の間に違いがあると思いますか?
Chandrahas Aroori

iostream1回のscanf呼び出しで複数の整数を解析すると、は失われます。
Maxim Egorushkin

68

http://www.quora.com/Is-cin-cout-slower-than-scanf-printf/answer/Aditya-Vishwakarma

cin/ coutは、基礎となるCライブラリとの同期を保つ必要があるため、パフォーマンスが低下する可能性があります。これは、C IOとC ++ IOの両方を使用する場合に不可欠です。

ただし、C ++ IOのみを使用する場合は、IO操作の前に次の行を使用してください。

std::ios::sync_with_stdio(false);

この詳細については、対応するlibstdc ++ docsを参照してください


上記の行をチェックしたところ(std :: ios :: sync_with_stdio(false);)そして、iostreamをcstdioとほぼ同じ速さで実現
gabrielhidasy

cin.tie(static_cast <ostream *>(0));も使用します。パフォーマンス向上のため
Mohamed El-Nakib 2014

42

おそらくscanfはストリームを使用するよりもいくらか高速です。ストリームは多くのタイプセーフを提供し、実行時にフォーマット文字列を解析する必要はありませんが、通常、過度のメモリ割り当てを必要としないという利点があります(これはコンパイラとランタイムに依存します)。とは言っても、パフォーマンスが唯一の最終目標であり、クリティカルパスにいる場合を除いて、より安全な(遅い)メソッドを優先する必要があります。

Herb Sutterによる「マナーファームの文字列フォーマッター」による非常に美味しい記事があり、文字列フォーマッターのパフォーマンスの詳細sscanflexical_cast、低速または高速で実行されているのはどのようなものでしたか。これは、CスタイルのIOとC ++スタイルの間のパフォーマンスに影響を与えるようなものに似ています。フォーマッタとの主な違いは、タイプセーフとメモリ割り当ての数である傾向がありました。


19

UVa Onlineの問題に取り組む夜を過ごしました(Factovisors、非常に興味深い問題、チェックしてください):

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=35&page=show_problem&problem=1080

提出物にTLE(制限時間を超えた)が出ました。これらの問題解決オンライン裁判官サイトでは、ソリューションの評価に使用される数千のテストケースを処理するために、2〜3秒の時間制限があります。このような計算集中型の問題では、マイクロ秒ごとにカウントされます。

私は提案されたアルゴリズム(サイトのディスカッションフォーラムでお読みください)を使用していましたが、まだTLEを取得していました。

"cin >> n >> m"を "scanf("%d%d "、&n、&m)"に変更し、いくつかの小さな "out"を "printfs"に変更すると、TLEは "Accepted"に変わりました!

そう、そうです、特に時間制限が短い場合、それは大きな違いを生む可能性があります。


同意します。同じことはUVAオンライン裁判官の問題で私に起こった:陸軍相棒のuva.onlinejudge.org/...
モハメド・エル・Nakib

6

パフォーマンスと文字列フォーマットの両方に関心がある場合は、Matthew WilsonのFastFormatライブラリをご覧ください

編集-そのライブラリーのアキュ出版物へのリンク:http : //accu.org/index.php/journals/1539


完全に同意します。ただし、FastFormatは出力専用であることを認識する必要があります。入力/読み取り機能はありません。(まだ、とにかく)
dcw

残念ながら、そのリンクは機能していないようです。ここではウェイバックマシンのコピーがあります:web.archive.org/web/20081222164527/http://fastformat.org
nibot

2

FILE *をC ++ streambufと​​して実装するstdio実装(libio)、およびランタイムフォーマットパーサーとしてfprintfがあります。IOstreamはランタイム形式の解析を必要としません。それはすべてコンパイル時に行われます。したがって、バックエンドが共有されている場合、実行時のiostreamの方が高速であることを期待するのは妥当です。


そうは思いません。GNUのlibcは純粋なCおよびアセンブリだと思います。
Chris Lutz、

2

はい、iostreamはcstdioより遅いです。
はい、C ++で開発している場合は、おそらくcstdioを使用すべきではありません。
そうは言っても、書式設定、タイプセーフ、何とか何とか何とか何も気にしないのであれば、scanfよりもI / Oを取得するためのより高速な方法があります...

たとえば、これはSTDINから数値を取得するカスタムルーチンです。

inline int get_number()
{
    int c;        
    int n = 0;

    while ((c = getchar_unlocked()) >= '0' && c <= '9')
    {
        // n = 10 * n + (c - '0');
        n = (n << 3) + ( n << 1 ) + c - '0';
    }
    return n;
}

1
getchar_unlocked()は非標準であり、ビジュアルスタジオではなくgccで使用できます
Mohamed El-Nakib '05 / 02/05

1

問題は、呼び出しcinより上の抽象化レイヤーを提供するため、多くのオーバーヘッドが発生することですscanf()。C ++ソフトウェアを作成scanf()してcinいる場合は、それが必要でcinあるため、overを使用しないでください。パフォーマンスが必要な場合は、おそらくC ++でI / Oを作成することはおそらくないでしょう。


2
あるcinよりも(実行時に)本当に多くの「抽象的」scanf?私はそうは思いません... scanfは実行時にフォーマット文字列を解釈する必要がありますが、iostreamはコンパイル時にフォーマットを認識します。
nibot 14

1
@nibot:タイプはコンパイル時に認識されますが、フォーマットは認識されません。たとえば、入力が16進数であると予想されるかどうかはstd::istream実行時に(I / Oマニピュレータを介して、またはistreamオブジェクト自体にフラグを設定することによって)がどのように構成されるかに完全に依存します。FILE*一方、オブジェクトがそのような状態を有していないのでへの呼び出しscanfこの点では、はるかに安定しています。
dreamlax

1

cincout一般的な使用には、より遅いように見えるscanfprintfC ++で、実際に彼らは高速です!

重要なのは、C ++では、cinandとを使用するたびにcout、デフォルトで同期プロセスが実行され、プログラムscanfと両方で使用した場合cin、両方が互いに同期して動作することを確認します。この同期プロセスには時間がかかります。したがってcincoutAPPEARは遅くなります。

ただし、同期プロセスが発生しないように設定されている場合は、cinよりも高速ですscanf

同期プロセスをスキップするには、次のコードスニペットをの最初のプログラムに含めますmain()

std::ios::sync_with_stdio(false);

詳細については、このサイトにアクセスしてください。


同期についての説明は+1。同期をオフにして、一部のコードでscanfとcinの両方を使用しました。今、私はそれの何が悪いのかを知っています。ありがとうございました!
Dariush

1
#include <stdio.h>
#include <unistd.h>

#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)

static int scanuint(unsigned int* x)
{
  char c;
  *x = 0;

  do
  {
      c = getchar_unlocked();
      if (unlikely(c==EOF)) return 1;
  } while(c<'0' || c>'9');

  do
  {
      //*x = (*x<<3)+(*x<<1) + c - '0';
      *x = 10 * (*x) + c - '0';
      c = getchar_unlocked();
      if (unlikely(c==EOF)) return 1;
  } while ((c>='0' && c<='9'));

  return 0;
}

int main(int argc, char **argv) {

  int parity = 0;
  unsigned int x;

  while (1 != (scanuint(&x))) {
    parity ^= x;
  }
  parity ^=x;
  printf("%d\n", parity);

  return 0;
}

ファイルの最後にバグがありますが、このCコードは高速のC ++バージョンよりも劇的に高速です。

paradox@scorpion 3845568-78602a3f95902f3f3ac63b6beecaa9719e28a6d6  make test        
time ./xor-c < rand.txt
360589110

real    0m11,336s
user    0m11,157s
sys 0m0,179s
time ./xor2-c < rand.txt
360589110

real    0m2,104s
user    0m1,959s
sys 0m0,144s
time ./xor-cpp < rand.txt
360589110

real    0m29,948s
user    0m29,809s
sys 0m0,140s
time ./xor-cpp-noflush < rand.txt
360589110

real    0m7,604s
user    0m7,480s
sys 0m0,123s

元のC ++は30秒かかり、Cコードは2秒かかりました。


-1

もちろん、iostreamではなくcstdioを使用するのはばかげています。少なくともソフトウェアを開発するときは(すでにc ++ over cを使用している場合は、すべての方法を試して、デメリットに悩まされるのではなく、メリットを利用してください)。

しかし、ソフトウェアを開発していないオンライン裁判官では、Microsoftソフトウェアが3秒で60秒かかることを実行できるプログラムを作成しています!!!

したがって、この場合、ゴールデンルールは次のようになります(もちろん、javaを使用してさらに問題が発生しない場合)

  • 問題を解決するためにc ++を使用し、その能力(および重さ/遅さ)のすべてを使用する
  • 時間制限がある場合は、printfsとscanfsのcinsとcoutsを変更します(クラス文字列を使用して失敗した場合は、次のように印刷します:printf(%s、mystr.c_str());
  • それでも時間が制限されている場合は、明らかな最適化を試みてください(組み込みのfor / while / dowhilesまたは再帰関数が多すぎるのを避けるなど)。また、大きすぎる参照オブジェクトを渡すようにしてください...
  • それでも時間制限がある場合は、std :: vectorsとc-arrayのセットを変更してみてください。
  • それでも時間制限がある場合は、次の問題に進んでください...

-2

場合でも、scanfより速くだったcin、それは問題ではないでしょう。ほとんどの場合、ハードドライブまたはキーボードから読み取ります。生データをアプリケーションに取り込むには、scanfそれcinを処理するよりも桁違いに長い時間がかかります。


パイプを介したIPCはどうですか?目立ったパフォーマンスヒットがあると思いますか?
dreamlax 2009年

パイプを介したIPCを使用しても、scanf / cinで解析するよりも、カーネルに出入りするのに多くの時間が費やされます。
ジェイコンロッド

8
私はこの領域でテストを行いました、そして確かにcout&cinはパフォーマンスを吸います。ユーザー入力の場合は無視できますが、パフォーマンスが重要な場合はそうではありません。ただし、より高速な他のc ++フレームワークが存在します。
ヨハネスシャウブ-litb

問題は、hddよりも遅いことiostream です。はい、それはそれだけ吸います。
polkovnikov.ph 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.