int与短矢量化

时间:2014-03-23 00:01:29

标签: 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的数据类型更改为而不是 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];

这第二个内核不起作用,我不知道为什么。我知道第一种和第二种情况下矢量的所有条目都是相同的(也没有溢出)。有人能帮助我找到错误吗?

由于

1 个答案:

答案 0 :(得分:2)

这是我看到的一些事情。

  1. intshort这两种情况下,当您将__m128存储到sumDot时,您对目标使用_mm_storeu_si128小于而不是128位。这意味着你一直在腐蚀记忆,幸运的是你没有被咬伤。

    • 与此相关,因为sumDot即使在int[1]案例中也是short,您将两个 short存储在一个int中{1}},然后将其作为int
    • 阅读
  2. short案例中,您错过了一个水平向量缩减步骤。请记住,既然每个向量有8个short s,那么现在必须有log_2(8)= 3个向量缩减步骤。

    vsum = _mm_hadd_epi16(vsum, vsum);
    vsum = _mm_hadd_epi16(vsum, vsum);
    vsum = _mm_hadd_epi16(vsum, vsum);
    
  3. (可选)由于您已经使用过SSE4.1,不妨使用其中的一个好东西:PEXTR*指令。它们取得从中提取的车道的索引。你对底线(第0道)感兴趣,因为这是你的矢量减少后总和结束的地方。  <击>

    <击>
    /* 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你使用{ {1}}在循环中。这只需要两个32位向量缩减级。 http://pastebin.com/A9ibkMwP

  4. (可选)这是一种很好的风格,但使用_mm_madd_epi16函数代替_mm_setzero_*()无效。