2

ループ内で仮想メソッドを何度も呼び出す必要がある関数があり、毎回vtableルックアップのオーバーヘッドを回避する方法があることを望んでいました。メソッドへのポインタを取得することは、これを回避するための良い方法だと思いました。以下のコードは、私がやろうとしていることを示しています。問題は、のメソッドのアドレスをメンバー関数ポインターDerivedに割り当てることができないことです。Base

class Base
{
    public:

    typedef float ( Base::*MFP )( float const & x ) const;

    virtual MFP getMFP( void ) const = 0;

    virtual float function( float const & x ) const = 0;
};

class Derived : public Base
{
    public:

    virtual MFP getMFP( void ) const
    {
        return &Derived::function;
    }

    virtual float function( float const & x ) const
    {
        return x * x;
    }
};

class Other
{
    public:

    float calculate( float const & x, Base * pBase ) const
    {
        Base::MFP function = pBase->getMFP();
        return ( ( *pBase ).*( function ) )( x );
    }
};

私がここでやろうとしていることをする方法はありますか?


編集:

まだ興味がある人のために、私は自分のコードの時限テストを実行しました。動的ディスパッチは私の計算方法を0.004%遅くしただけなので、ほとんど何もありません。完全に最適化されたMSVC2010を使用してコンパイルされました。

4

4 に答える 4

4

あなたの考えは、そのようなポインタを作成するとVMTアクセスがループから外れるという誤った仮定に基づいています(つまり、ループ内で繰り返し実行するのではなく、一度実行します)。

C ++言語では、「メンバーへのポインター関数」タイプのポインター(仮想メンバー関数にバインドされている)を介した呼び出しは、初期化の時点ではなく、呼び出しの時点で常に解決されます。これは、そのようなポインタを作成しても、何も最適化されないことを意味します。ループ内の各呼び出しは、ポインターがない場合と同様に、VMTへの本格的なアクセスを実行します。

C ++では、初期化の時点で、そのようなポインターが仮想関数の特定のバージョンを指すように強制する方法はありません。C++言語にはそのような機能はありません。

于 2012-06-26T20:26:29.033 に答える
2

しないでください。プロセッサには深いパイプラインがあり、コンパイラはほぼ確実に関数ポインタをキャッシュしています。あなたはいくつかの努力でこれを行うことができます。ただし、10年未満のコンパイラでは、これによって違いが生じないことを保証します。

于 2012-06-26T20:16:25.323 に答える
0

問題が発生するまで、時期尚早の最適化は避けます。そして、そしてその時だけ、あなたはあなたのコードをプロファイリングした後、あなたは変更を加えますか?最初に保守性と拡張性を確認します。それでもパフォーマンスの問題が発生する場合は、リファクタリングを検討してください。この例では、ルックアップのオーバーヘッドはほとんどありません。

パフォーマンスが5%向上したコードよりも読み取り可能なコードを見たいのですが、それが何をしているのかを理解するのに1日かかります。

于 2012-06-26T20:14:37.167 に答える
0

forループ内のオブジェクトがであることが確実にわかっている場合、最も簡単な方法はキャストですDerived

void foo(float);
Base* pObject;
//...
//If you know with certainty that the object is a Derived
Derived& der = *static_cast<Derived*>(object)
for(float x : floatContainer)
{
     foo(der.function(x));
}

これは、のvtableのみを呼び出すことを意味します。これはDerived、から派生するクラスの数によっては、より高速になる可能性がありますDerived。クラスがであることが保証できるのであれば、Base上記の質問で書いたものは、コンパイラが生成するものよりもはるかに遅いことを除いて、本質的にvtableです。vtableが遅いと聞いたことがあるかもしれませんが、生のCポインターや関数と比較すると、それは本当かもしれませんが、必要なものに対して十分に高速であることはほぼ完全に保証できます。

于 2012-06-26T20:25:23.020 に答える