用于乘以特定数组元素的等效SIMD指令

时间:2015-08-18 22:01:01

标签: c simd code-translation

我刚刚了解了如何获得2个数组的点积(如下面的代码所示):

int A[8] = {1,2,3,4,5,1,2,3};
int B[8] = {2,3,4,5,6,2,3,4};

float result = 0;

for (int i = 0; i < 8; i ++) {
    result += A[i] * B[i];
}

等同于(在SIMD中):

int A[8] = {1,2,3,4,5,1,2,3};
int B[8] = {2,3,4,5,6,2,3,4};

float result = 0;

__m128 r1 = {0,0,0,0};
__m128 r2 = {0,0,0,0};
__m128 r3 = {0,0,0,0};

for (int i = 0; i < 8; i += 4) {
  float C[4] = {A[i], A[i+1], A[i+2], A[i+3]};
  float D[4] = {B[i], B[i+1], B[i+2], B[i+3]};
  __m128 a = _mm_loadu_ps(C);
  __m128 b = _mm_loadu_ps(D);

  r1 = _mm_mul_ps(a,b);
  r2 = _mm_hadd_ps(r1, r1);
  r3 = _mm_add_ss(_mm_hadd_ps(r2, r2), r3);
  _mm_store_ss(&result, r3);
}

我现在好奇如何在SIMD中获取等效代码,如果我想将数组中不连续的元素相乘。例如,如果我想执行以下操作,SIMD中的等效内容是什么?

int A[8] = {1,2,3,4,5,1,2,3};
int B[8] = {2,3,4,5,6,2,3,4};

float result = 0;
for (int i = 0; i < 8; i++) {
    for (int j = 0; j < 8; j++) {
        result += A[foo(i)] * B[foo(j)]
    }
}

foo只是一些返回int作为输入参数的函数的函数。

2 个答案:

答案 0 :(得分:1)

如果我必须完成这项任务,我会按如下方式进行:

int A[8] = {1,2,3,4,5,1,2,3};
int B[8] = {2,3,4,5,6,2,3,4};

float PA[8], PB[8];
for (int i = 0; i < 8; i++) 
{
    PA[i] = A[foo(i)]; 
    PB[i] = B[foo(i)]; 
}

__m128 sums = _mm_set1_ps(0);
for (int i = 0; i < 8; i++) 
{
    __m128 a = _mm_set1_ps(PA[i]);
    for (int j = 0; j < 8; j += 4) 
    {
        __m128 b = _mm_loadu_ps(PB + j);
        sums = _mm_add_ps(sums, _mm_mul_ps(a, b));
    }
}
float results[4];
_mm_storeu_ps(results, sums);
float result = results[0] + results[1] + results[2] + results[3];

答案 1 :(得分:0)

一般来说,SIMD 就像随机访问各个元素一样。但是,仍然有一些技巧可以使用。

如果在编译时已知提供的foo索引,则可以将两个向量混合以正确对齐它们的元素。只需看看swizzle category in the Intrinsics Guide中的内在函数。大多数情况下,您需要_mm_shuffle_ps_mm_unpackXX_ps之类的内容。此外,各种移位/对齐指令可能很有用。

使用AVX2,您可以尝试使用收集指令。对于32位模式的 float 类型,您可以 使用_mm_i32gather_ps_mm256_i32gather_ps内在函数。但是,@ PaulR写here它们并不比琐碎的标量载荷快。

SSSE3中的_mm_shuffle_epi8 instrinsic可能是另一种解决方案。这是一个很棒的指令,允许以单个字节的粒度执行寄存器内收集操作。但是,创建shuffle掩码不是一项简单的任务。 This paper(阅读第3.1和4节)展示了如何将此方法扩展到大于一个XMM寄存器的输入数组,但似乎对于64个或更多元素,它不再比标量代码更好。