0

ループ内forの動的キャストのオーバーヘッドが原因で、必要以上に時間がかかっている高価なループがあります。

コード例は次のようになります (コンパイル可能)

#include <iostream>
#include <memory>

struct Base {
  virtual ~Base() {}
};

struct DerivedA : Base {};
struct DerivedB : Base {};

struct Calculator {
  virtual void proceed(const DerivedA& ) const  {
    std::cout << "doing A stuff" << std::endl;
  }
  virtual void proceed(const DerivedB& ) const  {
    std::cout << "doing B stuff" << std::endl;
  }
};

void doStuff(const std::shared_ptr<Base> &base_ptr) {
  Calculator calc;
  // Code that does stuff using only Base properties
  for(int i = 0; i < 1000000; i++) { // expensive loop
    // "redundant" dynamic cast at every iteration
    auto a_ptr = std::dynamic_pointer_cast<DerivedA>(base_ptr);
    if(a_ptr) calc.proceed(*a_ptr);
    auto b_ptr = std::dynamic_pointer_cast<DerivedB>(base_ptr);
    if(b_ptr) calc.proceed(*b_ptr);
  }
}

int main() {
  std::shared_ptr<Base> base_ptr = std::make_shared<DerivedA>();
  doStuff(base_ptr);
}

クラスは関数内で変更されないため、ループ内でポリモーフィズムのオーバーヘッド (および分岐) を回避し、ループを複数回記述することなく単一の動的キャストと単一の関数呼び出しを実行する方法が必要であると思います.

私が考えたこと:

  1. 通話中にキャストしproceedます。
  2. 来客パターン。

それらのどれもが問題を解決するとは思いません。これらは、同じことを別の方法で行っているだけです。

設計を再考することを検討していますが、その前に、このコードを改善するために必要なアイデアや提案を喜んでお聞きします.

4

3 に答える 3

2

まず第一に、OP へのコメントで Baldrick が示唆するように、OP で引用されている他の選択肢を試してみます。私は常に結果をプロファイリング/測定して、十分な情報に基づいた決定を下します。

まだ満足していない場合は、次のような提案をします。

template <typename T>
void doStuffImpl(const T &obj) {
    Calculator calc;
    for(int i = 0; i < 1000000; i++)
        calc.proceed(obj);
}

void doStuff(const std::shared_ptr<Base> &base_ptr) {

    auto a_ptr = std::dynamic_pointer_cast<DerivedA>(base_ptr);
    if (a_ptr)
        doStuffImpl(*a_ptr);

    auto b_ptr = std::dynamic_pointer_cast<DerivedB>(base_ptr);
    if (b_ptr)
        doStuffImpl(*b_ptr);
}
于 2013-11-05T16:38:29.850 に答える
0

派生クラスのタイプを列挙できるため、二重ディスパッチを回避できます-一部GetClass()を純粋な仮想として実装しますBase

void doStuff(const std::shared_ptr<Base> &base_ptr) {
Calculator calc;
// Code that does stuff using only Base properties
for(int i = 0; i < 1000000; i++) { // expensive loop

  switch(base_ptr->GetClass())
  {
    case TYPE_A:
    // "redundant" dynamic cast at every iteration
    auto a_ptr = std::dynamic_pointer_cast<DerivedA>(base_ptr);
    if(a_ptr) 
      calc.proceed(*a_ptr);
    break;

    case TYPE_B:
    auto b_ptr = std::dynamic_pointer_cast<DerivedB>(base_ptr);
    if(b_ptr)
      calc.proceed(*b_ptr);
    break;
  }
}

int main() {
  std::shared_ptr<Base> base_ptr = std::make_shared<DerivedA>();
  doStuff(base_ptr);
}
于 2015-05-06T09:01:47.187 に答える
0

データを知っていると、最適化に役立つ場合があります。

データにほとんどタイプ A が含まれていることがわかっている場合は、これが反復の if リストの最初のステートメントになるようにしてください。ただし、これが動的オブジェクトで可能かどうかはわかりません。

反復内で if-then-else ステートメントを使用して、反復される呼び出しを除外することはできますか? 常に両方の動的ポインター キャスト (a_ptr、b_ptr) を実行する必要があるか、それともワークロードを軽減する例外が発生する可能性がありますか?

これは役に立ちますか?

于 2013-11-05T15:42:35.477 に答える