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

首先,我宁愿按照 Baldrick 在对 OP 的评论中建议的那样做,然后我会尝试 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

有时,如果您了解自己的数据,它有助于优化。

如果您知道您的数据主要包含类型 A,那么请确保这将是您迭代的 if 列表中的第一条语句。不过,我不确定是否可以使用动态对象。

也许您可以通过在迭代中使用 if-then-else 语句来排除一些要迭代的调用?是否真的有必要总是同时进行动态指针转换(a_ptr,b_ptr),或者是否存在减少工作量的异常?

这对你有帮助吗?

于 2013-11-05T15:42:35.477 回答
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 回答