在高性能计算中,数组C [] = A [] * B []

时间:2011-06-08 05:00:38

标签: c++ arrays performance

我认为通常在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需要考虑以下事项:

  1. CPU缓存。 CPU如何填充缓存以获得最佳命中率?
  2. 我想SSE可以改善这个吗?
  3. 另一件事是,如果代码可以并行化怎么办?例如。使用OpenMP。在这种情况下,可能无法使用指针技巧。
  4. 任何建议都将不胜感激!

6 个答案:

答案 0 :(得分:5)

我的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和其他并发解决方案值得研究。

答案 1 :(得分:4)

性能规则是

  1. 尚未

  2. 获得目标

  3. 测量

  4. 了解可能有多少改进,并确认花时间去获取它是值得的。

  5. 现代处理器更是如此。关于你的问题:

    1. 指针映射的简单索引通常由编译器完成,当他们不这样做时,他们可能有充分的理由。

    2. 处理器通常已经过优化,可以顺序访问缓存:简单的代码生成通常可以提供最佳性能。

    3. SSE也许可以改善这一点。但如果你的带宽有限,那就不行了。所以我们回到测量并确定边界阶段

    4. 并行化:与SSE相同。如果带宽有限,使用单个处理器的多个内核将无济于事。根据内存架构,使用不同的处理器可能有所帮助。

    5. 手动循环展开(在现在删除的答案中建议)通常是一个坏主意。编译器知道如何在价值方面做到这一点(例如,如果它可以进行软件流水线操作),而现代OOO处理器通常不是这样(它增加了指令和跟踪缓存的压力,同时执行OOO,猜测跳转和注册重命名将自动带来展开和软件流水线的大部分好处。

答案 2 :(得分:2)

第一种形式正是编译器将识别和优化的结构,几乎肯定会自动发出SSE指令。

对于这种琐碎的内部循环,缓存效果是无关紧要的,因为你正在遍历所有内容。如果您有嵌套循环或一系列操作(如g(f(A,B),C)),那么您可能会尝试重复访问小块内存以更加缓存。

手动展开循环。如果这是一个好主意(它可能不在现代CPU上),你的编译器也会这样做。

如果循环很大并且内部的操作足够复杂,以至于你还没有内存限制,那么OpenMP可能会有所帮助。

通常,以自然而直接的方式编写代码,因为这是优化编译器最有可能理解的内容。

答案 3 :(得分:1)

何时开始考虑SSE或OpenMP?如果这两个都是真的:

  • 如果您发现类似于您的代码在您的项目中出现20次或更多次:
    for (size_t i = 0; i < ARRAY_SIZE; ++i) {
    {1}}
    或者一些类似的操作
  • 如果A[i] = B[i] * C[i];通常大于1000万,或者,如果探查者告诉您此操作正在成为瓶颈

然后,

  • 首先,将其变为功能:
    ARRAY_SIZE
    void array_mul(double* pa, const double* pb, const double* pc, size_t count)
  • 其次,如果您能找到合适的SIMD库,请更改您的功能以使用它。

作为旁注,如果你有很多操作只比这稍微复杂一点,例如{ for (...) }然后支持expression template的图书馆也会有用。

答案 4 :(得分:0)

您可以使用一些简单的并行化方法。 Cuda将依赖于硬件,但SSE几乎是每个CPU的标准配置。您也可以使用多个线程。在多个线程中,您仍然可以使用指针技巧,这不是很重要。这些简单的优化也可以由编译器完成。如果您使用的是Visual Studio 2010,则可以使用parallel_invoke并行执行函数,而无需处理Windows线程。在Linux中,pThread库非常易于使用。

答案 5 :(得分:0)

我认为使用valarray专门用于此类计算。我不确定它是否会改善性能。