C++で色々な関数呼び出しの方法があると思うが、それぞれどれぐらいの速度(コスト)で呼び出せるかを今回測定してみた。
呼び出し方法
今回は、以下の4種類のクラスのメソッド呼び出し方法について測定してみた。
- Static Call(静的関数呼び出し)
- Member Call(メンバー関数呼出し)
- Virtual Call(仮想関数呼出し)
- Pointer Member Call(ポインタを使ってのメンバー呼出し)
コード
実際に使ったコード(クラス部分のみ)は下のものを使ってみた。
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 32 33 34 35 36 37 38 39 |
class Test1 { public: void MemberCall() { }; virtual void VirtualCall() { }; }; class Test2 : public Test1 { public: Test1 * ptr_test1; static void StaticCall() { }; void MemberCall() { }; virtual void VirtualCall() { }; void PointerMemberCall() { if( ptr_test1 ) { ptr_test1->MemberCall(); } }; }; |
それぞれの呼出しを1728000000回行ってその時間をQueryPerformanceCounterを使って測定してみた。1728000000 = 48000 * 60 * 60 * 10(特に意味はないが、48000kHzで1サンプルあたり10回呼出しが入ったときの1時間分の呼出し処理回数)
結果
IBM X40 (Intel Pentium M 1.3GHz)
呼出し | 秒 |
---|---|
Static Call | 2.757345 |
Member Call | 2.759990 |
Virtual Call | 9.552945 |
Pointer Member Call | 2.719109 |
HP nx4300 (Intel Celeron M 1.4MHz)
呼出し | 秒 |
---|---|
Static Call | 2.017109 |
Member Call | 1,901998 |
Virtual Call | 8.751331 |
Pointer Member Call | 2.0377457 |
まとめ
静的関数の呼び出しとクラスメンバー関数(this call)の呼出しはほとんど違いがないことがわかる。それに対し、仮想関数呼び出しはいずれの場合においても3〜4倍ぐらいのコストがかかっていることがわかる。
実際に使う場面や回数によってはこの3〜4倍のコストが大きくタイムロスにつながる場合もあるので注意して使う必要はある。
また、Pointer Member Callと今回は名づけているが、ポインタをチェックしてそのメンバのメソッドを呼ぶものについては単純なメンバ呼出しと比べてもほとんどコストがかかってないことがわかる。(ポインタチェックが常にTrueになることから分岐予測が効果的に効いているからであろうか、、、)
ちなみに、この2つのCPUであるが、Pentium Mの方が一世代前のものなので(L2キャッシュも同じサイズなので)Celeron Mの方が速い。(全体的なパフォーマンスもベンチマークではnx4300の方がよい、、、本体は重たいが)