2011-01-08 71 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); 

然而,模板化的widget后,每个插件具有不同的类型,所以我不能这样定义功能了。我考虑了不同的继承选项,但我不确定模板的性能增益会超过继承函数调用的成本。

所以,我的问题是这样的:

我敢肯定我想两全其美(编译时间/运行时间),它可能是不可能的。但是,有没有办法在编译时获得知道N的性能,但是仍然能够将Widgets作为相同类型返回?

谢谢!

回答

8

这里的问题是,如果你存储部件为同一类型,然后从该商店检索小部件(通过调用GetWidget)代码不知道在编译时知道N [*]。调用构造函数的代码知道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的收益,或者可能更少。试试他们,看看。

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); 
} 

但我相信调用者不知道,因为如果它那么你可能不会问...

2

您需要声明模板类型继承的非模板类型,然后将这些小部件存储为指向非模板基类的指针。这是完成您所需要的唯一(类型安全)方式。

但是,保留非模板版本可能更干净。你有没有分析你的代码,看看运行时配置版本上的循环实际上是一个瓶颈?

+0

我还没有专门介绍这一点,但我们一直在寻找这段代码一段时间,并且我们所做的每一次小改变实际上都会影响性能(经常使用它,即300,000次/秒)。 – JaredC 2011-01-08 16:04:02

+0

@JaredC,那么你将不得不使用基类方法。我曾经遇到过类似的场景 - 3D渲染,我需要一张面部列表,其中一些有3个顶点和4个顶点。模板类的基类有一个纯虚拟的`render`方法,可以减少堆分配和使用循环展开,导致显着的加速。 – 2011-01-08 17:21:53

+0

我会试一试并测量性能,谢谢。 – JaredC 2011-01-08 18:03:09

1

你可以编写一个模板化的GetWidget函数。这需要你知道的类型,当你调用GetWidget:

w = GetWidget<Box>(index); 
2

我猜以下是不是一个选项?

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

反正,只要你管理多个构件类型在一起,你不能让他们了模板,因为你不能存储在一个容器中不同类型的对象。

Michael提出的非模板化基类是一种解决方案,但由于它会产生虚函数调用成本,我猜测让类模板化没有任何好处。

2

如果您的类型是finite and known,您可以使用boost::variant作为构造函数的参数。

的变体类模板是一种安全, 通用的,基于堆栈的判别 联合容器,提供一个简单的 溶液用于在 均匀的方式从非均相类型集合操作对象 。而标准的 容器如std :: vector可能被认为是“多值,单一的 类型”,变体是“多类型,单一的 值”。

这里是一些伪代码

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); 

或者,你可以使用一个boost::anymore flexibility