【发布时间】:2014-04-03 23:37:34
【问题描述】:
我正在用 C++ 编写一些数值模拟代码。在这个模拟中,有些东西是“局部的”,在二维网格上的每个点都有一个浮点值,而另一些是“全局的”,只有一个全局浮点值。
除了这种差异之外,这两种类型的对象的行为相似,因此我希望能够拥有一个包含这两种类型的对象的数组。但是,因为这是一个数值模拟,所以我需要以以下方式执行此操作:(a) 尽可能避免虚函数调用开销,并且 (b) 允许编译器尽可能多地使用优化 - 特别是,允许编译器在可能的情况下进行 SIMD 自动矢量化。
目前我发现自己正在编写这样的代码(我现在意识到,这实际上不会按预期工作):
class Base {};
class Local: public Base {
public:
float data[size];
// plus constructors etc.
};
class Global: public Base {
public:
float data;
// ...
};
void doStuff(Local a, Local b) {
for (int i; i<size; ++i) {
a.data[i] += b.data[i];
}
}
void doStuff(Local a, Global b) {
for (int i; i<size; ++i) {
a.data[i] += b.data;
}
}
void doStuff(Global a, Local b) {
for (int i; i<size; ++i) {
a.data += b.data[i];
}
}
void doStuff(Global a, Global b) {
a.data += b.data*size;
}
我的代码比这复杂一点——数组是二维的,有几个doStuff 类型的函数有三个而不是两个参数,所以我必须为每个函数编写八个特化。
这不能按预期工作的原因是doStuff 的参数类型在编译时实际上并不知道。我想要做的是拥有一个Base * 数组并在其两个成员上调用doStuff。然后,我希望针对其参数的特定类型调用 doStuff 的正确专业化。 (doStuff 中是否涉及虚拟方法调用并不重要——我只是想在内部循环中避免它们。)
这样做而不是(例如)重载operator[] 的重点是编译器可以(希望)对doStuff(Local, Local) 和doStuff(Local, Global) 进行SIMD 自动矢量化,我可以完全失去循环doStuff(Global, Global)。也许在这些函数中也可能发生其他编译器优化。
但是,不得不编写这样重复的代码很烦人。因此,我想知道是否有办法使用模板来实现这一点,这样我就可以只编写一个函数doStuff(Base, Base) 并生成与上述等效的代码。 (我希望 gcc 足够聪明,可以在 doStuff(Global, Global) 的情况下优化掉循环。)
我强调以下解决方案不是我正在寻找的,因为它涉及在循环的每次迭代中调用虚函数,这会增加开销并且可能会阻止许多编译器优化.
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];
}
}
我想实现与上述类似的效果,但没有通过内部循环在每次迭代时调用虚函数的开销。 (除非我完全错了,编译器实际上可以优化掉所有的虚函数调用并生成类似上面的代码。在这种情况下,告诉我这个可以为我节省很多时间!)
我确实看过CRTP,但由于doStuff 的多个重载参数,如何使其适应这种情况并不明显,至少对我来说不是。
【问题讨论】:
-
对我来说听起来像是模板的工作。
-
@EJP 我也是!这就是为什么我在问题中这么说的原因。但对我来说,如何使用模板来做到这一点一点都不明显。
标签: c++ templates optimization