c - int 与短向量化

标签 c performance sse

我有以下针对整数数组的内核矢量化:

    long valor = 0, i=0;

    __m128i vsum, vecPi, vecCi, vecQCi;

    vsum = _mm_set1_epi32(0);

    int32_t * const pA = A->data;
    int32_t * const pB = B->data;

    int sumDot[1];

    for( ; i<SIZE-3 ;i+=4){
            vecPi = _mm_loadu_si128((__m128i *)&(pA)[i] );
            vecCi = _mm_loadu_si128((__m128i *)&(pB)[i] );
            vecQCi = _mm_mullo_epi32(vecPi,vecCi);
            vsum = _mm_add_epi32(vsum,vecQCi);
    }

    vsum = _mm_hadd_epi32(vsum, vsum);
    vsum = _mm_hadd_epi32(vsum, vsum);
    _mm_storeu_si128((__m128i *)&(sumDot), vsum);

    for( ; i<SIZE; i++)
          valor += A->data[i] * B->data[i];

    valor += sumDot[0];

而且效果很好。但是,如果我将 A 和 B 的数据类型更改为 short 而不是 int,我是否应该使用以下代码:

    long valor = 0, i=0;

    __m128i vsum, vecPi, vecCi, vecQCi;

    vsum = _mm_set1_epi16(0);

    int16_t * const pA = A->data;
    int16_t * const pB = B->data;

    int sumDot[1];

    for( ; i<SIZE-7 ;i+=8){
            vecPi = _mm_loadu_si128((__m128i *)&(pA)[i] );
            vecCi = _mm_loadu_si128((__m128i *)&(pB)[i] );
            vecQCi = _mm_mullo_epi16(vecPi,vecCi);
            vsum = _mm_add_epi16(vsum,vecQCi);
    }

    vsum = _mm_hadd_epi16(vsum, vsum);
    vsum = _mm_hadd_epi16(vsum, vsum);
    _mm_storeu_si128((__m128i *)&(sumDot), vsum);

    for( ; i<SIZE; i++)
          valor += A->data[i] * B->data[i];

    valor += sumDot[0];

第二个内核无法工作,我不知道为什么。我知道第一种情况和第二种情况中 vector 的所有条目都是相同的(也没有溢出)。有人可以帮我找出错误吗?

谢谢

最佳答案

这是我看到的一些东西。

  1. int 中和short情况下,当您存储 __m128 时至sumDot ,您使用_mm_storeu_si128远小于 128 位的目标上。这意味着你的内存被破坏了,很幸运你没有被咬。

    • 与此相关,因为 sumDotint[1]即使在 short情况下,您存储了两个 short合二为一int ,然后将其读取为 int .
  2. short如果您缺少一个水平 vector 缩减步骤。请记住,现在您已经有了 8 short每个 vector s,您现在必须有 log_2(8) = 3 个 vector 缩减步骤。

    vsum = _mm_hadd_epi16(vsum, vsum);
    vsum = _mm_hadd_epi16(vsum, vsum);
    vsum = _mm_hadd_epi16(vsum, vsum);
    
  3. (可选)既然您已经开始使用 SSE4.1,不妨使用它所具有的优点之一: PEXTR*指示。他们获取要从中提取的车道的索引。您对底部泳道(泳道 0)感兴趣,因为这是 vector 缩减后总和的最终位置。 <罢工>

    <罢工>
    /* 32-bit */
    sumDot[0] = _mm_extract_epi32(vsum, 0);
    /* 16-bit */
    sumDot[0] = _mm_extract_epi16(vsum, 0);
    

    <罢工> 编辑:显然编译器没有对用 _mm_extract_epi16 提取的 16 位字进行符号扩展。您必须亲自说服它这样做。

    /* 32-bit */
    sumDot[0] = (int32_t)_mm_extract_epi32(vsum, 0);
    /* 16-bit */
    sumDot[0] = (int16_t)_mm_extract_epi16(vsum, 0);
    

    EDIT2:我找到了一个更好的解决方案!它完全使用我们需要的指令 ( PMADDWD ),并且与 32 位代码相同,只是迭代边界不同,而不是 _mm_mullo_epi16你使用_mm_madd_epi16在循环。这仅需要两个 32 位 vector 缩减阶段。 http://pastebin.com/A9ibkMwP

  4. (可选)这是很好的风格,但使用 _mm_setzero_*() 没有什么区别。函数而不是 _mm_set1_*(0) .

关于c - int 与短向量化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22585306/

相关文章:

assembly - 有没有办法在 x86 上使用 MMX/SSE 减去压缩的无符号双字,饱和?

c - 如何在C中的文本文件中读取由 ','分隔的数据

c - 如何在 Linux 内核模块中的用户空间中创建目录

python - 绘制 python 脚本性能图

postgresql - 为什么 postgresql 写入巨大的临时文件并在循环中填满我的磁盘?

c++ - 如何加快积分图像的计算?

c - 简化 if 语句的算法

c - 如果名称在 C 文件中不可用,如何显示错误消息?

performance - Scala 动态类管理

c - 矩阵乘法的自动向量化