忍者ブログ
  • 2019.11
  • 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
  • 2020.01
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
cout と printfの速さを比較してみた。

cout は遅い・・・

endlが遅い要因という話が多いので抜いてみても遅い・・。
【2013/01/05 13:10 】 | C/C++ | 有り難いご意見(0)
                                    
構造化プログラミング
私が思う構造化プログラミングとは、順次・選択・繰り返しにおいて
状態遷移がきちんと定義されていること。

これを理解できてないプログラマは多いと思う。

ある特定の条件の時だけ値を返すマイルールを
コメントに書かないで平気だとほんと困る。

●非構造化プログラミング

int a = 0; /* condition 1:真 2:偽 */
int b = 0; /* 1:処理Bを実施する 2:処理Bを実施しない */

if(condition)
{
a = 1;
b = 1;
}
else
{
a = 2;
}

if(a==1)
{
if(b==1)
{
/* 処理B */
}
}


●構造化プログラミング

int a = 0; /* condition 1:真 2:偽 */
int b = 0; /* (a = 1のとき)1:処理Bを実施する 2:処理Bを実施しない (左記以外)未定 */

if(condition)
{
a = 1;
b = 1;
}
else
{
a = 2;
}

if(a==1)
{
if(b==1)
{
/* 処理B */
}
}
【2012/10/12 06:22 】 | C/C++ | 有り難いご意見(0)
                                    
CにもVMあったんだ~

Clang&gcc&LLVM


結局のところ平行四辺形かまるっとした四角形かの違いになるのかな。
アップルが嫌った時のgccも今はやり方が変わっているし
今のコンパイラならもう少し違っていたかもしれない。

Perl5とPerl6くらい違うイメージかな。

個人的にはSSAとCPSとの再会ってすごくおもしろいと思う。

Clangとgccも再会することは・・
ライセンスの壁は厚いし無理か(笑
【2012/03/26 07:11 】 | C/C++ | 有り難いご意見(1)
                                    
呼出規約の縮小(gcc)
呼出規約とは呼出側(caller)と呼び出された側(callee)の受け渡し規約のこと。

・callerの選択肢はクリーンアップするか、しないか
・calleeの選択肢はクリーンアップするか、しないか

stdcallの特徴は
・calleeはクリーンアップする
・callerはクリーンアップしない

cdeclの特徴は
・calleeはクリーンアップしない
・callerはクリーンアップする

考えてみてほしい。

callerとcalleeのどちらでクリーンアップするほうが良いか

・calleeでやる場合の特徴はクリーンアップが呼出回数に比例し
 プログラムサイズは小さくなる。

・callerでやる場合の特徴は下記のような場合、
 クリーンアップしなくてもいいという選択肢が生まれる。
 しかし、プログラムサイズは大きくなる
 func1(1)
 func2(1, 2)

【2012/02/26 20:21 】 | C/C++ | 有り難いご意見(2)
                                    
<<前ページ | ホーム | 次ページ>>