4

Boost.Arrayのような単純なクラスがあります。テンプレートパラメータTとNは2つあります。Boost.Arrayの欠点の1つは、そのような配列を使用するすべてのメソッドが、パラメータN(TはOK)のテンプレートでなければならないことです。その結果、プログラム全体がテンプレートになる傾向があります。1つのアイデアは、T(ArrayInterfaceのようなもの)のみに依存するインターフェイス(純粋仮想関数のみを含む抽象クラス)を作成することです。これで、他のすべてのクラスはインターフェイスにのみアクセスするため、テンプレートパラメーターTのみが必要になります(Nとは対照的に、多かれ少なかれ常に知られています)。ここでの欠点は、インターフェイスが使用されている場合、仮想呼び出しのオーバーヘッド(インライン呼び出しの機会を逃す機会が増える)です。ここまでは事実だけです。

template<typename T>
class ArrayInterface {
public:
    virtual ~ArrayInterface() {};
    virtual T Get(int i) = 0;
};

template<typename T, int N>
class Array : ArrayInterface<T> {
public:
    T Get(int i) { ... }
};

template<typename T, int N>
class ArrayWithoutInterface {
public:
    T Get() { ... }
};

しかし、私の本当の問題はどこかにあります。Boost.Arrayをインターフェイスで拡張すると、Boost.Arrayの直接インスタンス化が遅くなります(重要な場合は、ファクター4)。インターフェイスを削除すると、Boost.Arrayは以前と同じように高速になります。ArrayInterfaceを介してメソッドが呼び出された場合、オーバーヘッドが発生することは理解しています。しかし、純粋仮想メソッドのみを含む追加のインターフェイスのみがあり、クラスが直接呼び出される場合、メソッドの呼び出しが遅くなる理由がわかりません。

Array<int, 1000> a;
a.Get(0); // Slow

ArrayWithoutInterface<int, 1000> awi;
awi.Get(0); // Fast

GCC4.4.3とClang1.1は同じ動作を示します。

4

4 に答える 4

2

この動作は予想されるものです。仮想メソッドを呼び出しています。直接呼び出すか、基本クラスポインターを介して呼び出すかは、最初は関係ありません。どちらの場合も、呼び出しは仮想関数テーブルを経由する必要があります。

(おそらく境界チェックなしで、配列セルを単純に逆参照する)のような単純な呼び出しの場合Get、これは実際に4倍の違いを生む可能性があります。

これで、コンパイル時にオブジェクトの動的タイプ(したがってメソッド呼び出しターゲット)がわかっているため、優れたコンパイラーは、ここで追加された間接参照が不要であることを確認できます。GCCが明らかにこれを最適化していないことに少し驚いています(コンパイルしました-O3か?)。繰り返しになりますが、これは最適化にすぎません。

于 2010-11-05T13:35:49.603 に答える
2

「プログラム全体がテンプレートになる傾向がある」というあなたの結論に同意しません。問題がないことを解決しようとしているように見えます。

ただし、「インターフェイスを使用してBoost.Arrayを拡張する」とはどういう意味かは不明ですboost::array。インターフェイスを導入してソースを変更していますか?arrayその場合、仮想メソッドを使用するかどうかに関係なく、作成するすべてのインスタンスはvtableポインターをドラッグする必要があります。仮想メソッドの存在により、コンパイラーは、純粋なヘッダー定義クラスで可能な非仮想メソッドで積極的な最適化を使用することを警戒する可能性もあります。

編集済み:...そしてもちろん、仮想メソッドを使用していますコンパイラーが仮想呼び出しを最適化できることを確認するには、かなり高度なコード分析手法が必要です。

于 2010-11-05T13:37:15.997 に答える
1

2 つの理由:

  • 遅延バインディングは遅い
  • 仮想メソッドはインライン化できません
于 2010-11-05T13:33:23.237 に答える
0

拡張されない仮想メソッドがある場合、コンパイラがメソッドの仮想部分を最適化する可能性は十分にあります。通常の非仮想メソッド呼び出し中、プログラム フローは呼び出し元からメソッドに直接進みます。ただし、メソッドが仮想としてマークされている場合、CPU は最初に仮想テーブルにジャンプし、次に探しているメソッドを見つけてから、そのメソッドにジャンプする必要があります。

現在、これは通常あまり目立ちません。呼び出しているメソッドの実行に 100 ミリ秒かかる場合、仮想テーブルのルックアップに 1 ミリ秒かかっても問題ありません。しかし、配列の場合、メソッドの実行に 0.5 ミリ秒かかる場合、その 1 ミリ秒のパフォーマンスの低下は非常に顕著になります。

Boost.Array を拡張しないか、オーバーライドしないようにメソッドの名前を変更する以外に、できることはあまりありません。

于 2010-11-05T13:33:52.373 に答える