DEZZ.net

2008/6/16 月曜日

STLって遅いの?

カテゴリー: C++ — nano @ 2:28:34

なんかこう、最近templateに凝り始めて色々と変なクラスを作ってるんですが、
そのクラスの中でstd::queueを使ってたんです。

で、templateに凝ってる理由が、「パフォーマンスを維持したまま多態っぽい事がしたい!」なので、吐き出されたアセンブラとかちらちら見てみたんですよ。
アセンブラは正直全然ダメなんですが、なんかこう、見るからに効率悪そうなコードが並んでました。
仕方ないので、思い切ってqueueみたいなのを自作してみました。

そしたら、QueryPerformanceCounterで計測したクロックで比べると、大体2倍ぐらい速くなりました。
まあきっと、Allocatorとかメモリ管理とか色々で、エロいことをたくさんしてるんでしょうが、何か微妙ですね。
メモリ効率は良いのかな。

うーん。
まあ、今のマシンは速いので、堅実な実装のSTLのほうが良いですね。きっと。

2008/5/13 火曜日

setter/getter

カテゴリー: C++ — nano @ 2:02:15

Javaでは、オブジェクトが持つプロパティ(XXX)にアクセスする手段として、setter(setXXX), getter(getXXX)メソッドを定義するのが一般的。
プロパティが変更された際の振る舞いを設定することが出来るようになるから。

しかしこのsetter/getterは、ずーっと、メソッド呼び出しのオーバーヘッドがあるもんだと思ってました。
さっき知ったんですが、こいつらは最適化の対象になるため、そんなにオーバーヘッドはあるもんじゃないらしいです。
そりゃもちろん、振る舞いが定義されてればそれが実行されるというオーバーヘッドはありますが。

そこで、C++ではどうなんだろう?と思い、ちょっとやってみました。

class SetterGetter {
private:
    int n;

public:
    int getN() {
        return n;
    }

    void setN(int value) {
        this->n = value;
    }
};

class NoSetterGetter {
public:
    virtual ~NoSetterGetter() {}
    int n;
};

int _tmain(int argc, _TCHAR* argv[])
{

    SetterGetter *a = new SetterGetter();
    NoSetterGetter *b = new NoSetterGetter();

    a->setN(10);
    b->n = 11;

    printf("%d\n", a->getN());
    printf("%d\n", b->n);

    delete a;
    delete b;

    return 0;
}

これを最適化して、アセンブラで見ると↓こんな感じ。

int _tmain(int argc, _TCHAR* argv[])
{
004010C0 53               push        ebx
004010C1 56               push        esi
004010C2 57               push        edi  

	SetterGetter *a = new SetterGetter();
004010C3 6A 04            push        4
004010C5 E8 42 01 00 00   call        operator new (40120Ch)
004010CA 83 C4 04         add         esp,4
004010CD 85 C0            test        eax,eax
004010CF 74 0A            je          wmain+1Bh (4010DBh)
004010D1 C7 00 00 00 00 00 mov         dword ptr [eax],0
004010D7 8B F8            mov         edi,eax
004010D9 EB 02            jmp         wmain+1Dh (4010DDh)
004010DB 33 FF            xor         edi,edi
	NoSetterGetter *b = new NoSetterGetter();
004010DD 6A 08            push        8
004010DF E8 28 01 00 00   call        operator new (40120Ch)
004010E4 83 C4 04         add         esp,4
004010E7 85 C0            test        eax,eax
004010E9 74 0A            je          wmain+35h (4010F5h)
004010EB C7 00 0C 48 40 00 mov         dword ptr [eax],offset NoSetterGetter::`vftable' (40480Ch)
004010F1 8B F0            mov         esi,eax
004010F3 EB 02            jmp         wmain+37h (4010F7h)
004010F5 33 F6            xor         esi,esi 

	a->setN(10);
	b->n = 11;

	printf("%d\n", a->getN());
004010F7 8B 1D 48 72 40 00 mov         ebx,dword ptr [__imp__printf (407248h)]
004010FD C7 07 0A 00 00 00 mov         dword ptr [edi],0Ah 
00401103 C7 46 04 0B 00 00 00 mov         dword ptr [esi+4],0Bh 
0040110A 8B 07            mov         eax,dword ptr [edi] 
0040110C 50               push        eax  
0040110D 68 FC 47 40 00   push        offset std::_Iosb::end+4 (4047FCh) 
00401112 FF D3            call        ebx
	printf("%d\n", b->n);
00401114 8B 4E 04         mov         ecx,dword ptr [esi+4]
00401117 51               push        ecx
00401118 68 00 48 40 00   push        offset std::_Iosb::end+8 (404800h)
0040111D FF D3            call        ebx  

ばっちり最適化されてますね。

結論:暇ならsetter/getter定義しとけ

2008/4/10 木曜日

VC++2005のauto_ptrがおかしい

カテゴリー: C++ — nano @ 0:54:10

タイトルの通り。
何か変。

std::auto_ptr<test> t;

t = new Test();

↑これを実行すると、アクセス違反になる。

t.reset(new Test());

にすればOK。

operator =がおかしいっぽいんだけど。
見てみると、なぜかauto_ptr_refを引数に取るようにしてる。
で、そこからポインタのポインタを辿って…


auto_ptr<_Ty>&
operator=(auto_ptr_ref<_Ty> _Right) _THROW0()
{    // assign compatible _Right._Ref
(assume pointer)
    _Ty **_Pptr = (_Ty **)_Right._Ref;
    _Ty *_Ptr = *_Pptr;
    *_Pptr = 0;  
 // release old
    reset(_Ptr);  
 // set new
    return (*this);
}

*_Pptr = 0; // release old

↑これって一体どこが0になるんだろ。
Refの指してる場所って、呼び出し側のスタック領域じゃ?

なんかわけわからんので、auto_ptrはやめてboost::scoped_ptrにしようかと思いました。
STLってだいぶ陳腐化してるなぁ…。

追記:
http://d.hatena.ne.jp/xanthus/20080205/p2
↑ここで詳しく解説されてました。
やっぱ、0入れてるとこで領域ぶっ壊してるよねw

で、boost::scoped_ptrはnewされたポインタを扱うoperator =がありませんでした。
はいはいワロスワロス。

まあ普通にresetでいいかー。
所有権とかワケワカメな要素が無いので、auto_ptrよりはscoped_ptrのほうが効率が良いっぽい。

あぁー。俺は普通に代入がしたいんだ!
自由なタイミングでnewしたいじゃん。

2008/4/7 月曜日

仮想関数でリンカエラー

カテゴリー: C++ — nano @ 2:44:30

エラー 1 error LNK2005: _printf は既に LIBCMTD.lib(printf.obj) で定義されています。 MSVCRTD.lib

↑みたいなエラーが出て困ってる人ー!

もうね。やっぱC++はABIがしっかりしてないからダメかもしれないと思いました。
1時間半悩んだ結果、次の結論に…。

VC++2005では
1.スタティックリンクライブラリを使うときに
2.ライブラリプロジェクトと、使用する側のプロジェクトで、CRT(C Runtime Library)の設定(DLL or スタティックリンク)が異なり
3.ライブラリ側にある仮想関数を持ったクラスを使うと
リンカエラーが出ます(‘A`;)

