忍者ブログ
  • 2017.09
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 2017.11
cout vs printf (2)
なぜ、coutのほうが遅いのか?

どうやら double型が特に遅いみたいである。

で、かくかくしかじか辿っていった結果。

一因がnum_putクラスが遅いことが分かった。

num_putのdo_putオーバーライド

class my_numput : public std::num_put<char>
{
protected:
iter_type do_put(iter_type out, ios_base& str, char_type fill, double val) const
{
char buf[50], *bufp = buf;
sprintf(buf, "%g", val);
copy(buf, buf + strlen(buf), out);

return out;
}
};


localeにnum_putを追加

loc = new locale(locale::classic(), new my_numput);
cout.imbue(*loc);


●実行方法1~3で計測してみることにした。
実行方法1

cout << d ;


実行方法2

char buf[50], *bufp = buf;
sprintf(buf, "%g", d);
fputs(buf, stdout);


実行方法3

fprintf(stdout, "%g", d);


結果(ms)

実行方法1(改良後←改良なし)
0.836←0.992

実行方法2
0.768

実行方法3
0.652


情報整理
・num_putが遅いことには間違いない。
・coutは doubleを置換する場合、try catchを行っている。
・coutのostream の出力方法は4×nバイト または1バイトのみである。
・coutのostreambufの出力方法はnバイト または1バイトのみである。
・coutのnum_putの出力対象はchar型のiter_typeなので出力方法は1バイトのみである。
・coutはostreambufでバッファを抽象化し、osteramで循環キューを抽象化している。

結論
coutまわりはクラスが立て込んでいて、ostreambufにフォーマッタがあればいいが
フォーマッタはostreamはlocaleに散らばっている。
これらはostreamが使うワーク領域がostearmbufが管理するワーク領域が
異なっていることに起因しているのかもしれない。
ただし、num_putの中身がどうなっているのか未確認のため怪しい。
また、printf系はバッファを素直にI/Oにしているため
FILEポインタのみを通しているのに対し、writerと媒体の距離が近いのに対し
cout系はostreamからnum_putを経てostreambufの間を1バイトずつ細切れで渡していってる模様。
writerとバッファの距離は遠そうなイメージ?

そんなこんなで、よっぽど最適化されるようにならないと
関数を読んでるだけのprintf系に追いつくのは厳しいと思われる。
きっとjavaの初期のころってこういう課題をなんとかインラインになるように
頑張ったんだろうなと。

しかし、C++の場合はジェネリックはテンプレート頼みだし
coutなんて標準ライブラリだからインラインにする前に
コンパイル済みの関数に紐づくんじゃないかなぁと。
PR
【2013/01/06 18:12 】 | C/C++ | 有り難いご意見(0)
                                    
<<ヘロンの公式 | ホーム | cout vs printf>>
有り難いご意見
貴重なご意見の投稿














<<前ページ | ホーム | 次ページ>>