【问题标题】:Fast multiplication of values in an array数组中值的快速乘法
【发布时间】:2011-04-10 05:00:00
【问题描述】:

有没有一种快速的方法可以在 C++ 中将浮点数组的值相乘,以优化此函数(其中count 是 4 的倍数):

void multiply(float* values, float factor, int count)
{
    for(int i=0; i < count; i++)
    {
        *value *= factor;
        value++;
    }
}

解决方案必须适用于 Mac OS X 和 Windows、英特尔和非英特尔。想想 SSE、矢量化、编译器(gcc 与 MSVC)。

【问题讨论】:

  • 你似乎已经知道答案了。您是否陷入了某种困境,或者您只是希望其他人为您编写代码?
  • 这不是 Rent-a-Coder!
  • 预期数组有多大(>1、>10、>100、>1000、>10000)?您考虑在您的情况下使用多核(线程)优化吗?除了 count 是 4 的倍数之外,是否事先知道有关数组的任何约束?
  • 或者可能是 OP 高估了开始使用 SSE 所需的工作!
  • 问题:float* values 是否与 16 字节对齐?如果是,则可以使用对齐的加载/存储指令,它会产生很好的速度差异。 (通常一个函数应该提供两个选项,并在运行时通过测试指针的偏移量来选择)

标签: c++ arrays optimization multiplication


【解决方案1】:

如果您希望您的代码是跨平台的,那么您要么必须编写与平台无关的代码,要么必须编写大量 #ifdefs。

您是否尝试过手动展开循环,看看是否有什么不同?

【讨论】:

    【解决方案2】:

    由于您知道count 是 4 的倍数,因此您可以展开循环...

    void multiply(float* values, float factor, int count)
    {
        count = count >> 2; // count / 4
        for(int i=0; i < count ; i++)
        {
            *value *= factor;
            *(value+1) *= factor;
            *(value+2) *= factor;
            *(value+3) *= factor;
            value += 4;
        }
    }
    

    【讨论】:

    • 这几乎肯定不会更快,因为它执行相同数量的乘法运算,但指针运算比原来的更复杂。我很想看到您的测量结果来支持这是一项改进。
    • GCC 使用-funroll-loops 完成此操作。
    • @Steve:这很可能会有所不同,这取决于编译器已经有多好(以及 CPU 的分支预测器有多好)。乘法与条件分支的比例从 1:1 上升到 4:1。
    • @Alex:GCC 不知道count 是 4 的倍数,所以这可能会稍微快一些。 可能倒计时到零 (for (int i = count/4; i != 0; --i)) 和递增每一行上的指针 (*value++ *= factor) 也会更快。
    • @Steve,我的版本时钟为0.731s,原始时钟为0.944s,数组大小为(4*10000000)...快20%。
    【解决方案3】:

    免责声明:显然,这不适用于 iPhone、iPad、Android 或它们未来的同类产品。

    #include <mmintrin.h>
    #include <xmmintrin.h>
    
    __m128 factor4 = _mm_set1_ps(factor);
    for (int i=0; i+3 < count; i += 4)
    {
       __m128 data = _mm_mul_ps(_mm_loadu_ps(values), factor4);
       _mm_storeu_ps(values, data);
       values += 4;
    }
    for (int i=(count/4)*4; i < count; i++)
    {
       *values *= factor;
       value++;
    }
    

    【讨论】:

      【解决方案4】:

      你想过 OpenMP 吗?

      大多数现代计算机都有多核 CPU,几乎每个主要编译器似乎都内置了 OpenMP。您几乎不惜任何代价获得速度。

      Wikipedia's article on OpenMP

      【讨论】:

        【解决方案5】:

        最好的解决方案是保持简单,让编译器为您优化。 GCC 知道 SSE、SSE2、altivec 和其他什么。 如果您的代码过于复杂,您的编译器将无法针对每个可能的目标对其进行优化。

        【讨论】:

          【解决方案6】:

          正如您所提到的,有许多架构都具有 SIMD 扩展,而 SIMD 可能是您进行优化的最佳选择。然而,它们都是特定于平台的,并且作为语言的 C 和 C++ 对 SIMD 不友好。

          但是,您应该尝试的第一件事是为给定的构建启用 SIMD 特定标志。编译器可以识别可以使用 SIMD 优化的模式。

          接下来是使用编译器内在函数或适当的程序集编写特定于平台的 SIMD 代码。但是,您应该为没有优化版本的平台保留可移植的非 SIMD 实现。 #ifdefs 在支持 SIMD 的平台上启用它。

          最后,至少在 ARM 上,但在 Intel 上不确定,请注意,较小的整数和浮点类型允许每条 SIMD 指令进行更多的并行操作。

          【讨论】:

            【解决方案7】:

            我认为,你可以做的事情并没有什么大不了的。也许您可以使用 OpenMP 或 SSE 加快速度。但是现代 CPU 已经相当快了。在某些应用程序中,内存带宽/延迟实际上是瓶颈,而且会变得更糟。我们已经有了三级缓存,需要智能预取算法来避免巨大的延迟。因此,考虑内存访问模式也是有意义的。例如,如果您实现这样的multiplyadd 并像这样使用它:

            void multiply(float vec[], float factor, int size)
            {
              for (int i=0; i<size; ++i)
                vec[i] *= factor;
            }
            
            void add(float vec[], float summand, int size)
            {
              for (int i=0; i<size; ++i)
                vec[i] += summand;
            }
            
            void foo(float vec[], int size)
            {
              multiply(vec,2.f,size);
              add(vec,9.f,size);
            }
            

            您基本上在内存块上传递了两次。根据向量的大小,它可能不适合 L1 缓存,在这种情况下,通过它两次会增加一些额外的时间。这显然很糟糕,您应该尝试将内存访问保持为“本地”。在这种情况下,单个循环

            void foo(float vec[], int size)
            {
              for (int i=0; i<size; ++i) {
                vec[i] = vec[i]*2+9;
              }
            }
            

            可能会更快。根据经验:尝试线性访问内存并尝试“本地”访问内存,我的意思是,尝试重用已经在 L1 缓存中的数据。只是一个想法。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-08-12
              • 2013-11-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-12-03
              • 2017-01-25
              相关资源
              最近更新 更多