enum
メンバー変数を持つクラスがあります。メンバー関数の1つは、これに基づいて動作するenum
ため、「可能な」最適化として、2つの異なる動作を2つの異なる関数として持ち、クラスに構築時に設定されるメンバー関数ポインターを与えます。私はこの状況を次のようにシミュレートしました:
enum catMode {MODE_A, MODE_B};
struct cat
{
cat(catMode mode) : stamp_(0), mode_(mode) {}
void
update()
{
stamp_ = (mode_ == MODE_A) ? funcA() : funcB();
}
uint64_t stamp_;
catMode mode_;
};
struct cat2
{
cat2(catMode mode) : stamp_(0), mode_(mode)
{
if (mode_ = MODE_A)
func_ = funcA;
else
func_ = funcB;
}
void
update()
{
stamp_ = func_();
}
uint64_t stamp_;
catMode mode_;
uint64_t (*func_)(void);
};
次に、catオブジェクトと長さの配列を作成します32
。配列をトラバースしてキャッシュに入れ、次にcats updateメソッド時間を呼び出し、配列で32
使用してレイテンシを保存しrdtsc
ます...
rand()
次に、、、を使用して数百回ループする関数を呼び出し、ulseep()
任意strcmp()
の..が戻ってきて、32
もう一度やり直します。
その結果、ブランチを使用するメソッドは常に約44
+/-10
サイクルであるように見えますが、関数ポインターを使用するメソッドは約+/-サイクルになる傾向があります130
。なぜそうなるのか知りたいのですが?
どちらかといえば、私は同様のパフォーマンスを期待していました。また、その1つの関数に対する実際の猫のクラスの完全な特殊化はやり過ぎになるため、テンプレート化はほとんどオプションではありません。