6

有没有一种快速的方法可以在 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)。

4

7 回答 7

2

免责声明:显然,这不适用于 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++;
}
于 2010-09-09T11:29:07.797 回答
2

如果您希望您的代码是跨平台的,那么您将不得不编写与平台无关的代码,或者您将不得不编写大量的#ifdefs。

您是否尝试过一些手动循环展开,并查看它是否有任何不同?

于 2010-09-09T11:21:07.793 回答
2

你有想过 OpenMP 吗?

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

请参阅Wikipedia 关于 OpenMP 的文章

于 2010-09-09T11:32:44.637 回答
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;
    }
}
于 2010-09-09T11:24:19.130 回答
0

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

于 2010-09-09T11:35:56.590 回答
0

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

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

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

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

于 2010-09-09T13:27:33.503 回答
0

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

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 缓存中的数据。只是一个想法。

于 2010-09-09T13:46:34.037 回答