数组C[]=A[]*B[],用于高性能计算
Array C[]=A[]*B[] in high-performance calculation
我相信在C++中有这样的代码是很常见的
for(size_t i=0;i<ARRAY_SIZE;++i)
A[i]=B[i]*C[i];
一个通常提倡的替代方案是:
double* pA=A,pB=B,pC=C;
for(size_t i=0;i<ARRAY_SIZE;++i)
*pA++=(*pB++)*(*pC++);
我想知道的是,改进该代码的最佳方式,因为IMO需要考虑以下事项:
- CPU缓存。CPU如何填充缓存以获得最佳命中率
- 我想苏格兰和南方能源公司可以改善这一点吗
- 另一件事是,如果代码可以并行化呢?例如,使用OpenMP。在这种情况下,指针技巧可能不可用
如有任何建议,我们将不胜感激!
My g++4.5.2为两个循环生成完全相同的代码(修复了double *pA=A, *pB=B, *pC=C;
中的错误,它是
.L3:
movapd B(%rax), %xmm0
mulpd C(%rax), %xmm0
movapd %xmm0, A(%rax)
addq $16, %rax
cmpq $80000, %rax
jne .L3
(我的ARRAY_SIZE是10000)
编译器的作者已经知道这些技巧了。不过,OpenMP和其他并发解决方案值得研究。
性能规则是
-
尚未
-
获取目标
-
测量
-
了解有多少改进是可能的,并验证花时间来实现它是值得的。
对于现代处理器来说更是如此。关于您的问题:
-
简单的索引到指针映射通常由编译器完成,如果他们不这样做,他们可能有充分的理由。
-
处理器通常已经针对顺序访问缓存进行了优化:简单的代码生成通常会提供最佳性能。
-
苏格兰和南方能源公司或许可以改善这一点。但如果您已经受到带宽限制,则不会这样做。因此,我们回到的测量和确定边界阶段
-
并行化:和SSE一样。如果您的带宽有限,那么使用单个处理器的多个核心将没有帮助。根据存储器体系结构的不同,使用不同的处理器可能会有所帮助。
-
手动循环展开(在现已删除的答案中建议)通常是个坏主意。编译器知道如何在有价值的时候做到这一点(例如,如果它可以进行软件管道),而对于现代OOO处理器,情况往往并非如此(它增加了指令和跟踪缓存的压力,而OOO执行、对跳跃的猜测和寄存器重命名将自动带来展开和软件管道的大部分好处)。
第一种形式正是编译器将识别和优化的那种结构,几乎可以肯定会自动发出SSE指令。
对于这种琐碎的内部循环,缓存效果是无关紧要的,因为您正在遍历所有内容。如果您有嵌套循环或一系列操作(如g(f(a,B),C)),那么您可能会尝试安排重复访问小块内存,以更方便缓存。
不要用手展开循环。如果这是一个好主意(可能不是在现代CPU上),您的编译器也会这样做。
如果循环很大,并且其中的操作足够复杂,以至于您还没有内存绑定,那么OpenMP可能会有所帮助。
一般来说,以自然而直接的方式编写代码,因为这是优化编译器最容易理解的。
何时开始考虑SSE或OpenMP?如果这两者都是真的:
- 如果您发现与您的代码相似的代码在您的项目中出现20次或更多:
for (size_t i = 0; i < ARRAY_SIZE; ++i)
A[i] = B[i] * C[i];
或一些类似的操作 - 如果
ARRAY_SIZE
通常大于1000万,或者如果profiler告诉您此操作正在成为瓶颈
然后,
- 首先,将其变成一个函数:
void array_mul(double* pa, const double* pb, const double* pc, size_t count)
{ for (...) }
- 其次,如果您能够找到合适的SIMD库,请更改函数以使用它。
- 良好的便携式SIMD库
- SIMD C++库
顺便说一句,如果你有很多操作只比这稍微复杂一点,例如A[i] = B[i] * C[i] + D[i]
,那么支持表达式模板的库也会很有用。
您可以使用一些简单的并行化方法。Cuda将依赖于硬件,但SSE几乎是每个CPU的标准配置。还可以使用多个线程。在多线程中,您仍然可以使用指针技巧,这不是很重要。编译器也可以完成这些简单的优化。如果您使用的是Visual Studio 2010,则可以使用parallel_invoke并行执行函数,而无需处理windows线程。在Linux中,pThread库非常容易使用。
我认为使用valarrays是专门用于此类计算的。我不确定它是否能提高性能。
- 1d 智能指针不适用于语法 (*)++
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 用于访问容器<T>数据成员的正确 API
- 重载操作程序时出错>>用于类中的字符串 memebr
- 如何防止 c++ 在从浮点型转换为双精度型(不适用于 IO)时添加额外的小数?
- C++中的cin.ignore()函数不适用于整个流
- 没有用于初始化C++中的变量模板的匹配构造函数
- 用于C++中带有数组和指针的循环
- 为什么它不适用于Visual 2019的原因
- 使用在用于SFINAE的void_t中具有参数的方法
- 在createdialog创建的窗口中捕获用于编辑控件的OnMouseMove消息
- 重载==不适用于二进制树
- Insert函数不适用于2 if语句C++
- 用于矢量处理的多个线程
- 使外部项目可用于find_package CMake
- 用于高性能加法和乘法的常量形式
- ACE (c++库)还用于高性能计算吗?
- 用于高性能计算的c++ std::vector
- 用于结构化对象的高性能装饰器模式
- 数组C[]=A[]*B[],用于高性能计算