簡體   English   中英

int vs 短向量化

[英]int vs short vectorization

我有以下內核向量化的整數數組:

    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];

這第二個內核不起作用,我不知道為什么。 我知道第一種和第二種情況下向量的所有條目都是相同的(也沒有溢出)。 有人可以幫我找出錯誤嗎?

謝謝

這是我看到的一些事情。

  1. intshort情況下,當您將__m128存儲到sumDot ,您在遠小於128 位的目標上使用_mm_storeu_si128 這意味着你一直在破壞記憶,幸運的是你沒有被咬。

    • 與此相關,因為sumDotint[1]即使在short情況下,您將兩個short存儲在一個int ,然后將其作為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使用_mm_madd_epi16在循環。 這僅需要兩個 32 位向量縮減階段。 http://pastebin.com/A9ibkMwP

  4. (可選)這是很好的風格,但使用_mm_setzero_*()函數而不是_mm_set1_*(0)沒有區別。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM