我正在编写一个C++数字运算应用程序,其中瓶颈是一个必须计算为double的函数:
template<class T> inline T sqr(const T& x){return x*x;}
Run Code Online (Sandbox Code Playgroud)
和另一个计算
Base dist2(const Point& p) const
{ return sqr(x-p.x) + sqr(y-p.y) + sqr(z-p.z); }
Run Code Online (Sandbox Code Playgroud)
这些操作占用了80%的计算时间.我想知道你是否可以建议让它更快的方法,即使有某种准确性损失
谢谢
gue*_*ser 14
首先,确保dist2可以内联(从你的帖子中不清楚是否是这种情况),如果有必要在头文件中定义它(通常你需要这样做 - 但是如果你的编译器在链接时间,那不一定是这种情况).
假设x86架构,请确保允许编译器使用SSE2指令(SIMD指令集的示例)生成代码(如果它们在目标体系结构上可用).为了给编译器提供优化这些优化的最佳机会,您可以尝试将sqr操作一起批处理(SSE2指令一次最多可以执行4次浮点运算或2次双操作,具体取决于指令..但当然它可以只有在准备好的多个操作的输入时才执行此操作.我不会过于乐观地认为编译器能够确定它可以批量处理它们.但是你至少可以设置你的代码以便在理论上可行.
如果你仍然对速度不满意并且你不相信你的编译器做得最好,你应该考虑使用编译器内在函数,这将允许你明确地编写潜在的并行指令..或者你可以正确提前编写特定于体系结构的汇编代码,以利用SSE2或最适合您架构的指令.(警告:如果您手动编写程序集,要么特别注意它仍然被内联,或者进入大批量操作)
为了更进一步,(并且就像glowcoder已经提到的那样),您可以在GPU上执行这些操作.对于您的具体情况,请记住GPU通常不支持双精度浮点数.虽然如果它非常适合您正在做的事情,您将以这种方式获得更好的性能.谷歌GPGPU或诸如此类的东西,看看什么是最适合你的.
Ste*_*hen 10
什么是Base
?
它是一个非显式构造函数的类吗?您可能正在创建大量临时Base
对象.这可能是一个很大的CPU.
template<class T> inline T sqr(const T& x){return x*x;}
Base dist2(const Point& p) const {
return sqr(x-p.x) + sqr(y-p.y) + sqr(z-p.z);
}
Run Code Online (Sandbox Code Playgroud)
如果p
成员变量属于类型Base
,则可以调用sqr
Base对象,这将为减去的坐标创建临时对象,sqr
然后为每个添加的组件创建临时对象.
(没有类定义,我们无法分辨)
您可以通过强制将sqr调用置于原始状态而不是使用Base
直到达到返回类型来加速它dist2
.
其他绩效改进机会是:
dist2
太多的算法,可能是缓存或使用传递属性.您没有说是否dist2
可以并行调用.如果可以的话,那么你可以构建一个线程池,并将每个线程的工作分成更小的块.
你的探查者告诉你什么在里面发生dist2
.您是否实际上一直使用100%的CPU,或者您是否缓存丢失并等待数据加载?
说实话,我们真的需要更多细节来给你一个明确的答案.
如果您有许多这样做,并且您正在进行图形或"图形化"任务(热建模,几乎任何3D建模),您可以考虑使用OpenGL并将任务卸载到GPU.这将允许计算并行运行,具有高度优化的运行能力.毕竟,你会期望像distance或distanceq这样的东西在GPU上拥有自己的操作码.
一位当地大学的研究人员将几乎所有用于AI工作的3d计算工作卸载到GPU上,并取得了更快的结果.
有很多答案提到了 SSE 已经 \xe2\x80\xa6 但由于没有人提到如何使用它,我将在 \xe2\x80\xa6 中抛出另一个
\n\n您的代码拥有矢量化器工作所需的几乎所有内容,除了两个限制:别名和对齐。
\n\n别名是指两个名称引用两个同一对象的问题。例如,my_point.dist2( my_point )
将在 的两个副本上进行操作my_point
。这会扰乱矢量化器。
C99 定义了指针的关键字来指定所引用的对象是唯一引用的:当前作用域中restrict
不会有其他指针指向该对象。restrict
大多数不错的 C++ 编译器也实现 C99,并以某种方式导入此功能。
__restrict__
. 它可以应用于参考文献或this
。__restrict
. 如果支持与 GCC 有任何不同,我会感到惊讶。(不过,它不在 C++0x 中。)
\n\n#ifdef __GCC__\n#define restrict __restrict__\n#elif defined _MSC_VER\n#define restrict __restrict\n#endif\n\xc2\xa0\nBase dist2(const Point& restrict p) const restrict\n
Run Code Online (Sandbox Code Playgroud)大多数 SIMD 单元需要与向量的大小对齐。C++ 和 C99 保留对齐实现定义,但 C++0x 通过引入[[align(16)]]
. 由于这还需要一段时间,您可能需要编译器的半便携式支持,例如restrict
:
#ifdef __GCC__\n#define align16 __attribute__((aligned (16)))\n#elif defined _MSC_VER\n#define align16 __declspec(align (16))\n#endif\n\xc2\xa0\nstruct Point {\n double align16 xyz[ 3 ]; // separate x,y,z might work; dunno\n \xe2\x80\xa6\n};\n
Run Code Online (Sandbox Code Playgroud)这并不能保证产生结果;GCC 和 MSVC 都实施了有用的反馈,告诉您哪些内容没有矢量化以及原因。谷歌你的矢量化器以了解更多信息。
\n