Внутреннее произведение двух 16-битных целочисленных векторов с AVX2 в C ++ - PullRequest
0 голосов
/ 27 мая 2020

Я ищу наиболее эффективный способ умножить два выровненных массива int16_t, длину которых можно разделить на 16 с помощью AVX2.

После умножения на вектор x Я начал с _mm256_extracti128_si256 и _mm256_castsi256_si128 чтобы иметь младшую и высокую часть x и добавить их с помощью _mm_add_epi16.

Я скопировал регистр результатов и применил _mm_move_epi64 к исходному регистру и снова добавил оба с _mm_add_epi16. Теперь я думаю, что у меня есть:
-, -, -, -, x15+x7+x11+x3, x14+x6+x10+x2, x13+x5+x9+x1, x12+x4+x8+x0 в 128-битном регистре. Но теперь я застрял и не знаю, как эффективно суммировать оставшиеся четыре записи и как извлечь 16-битный результат.

1 Ответ

1 голос
/ 04 июня 2020

Следуя комментариям и часам Google, мое рабочее решение:

// AVX multiply
hash = 1;
start1 = std::chrono::high_resolution_clock::now();
for(int i=0; i<2000000; i++) {
    ZTYPE*  xv = al_entr1.c.data();
    ZTYPE*  yv = al_entr2.c.data();

    __m256i tres = _mm256_setzero_si256();
    for(int ii=0; ii < MAX_SIEVING_DIM; ii = ii+16/*8*/)
    {
        // editor's note: alignment required.  Use loadu for unaligned
        __m256i  xr = _mm256_load_si256((__m256i*)(xv+ii));
        __m256i  yr = _mm256_load_si256((__m256i*)(yv+ii));
        const __m256i tmp = _mm256_madd_epi16 (xr, yr);
        tres =  _mm256_add_epi32(tmp, tres);
    }

    // Reduction
    const __m128i x128 = _mm_add_epi32  ( _mm256_extracti128_si256(tres, 1), _mm256_castsi256_si128(tres));
    const __m128i x128_up = _mm_shuffle_epi32(x128, 78);
    const __m128i x64  = _mm_add_epi32  (x128, x128_up);
    const __m128i _x32 =  _mm_hadd_epi32(x64, x64);

    const int res = _mm_extract_epi32(_x32, 0);
    hash |= res;
}

finish1 = std::chrono::high_resolution_clock::now();
elapsed1 = finish1 - start1;
std::cout << "AVX multiply: " <<elapsed1.count() << " sec. (" << hash << ")" << std::endl;

Это, по крайней мере, самое быстрое решение на данный момент:

  • std :: inner_product: 0.819781 se c. (-14335)
  • std :: inner_product (выровнено): 0.964058 se c. (-14335)
  • наивное умножение: 0,588623 se c. (-14335)
  • Развернуть умножение: 0.505639 se c. (-14335)
  • умножение AVX: 0,0488352 se c. (-14335)
...