AVX2带符号的8位元素的整数乘法,产生带符号的16位结果?

时间:2018-09-17 15:01:17

标签: c++ simd avx avx2

我有两个__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个结果向量,因为输出元素的宽度是输入元素宽度的两倍。或者只使用偶数输入元素(0、2、4等),可以与_mm_mul_epu32类似地工作

1 个答案:

答案 0 :(得分:3)

您想将结果分成两个向量,所以这是我对您的问题的建议。我试图做到清晰,简单和可实现:

#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);

}