2011-01-08 10 views
1

パフォーマンス重視のコードパスの中核となるクラスがありますので、可能な限り最適化しようとしています。するために使用クラス:同じ種類のC++テンプレートオブジェクトを格納する

class Widget 
{ 
    Widget(int n) : N(n) {} 
    .... member functions that use the constant value N .... 
    const int N;    // just initialized, will never change 
} 

コンストラクタの引数はコンパイル時に知られているので、Nは関数にコンパイルすることができるように、私は、テンプレートにこのクラスを変更しました:

template<int N> 
class Widget 
{ 
    .... member functions that use N .... 
} 

は、私が法と別のクラスがあります:私はもうこのような関数を定義することはできませんので

Widget & GetWidget(int index); 

をしかし、ウィジェットをテンプレート化した後、各ウィジェットは異なるタイプがあります。私は異なる継承オプションを考慮しましたが、テンプレートからのパフォーマンス向上が継承された関数呼び出しのコストを上回るかどうかはわかりません。

SO、私の質問はこれです:

私は両方の世界(コンパイル時/実行時)の最善の希望はかなり確信している、それができない場合があります。しかし、コンパイル時にNを知るというパフォーマンスを得る方法はありますか?それでも同じ種類のWidgetsを返すことはできますか?

ありがとうございます!

答えて

8

ここでの問題は、あなたは(GetWidgetを呼び出すことによって)その店からウィジェットを取得し、その後、コードと同じタイプなどのウィジェットを格納する場合はありませんコンパイル時に[N]を知っています。コンストラクタを呼び出すコードはNを知っていますが、オブジェクトを使用するコードは複数の可能性に対応しなければなりません。

パフォーマンスヒット(存在する場合)は、ウィジェットを作成するコードではなく、そのウィジェットを使用するコードにある可能性が高いため、ランタイム情報に依存する重要なコードでの何かを実行することは避けられません。 。オプティマイザ

class Widget { 
    public: 
    virtual ~Widget() {} 
    virtual void function() = 0; 
}; 

template <int N> 
class WidgetImpl : public Widget { 
    public: 
    virtual void function() { use N; } 
}; 

それははあなたのクラステンプレートで実現される機能への仮想呼び出しは、値を知らなくても、Nを使用する関数への非仮想呼び出しよりも高速であることであってもよいですループが最適に展開され、算術演算などが変換されるため、Nがわかっているときにはおそらく最高の仕事をすることができます。しかし、呼び出しのいずれもインライン化できないことであると起動するあなたは一つの大きな欠点を見ている仮想呼び出し、と(と私は仮想呼び出しがインライン化しない場合は非仮想呼び出しよりも予測しにくいですよね)。未知のNでインライン展開することによる利益は、Nを知ることの利益以上になる可能性があります。両方を試してみてください。より多くのこじつけの努力のために

は、一般的な例適度に小さい数がある場合、あなたものようなものとして、重要なウィジェット機能を実装することで改善が表示される場合があります

switch(n) { 
    case 1: /* do something using 1 */; break; 
    case 2: /* do the same thing using 2 */; break; 
    default: /* do the same thing using n */; break; 
}; 

「何かをする」ためにデフォルト以外のすべてのケースでは、定数にテンプレート化された関数を呼び出すことができます。デフォルトは、テンプレートパラメータではなく関数パラメータを持つ同じコードです。あるいは、関数のパラメータを使用して同じ関数を呼び出すこともできますが、パラメータが定数の場合はテンプレート化された場合と同じ結果を得るために、コンパイラが最適化前にインライン化することができます。

ない大規模な保守性、そしてそれは、このようなオプティマイザを第二推測するのは悪い考えですが、多分あなたは一般的なケースが何であるかを知っており、コンパイラがありません。このような関数テンプレートと

呼び出し元のコードはコンパイル時にNの値を知っている場合は、[*]、その後、あなたは置き換えることができますがGetWidget

template <int N> 
Widget<N> &getWidget(int index) { 
    return static_cast<Widget<N> &>(whatever you have already); 
} 

しかし、私は、呼び出し側が把握していないと仮定し、それはその後なかった場合、あなたはおそらく、私は次のオプションではありません推測...

+0

+1は "どちらも試してみてください"です。特定のNに対して複数の関数を持たせることは、libfftwの実装が性能を助ける場合があるという事例の1つです。 - (2 * M_PI/n)の 'cos'と' sin'の値を格納することができます。ある意味、定数として。 – aschepler

2

あなたは、テンプレートタイプが継承する鋳型非依存型を宣言した後、非テンプレートベースクラスへのポインタとしてウィジェットを格納する必要があります。これは、あなたが探しているものを達成するための唯一の(タイプセーフな)方法です。

ただし、テンプレート化されていないバージョンを保持する方がよりクリーンです。ランタイム構成のバージョンのループが実際にボトルネックになっていることを確認するためにコードをプロファイリングしましたか?

+0

実際に(それが、頻繁にあること、すなわち30万回/秒を使用している)衝撃性能を行い、私はこれを具体的にプロファイリングしていないが、我々は我々が作るいくつかの時間とすべてのマイナーチェンジのため、このコードを見てきました。 – JaredC

+0

@ JaredC次に、基本クラスのメソッドを使用する必要があります。私は3Dレンダリングと同様のシナリオに遭遇しました。顔のリストが必要でした。その中には3つの頂点と4つのものがありました。純粋な仮想 'render'メソッドを持つ基本クラスを持つテンプレートクラスは、ヒープ割り当てとループを減らすことを可能にしました大幅なスピードアップをもたらしました。 –

+0

私はこれを試してパフォーマンスを測定します、ありがとう。 – JaredC

1

テンプレートのGetWidget関数を記述することができます。それはあなたがGetWidget呼び出すときのタイプを知っているあなたを必要とする:

w = GetWidget<Box>(index); 
2

を尋ねることはないので?

template <int N> 
Widget<N> & GetWidget(); 

とにかく、あなたが一緒にいくつかのウィジェットタイプを管理しているや否や、あなたは一つの容器内の異なるタイプのオブジェクトを格納することはできませんので、彼らはもはやテンプレート化することはできません。それは、私はクラスがテンプレート化することはどんな利益を得なかったことを推測している仮想関数呼び出しのコストが発生しますので、マイケルが提案し

非テンプレートベースクラスは、ソリューションですけど。

2

あなたのタイプはfinite and knownしている場合、あなたは、コンストラクタの引数としてboost::variantを使用することができます。

バリアントクラステンプレートは、一般的に安全、 であり、スタックベース 均一的にタイプの異種の集合からオブジェクト を操作するための簡単な ソリューションを提供し、 組合コンテナを判別。このようなSTD ::ベクトルとして標準 コンテナがASの 考えることができるのに対し、「多値、単一 タイプ、」変異体「は、多型、単一 値」です。ここ

あなたは more flexibilityため boost::anyを使用することができ、またいくつかの擬似コード

boost::variant< int, double, std::string > variant; 
const variant foo(1); 
const variant bar(3.14); 
const variant baz("hello world"); 

const Widget foo_widget(foo); 
const Widget bar_widget(bar); 
const Widget baz_widget(baz); 

です。

+0

ウィジェットは、型上ではパラメータ化されていませんが、ちょうどint上にあります。 –

+0

@スティーブの良い点、私は質問にそれを逃した。 –

+0

Nがタイプの場合、これはほぼ完璧な解決策でした。バマー。 boost :: mpl :: int_ <5>のようなもので、型をパラメータ化して同じ効果を得ることができますか? – JaredC

関連する問題