避免数字C+中的虚函数调用+; 我在C++中编写了一些数值模拟代码。在这个模拟中,有些东西是“局部的”,在二维网格上的每个点上都有一个浮点值,而有些东西是“全局的”,只有一个全局浮点值
除此之外,这两种类型的对象的行为类似,因此我希望能够有一个包含这两种类型对象的数组。然而,由于这是一个数值模拟,我需要以一种方式来完成这项工作:(a)尽可能避免虚拟函数调用开销,(b)允许编译器尽可能多地使用优化,特别是允许编译器尽可能地进行SIMD自动矢量化 目前,我发现自己正在编写这样的代码(我现在意识到,这实际上不会按预期工作): 我希望实现与上述类似的效果,但不需要在通过内部循环的每次迭代中调用虚拟函数的开销。(除非我完全错了,而且编译器实际上可以优化所有虚拟函数调用并生成如上所述的代码。在这种情况下,告诉我这一点可以节省我很多时间!)避免数字C+中的虚函数调用+; 我在C++中编写了一些数值模拟代码。在这个模拟中,有些东西是“局部的”,在二维网格上的每个点上都有一个浮点值,而有些东西是“全局的”,只有一个全局浮点值,c++,templates,optimization,C++,Templates,Optimization,除此之外,这两种类型的对象的行为类似,因此我希望能够有一个包含这两种类型对象的数组。然而,由于这是一个数值模拟,我需要以一种方式来完成这项工作:(a)尽可能避免虚拟函数调用开销,(b)允许编译器尽可能多地使用优化,特别是允许编译器尽可能地进行SIMD自动矢量化 目前,我发现自己正在编写这样的代码(我现在意识到,这实际上不会按预期工作): 我希望实现与上述类似的效果,但不需要在通过内部循环的每次迭代中调用虚拟函数的开销。(除非我完全错了,而且编译器实际上可以优化所有虚拟函数调用并生成如上所述的代码
我确实看了一下,但不清楚如何使它适应这种情况,至少对我来说不是这样,因为对
doStuff
的多个重载参数,您几乎可以找到答案。这样的模板函数应该可以工作(尽管我不知道size
来自哪里):
您会注意到if块中的代码对于每个条件都是相同的,假设dostuffinpl
是一个模板函数。我建议将其包装在宏中以减少代码开销。您也可以自己跟踪类型,而不使用dynamic\u cast
。在Base
类中有一个枚举,它显式地列出了类型。这是一种基本上防止未知派生类出现在doStuff
的安全机制
不幸的是,这种方法是必需的。这是将动态类型转换为静态类型的唯一方法。如果您希望使用模板,您需要静态模板。在您的代码中,
本地
可以了解全局
和全局
可以了解本地
如果上述问题的答案是肯定的,则可以通过一个虚拟函数调用和两个动态强制转换来避免域中每个点的虚拟函数成本
class Base {
public:
virtual void doStuff(Base& b) = 0;
};
class Local: public Base {
public:
virtual void doStuff(Base& b);
float data[size];
// plus constructors etc.
};
class Global: public Base {
public:
virtual void doStuff(Base& b);
float data;
// ...
};
void Local::doStuff(Base& b) {
Local* lb = NULL;
Global* gb = NULL;
if ( NULL != (lb = dynamic_cast<Local*>(&b)) )
{
// Do Local+Local stuff.
}
else if ( NULL != (gb = dynamic_cast<Global*>(&b)))
{
// Do Local+Global stuff.
}
}
void Global::doStuff(Base& b) {
Local* lb = NULL;
Global* gb = NULL;
if ( NULL != (lb = dynamic_cast<Local*>(&b)) )
{
// Do Global+Local stuff.
}
else if ( NULL != (gb = dynamic_cast<Global*>(&b)))
{
// Do Global+Global stuff.
}
}
void doStuff(Base a, Base b) {
a.doStuff(b);
}
类基{
公众:
虚空密度(基准和b)=0;
};
本地类:公共基{
公众:
虚拟空穴灰岩(基础和b);
浮动数据[大小];
//加上施工人员等。
};
全局类:公共基{
公众:
虚拟空穴灰岩(基础和b);
浮动数据;
// ...
};
无效本地::doStuff(基础和b){
本地*lb=NULL;
全局*gb=NULL;
如果(NULL!=(lb=动态施法(&b)))
{
//做本地+本地的事情。
}
else if(NULL!=(gb=dynamic_cast(&b)))
{
//做本地+全球的事情。
}
}
void Global::doStuff(基础和早餐){
本地*lb=NULL;
全局*gb=NULL;
如果(NULL!=(lb=动态施法(&b)))
{
//做全球+本地的事情。
}
else if(NULL!=(gb=dynamic_cast(&b)))
{
//做全球+全球的事情。
}
}
空隙度凝灰岩(基底a、基底b){
a、 多斯塔夫(b);
}
听起来像是模板的工作。@EJP我也是!这就是我在问题中这样说的原因。但是如何使用模板来实现这一点对我来说一点也不明显。如果在编译时知道A和B的类型,这将很好,但在我的例子中不是这样。我意识到这个问题不是很清楚-我会编辑它。(size
只是某个标题中定义的常量。)派生类型的数量是常量还是无限?也就是说,您能静态地知道doStuff
?Constant中所有可能的类型吗?据我所知,只有两种派生类型,全局和局部。感谢更新版本。在这种情况下,似乎应该有一些聪明的方法来避免重复使用模板。我不太明白是怎么回事,但我把它作为另一个问题贴了出来:
class Base {
virtual float &operator[](int) = 0;
};
class Local: public Base {
float data[size];
public:
float &operator[](int i) {
return data[i];
}
// …
};
class Global: public Base {
float data;
public:
float &operator[](int i) {
return data;
}
// ...
};
void doStuff(Base a, Base b) {
for (int i; i<size; ++i) {
a[i] += b[i];
}
}
template<typename A, typename B>
void doStuff(A & a, B & b) {
for (int i; i<size; ++i) {
a[i] += b[i];
}
}
void doStuff( Base & a, Base & b ) {
Local * a_local = dynamic_cast<Local*>(&a);
Global * a_global = dynamic_cast<Global*>(&a);
//same for b
if( a_local && b_local ) {
doStuffImpl(*a, *b); {
} else if( a_local && b_global ) {
doStuffImpl(*a, *b):
} ...
}
class Base {
public:
virtual void doStuff(Base& b) = 0;
};
class Local: public Base {
public:
virtual void doStuff(Base& b);
float data[size];
// plus constructors etc.
};
class Global: public Base {
public:
virtual void doStuff(Base& b);
float data;
// ...
};
void Local::doStuff(Base& b) {
Local* lb = NULL;
Global* gb = NULL;
if ( NULL != (lb = dynamic_cast<Local*>(&b)) )
{
// Do Local+Local stuff.
}
else if ( NULL != (gb = dynamic_cast<Global*>(&b)))
{
// Do Local+Global stuff.
}
}
void Global::doStuff(Base& b) {
Local* lb = NULL;
Global* gb = NULL;
if ( NULL != (lb = dynamic_cast<Local*>(&b)) )
{
// Do Global+Local stuff.
}
else if ( NULL != (gb = dynamic_cast<Global*>(&b)))
{
// Do Global+Global stuff.
}
}
void doStuff(Base a, Base b) {
a.doStuff(b);
}