なんかこう、最近templateに凝り始めて色々と変なクラスを作ってるんですが、
そのクラスの中でstd::queueを使ってたんです。
で、templateに凝ってる理由が、「パフォーマンスを維持したまま多態っぽい事がしたい!」なので、吐き出されたアセンブラとかちらちら見てみたんですよ。
アセンブラは正直全然ダメなんですが、なんかこう、見るからに効率悪そうなコードが並んでました。
仕方ないので、思い切ってqueueみたいなのを自作してみました。
そしたら、QueryPerformanceCounterで計測したクロックで比べると、大体2倍ぐらい速くなりました。
まあきっと、Allocatorとかメモリ管理とか色々で、エロいことをたくさんしてるんでしょうが、何か微妙ですね。
メモリ効率は良いのかな。
うーん。
まあ、今のマシンは速いので、堅実な実装のSTLのほうが良いですね。きっと。
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定義しとけ
タイトルの通り。
何か変。
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したいじゃん。
エラー 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の設定も影響してくる。おそろしー
こんにちは!
昨日の続きです!
エラー 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`)