【问题标题】:How to use intrinsics to elementwise multiply two char arrays and sum up the multiplications into int?如何使用内在函数将两个 char 数组元素相乘并将乘法相加为 int?
【发布时间】:2017-02-26 09:49:09
【问题描述】:

我不熟悉 x86_64 内在函数,我想使用 256 位向量寄存器进行以下操作。 我正在使用 _mm256_maddubs_epi16(a, b);但是,该指令似乎存在溢出问题,因为 char*char 可以超过 16 位最大值。我对 _mm256_unpackhi_epi32 和相关说明有问题。

谁能详细说明我并告诉我通往目的地的光?谢谢!

int sumup_char_arrays(char *A, char *B, int size) {
    assert (size % 32 == 0);
    int sum = 0;
    for (int i = 0; i < size; i++) {
        sum += A[i]*B[i];
    }
    return sum;
}

【问题讨论】:

  • 我建议你先写一个SSE版本,e.g.解压到 16 位,然后使用 _mm_madd_epi16 进行繁重的工作。对于初学者来说,这可能是一个足够的挑战,没有 AVX 上所有繁琐的分道问题。如果您觉得需要,您可以随时从 SSE 转到 AVX。

标签: c++ algorithm x86-64 simd intrinsics


【解决方案1】:

我已经找到了解决方案,任何改进它的想法,尤其是减少的最后阶段。

int sumup_char_arrays(char *A, char *B, int size) {
    assert (size % 32 == 0);
    int sum = 0;
    __m256i sum_tmp;
    for (int i = 0; i < size; i += 32) {
        __m256i ma_l = _mm256_cvtepi8_epi16(_mm_load_si128((__m128i*)A));
        __m256i ma_h = _mm256_cvtepi8_epi16(_mm_load_si128((__m128i*)(A+16)));
        __m256i mb_l = _mm256_cvtepi8_epi16(_mm_load_si128((__m128i*)B));
        __m256i mb_h = _mm256_cvtepi8_epi16(_mm_load_si128((__m128i*)(B+16)));
        __m256i mc = _mm256_madd_epi16(ma_l, mb_l);
        mc = _mm256_add_epi32(mc, _mm256_madd_epi16(ma_h, mb_h));
        sum_tmp = _mm256_add_epi32(mc, sum_tmp);
        //sum += A[i]*B[i];
    }
    sum_tmp = _mm256_add_epi32(sum_tmp, _mm256_permute2x128_si256(sum_tmp, sum_tmp, 0x81));
    sum_tmp = _mm256_add_epi32(sum_tmp, _mm256_srli_si256(sum_tmp, 8));
    sum_tmp = _mm256_add_epi32(sum_tmp, _mm256_srli_si256(sum_tmp, 4));        
    sum = _mm256_extract_epi32(sum_tmp, 0);
    return sum;
}

【讨论】:

  • 对我来说看起来不错,除非您的 char 数组之一可以被视为无符号,因此您可以使用 PMADDUBSW。水平减少不需要置换,只需提取并降低到 128。请参阅 this answer 了解水平和的可能最佳模式,与您的相比,可能会节省几个代码字节。
猜你喜欢
  • 1970-01-01
  • 2021-08-31
  • 1970-01-01
  • 2013-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多