Целочисленное умножение AVX2 на 8-битные элементы со знаком и получение 16-битных результатов со знаком? - PullRequest
0 голосов
/ 17 сентября 2018

У меня есть два вектора __m256i, заполненные 32 8-битными целыми числами.Примерно так:

    __int8 *a0 = new __int8[32] {2};
    __int8 *a1 = new __int8[32] {3};

    __m256i v0 = _mm256_loadu_si256((__m256i*)a0);
    __m256i v1 = _mm256_loadu_si256((__m256i*)a1);

Как мне умножить эти векторы, используя что-то вроде _mm256_mul_epi8(v0, v1) (которого не существует) или любым другим способом?

Я хочу 2 вектора результатов,потому что ширина выходного элемента в два раза больше ширины входного элемента.Или что-то, что работает аналогично _mm_mul_epu32, будет в порядке, используя только четные элементы ввода (0, 2, 4 и т. Д.)

1 Ответ

0 голосов
/ 23 сентября 2018

Вы хотите, чтобы результат был разделен на два вектора, так что это мое предложение для вашего вопроса.Я пытался быть понятным, простым и осуществимым:

#include <stdio.h>
#include <x86intrin.h>
 void _mm256_print_epi8(__m256i );
 void _mm256_print_epi16(__m256i );
 void _mm256_mul_epi8(__m256i , __m256i , __m256i* , __m256i* );


int main()
{
    char a0[32] = {1, 2, 3, -4, 5, 6, 7, 8, 9, -10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, -24, 25, 26, 27, 28, 29, 30, 31, 32};
    char a1[32] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -13, 14, 15, 16, 17, 18, 19, -20, 21, 22, 23, 24, -25, 26, 27, 28, 29, 30, 31, 32, 33};

    __m256i v0 = _mm256_loadu_si256((__m256i*) &a0[0]);
    __m256i v1 = _mm256_loadu_si256((__m256i*) &a1[0]);

    __m256i r0, r1;//for 16 bit results

    _mm256_mul_epi8(v0, v1, &r0, &r1);

    printf("\nv0 = ");_mm256_print_epi8(v0);
    printf("\nv1 = ");_mm256_print_epi8(v1);
    printf("\nr0 = ");_mm256_print_epi16(r0);
    printf("\nr1 = ");_mm256_print_epi16(r1);
    printf("\nfinished\n");


    return 0;
}
//v0 and v1 are 8 bit input vectors. r0 and r1 are 18 bit results of multiplications
 void _mm256_mul_epi8(__m256i v0, __m256i v1, __m256i* r0, __m256i* r1)
{
    __m256i tmp0, tmp1;
    __m128i m128_v0, m128_v1;

    m128_v0 = _mm256_extractf128_si256 (v0, 0);
    m128_v1 = _mm256_extractf128_si256 (v1, 0);

    tmp0= _mm256_cvtepi8_epi16 (m128_v0); //printf("\ntmp0 = ");_mm256_print_epi16(tmp0);
    tmp1= _mm256_cvtepi8_epi16 (m128_v1); //printf("\ntmp1 = ");_mm256_print_epi16(tmp1);


    *r0 =_mm256_mullo_epi16(tmp0, tmp1);

    m128_v0 = _mm256_extractf128_si256 (v0, 1);
    m128_v1 = _mm256_extractf128_si256 (v1, 1);

    tmp0= _mm256_cvtepi8_epi16 (m128_v0); //printf("\ntmp0 = ");_mm256_print_epi16(tmp0);
    tmp1= _mm256_cvtepi8_epi16 (m128_v1); //printf("\ntmp1 = ");_mm256_print_epi16(tmp1);

    *r1 =_mm256_mullo_epi16(tmp0, tmp1);


}
 void _mm256_print_epi8(__m256i vec)
{
    char temp[32];
    _mm256_storeu_si256((__m256i*)&temp[0], vec);
    int i;
    for(i=0; i<32; i++)
        printf(" %3i,", temp[i]);


}

 void _mm256_print_epi16(__m256i vec)
{
    short temp[16];
    _mm256_storeu_si256((__m256i*)&temp[0], vec);
    int i;
    for(i=0; i<16; i++)
        printf(" %3i,", temp[i]);   
}

Вывод:

[martin@mrt Stack over flow]$ gcc -O2 -march=native mul_epi8.c -o out
[martin@mrt Stack over flow]$ ./out

v0 =    1,   2,   3,  -4,   5,   6,   7,   8,   9, -10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23, -24,  25,  26,  27,  28,  29,  30,  31,  32,
v1 =    2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12, -13,  14,  15,  16,  17,  18,  19, -20,  21,  22,  23,  24, -25,  26,  27,  28,  29,  30,  31,  32,  33,
r0 =    2,   6,  12, -20,  30,  42,  56,  72,  90, -110, 132, -156, 182, 210, 240, 272,
r1 =  306, 342, -380, 420, 462, 506, 552, 600, 650, 702, 756, 812, 870, 930, 992, 1056,
finished
[martin@mrt Stack over flow]$ 

ПРИМЕЧАНИЕ: Я прокомментировал промежуточные результаты tmp0и tmp1 в рекомендуемом коде.Кроме того, как предложил Питер в комментариях и предоставил ссылку на Годболт, если ваша программа загружается из памяти и вам не нужно умножать элементы в векторах, вы можете использовать этот код:

#include <immintrin.h>

//v0 and v1 are 8 bit input vectors. r0 and r1 are 18 bit results of multiplications
__m256i mul_epi8_to_16(__m128i v0, __m128i v1)
{
    __m256i tmp0 = _mm256_cvtepi8_epi16 (v0); //printf("\ntmp0 = ");_mm256_print_epi16(tmp0);
    __m256i tmp1 = _mm256_cvtepi8_epi16 (v1); //printf("\ntmp1 = ");_mm256_print_epi16(tmp1);

    return _mm256_mullo_epi16(tmp0, tmp1);
}

__m256i mul_epi8_to_16_memsrc(char *__restrict a, char *__restrict b){

    __m128i v0 = _mm_loadu_si128((__m128i*) a);
    __m128i v1 = _mm_loadu_si128((__m128i*) b);
    return mul_epi8_to_16(v0, v1);
}


int main()
{
    char a0[32] = {1, 2, 3, -4, 5, 6, 7, 8, 9, -10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, -24, 25, 26, 27, 28, 29, 30, 31, 32};
    char a1[32] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -13, 14, 15, 16, 17, 18, 19, -20, 21, 22, 23, 24, -25, 26, 27, 28, 29, 30, 31, 32, 33};

    __m256i r0 = mul_epi8_to_16_memsrc(a0, a1);

}
...