つまり、、


↑これと


↑これを組み合わせると


↑こうなっちゃうわけですね。

うーん。これに気づくまで2時間ぐらいかかった。
ちょおおお大変だったよ!

っつうかこれってリンクの仕様としては正しいの?
バグ?

# 追記:
更に、うちの環境ではCPPUnitをスタティックリンクで使用していた関係で、余計ややこしくなってました…。
CPPUnitのライブラリをビルドしたときのCRTの設定も影響してくる。おそろしー

2008/3/3 月曜日

プリコンパイルヘッダーとLNK2005

カテゴリー: C++ — nano @ 22:23:58

こんにちは!
昨日の続きです!

エラー 4 error LNK2005: ___@@_PchSym_@00@UwlxUwveUhixUwahxirkgUwahxirkgUivovzhvUgvhgnzrmOlyq@ は既に BufferedReader.obj で定義されています。 DzException.obj

↑こんなエラーですね。

あの後、色々と調べてみたところ、

・プリコンパイル済みヘッダーを作成する (/Yc)
・リンク時のコード生成を有効にする (/GL)

この二つが組み合わさると、エラーが起こるということが判明しました!
思うに、各ソース単位でPCHのシンボルを持っちゃってて、それをリンク時に展開すると競合しちゃうよーっていう事じゃないですかね。

というわけで、
「プリコンパイル済みヘッダーを作成する (/Yc)」の代わりに、「プリコンパイル済みヘッダーを使用する (/Yu)」にすればいいんじゃね?
と思ったわけです。
MFCのプロジェクトとか作ったときのデフォルトの設定ですね。

ただし、プリコンパイル済みヘッダーを使わないプロジェクトとして作成した場合、この設定はなんだかうまくいきません。
これを実現するためには、

1. プリコンパイル済みヘッダー用のヘッダファイルを作る。 →これをpre.hとします。
2. プリコンパイル済みヘッダー用のcppソースファイル(自動生成時のStdAfx.cppみたいなやつ)を作る。 →これをpre.cppとします。
3. プロジェクトファイル(*.proj)をテキストエディタで開き、

<File
    RelativePath=".pre.cpp"
    >
</File>

みたいなところを探し出して、

<File
    RelativePath=".pre.cpp"
    >
    <FileConfiguration
        Name="Release|Win32"
    >
    <Tool
        Name="VCCLCompilerTool"
        UsePrecompiledHeader="1"
    />
    </FileConfiguration>
</File>

とすればOK!(pre.cppだけはPCHを作成する設定にする)

うちの環境では上記の方法で問題が回避できました。
いやー、難しいですねww

コンパイルするだけで難しいとか。言語仕様がかなりアレですね(‘A`)

Powered by WordPress