8

この質問に答える前に、分岐予測も考慮してください。

関数ポインタを使用して、条件文を関数の呼び出しに置き換えることができるシナリオがいくつかあります。このようなものがあります。(同様のタイプのシナリオでは、継承よりもコンポーネントベースのプログラミングを考えることができます)

     class Shape
     {
        float Area()
        {
            if(type == SQUARE)
             {
                return length*length;
             }
            else if(type == RECTANGLE)
            {
             return length*breadth;
            }
        } 
     }

同じクラスをこのように書くことができます。

       class Shape
     {
        void SetAreaFunction(void *funcptr)//this function is used to set the current AreaFunc
        {
             CurrentAreaFunc = funcptr ;//this holds the pointer to current area func
        }
        float SqauareArea();//this will return square area
        float RectangleArea();//this will return rectangle area 
        float Area()
        {
            currentAreaFunc();
        } 
     }

上記の場合を考えれば、どちらも同じ結果になりますが、パフォーマンスのオーバーヘッドを考えています.2番目のケースでは、関数呼び出しを行うことで分岐予測の問題を回避しています。

ここで、この種のシナリオでどちらがより良いプラクティスであり、「より適切に最適化されたコード」であるかを教えてください(ところで、「時期尚早の最適化はすべての悪の根源である」というステートメントは好きではありません。コードの最適化を検討してください!)

PS:アセンブリコードでも、「分岐予測がいかに悪いか」について詳細な概要を説明してもかまいません。

更新:プロファイリング後(上記のコードと同様の種類)、
Conditionがこの種のシナリオで成功した場合、誰かがこれの理由を説明できますか?分岐コードがないので、機能呼び出しコードをプリフェッチできますか?しかし、ここでは逆に見えます。分岐コードが勝ちます!:O Intel Mac Osx、GCC O3/Os最適化でプロファイルされます。

4

3 に答える 3

10

ifステートメントを間接参照に置き換えました。

ifステートメントと間接参照の両方にメモリアクセスが必要です。

ただし、ifは短いジャンプになります。これにより、パイプラインが無効になることはありませんが、間接参照によってパイプラインが無効になる可能性があります。

一方、間接参照はジャンプですが、ifステートメントは条件付きジャンプです。分岐予測が見落とされる可能性があります。

テストせずにどちらが速いかを判断するのは困難です。ifステートメントが勝つと予測しています。

結果を共有してください!

于 2011-10-01T09:51:02.063 に答える
3

特定の環境(コンパイラ、コンパイラバージョン、OS、ハードウェア)に対して特定のステートメントを作成できるように、このようなコードをプロファイリングする必要があります。また、特定のアプリケーションで測定して、これがそのアプリケーションにとって重要かどうかを知る必要があります。ライブラリコードを記述している場合を除いて、プロファイリングでこれがアプリケーションのホットスポットであることが示された場合を除いて、気にしないでください。

保守が最も簡単な、最も読みやすいコードを書くだけです。バグが最適化されたコードを修正するよりも、クリーンでバグがなく、読みやすいコードを最適化する方が常に簡単です。

そうは言っても、LippmanがC ++オブジェクトモデルで、仮想関数(基本的には関数ポインター)が少なくとも実際のアプリケーションで型を切り替えるのと同じくらい高速であることを発見した研究を引用していることを覚えています。詳細はわかりませんが、本のどこかにあります。

于 2011-10-01T09:30:32.800 に答える
3

オプティマイザーが魔法をifステートメントに適用してから、動的に変化する関数ポインターに適用できる可能性が高くなります。推測ですが、理論的には、コンパイラーは、セマンティクスを変更しないことを証明できることは何でも実行できます。

ただし、関数を呼び出さにブランチを実装するだけの場合(この場合はを使用if)、CPUはその魔法を適用する可能性が高くなります。つまり、命令の並べ替え、プリフェッチなどです。ほとんどのCPUの間に関数呼び出しがある場合、パイプラインが「フラッシュ」される可能性が高く、CPUの最適化は不可能です。

とは言うものの、ヘッダー、呼び出し元、呼び出し先の中にすべてを入れると、コンパイラーは関数呼び出しを廃止したり、それ自体を並べ替えたりする可能性があります。

自分で測定してみてください。それを100万回呼びます。C ++ 11では、を使用<chrono>monothonic_clock::now()ます。

更新:私の経験は次のとおりです。私の手でコードを過度に最適化しないでください。コードが悪化する可能性が非常に高くなります。コンパイラに作業を任せ、そのためにできるだけ多くのコードをコンパイラに表示させます。もしそうなら、あなたは間違いなくプロファイラーを必要とし、代替案を試して、それらのいくつかがあなたに提供するヒントを使用してください。ただし、忘れないでください。これは非常に注意深く微調整する必要があります。パフォーマンスの唯一測定値は速度です。読みやすさテスト可能性再利用性などもあります。そして、ドナルド・クヌースの言葉を引用すると、「時期尚早の最適化はすべての悪の根源です。」

于 2011-10-01T09:59:15.617 に答える