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

C++ AVX2有符号8位元素的整数乘法,产生有符号16位结果?,c++,simd,avx,avx2,C++,Simd,Avx,Avx2,我有两个uum256i向量,用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); 我如何使用类似于\u mm256\u mul\u epi8(v0,v1)(不存

我有两个uum256i向量,用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);
我如何使用类似于
\u mm256\u mul\u epi8(v0,v1)
(不存在)或任何其他方式来乘以这些向量


我想要2个结果向量,因为输出元素宽度是输入元素宽度的两倍。或者类似于
\u mm\u mul\u epu32
的东西也可以,只使用偶数输入元素(0、2、4等)。

您希望结果在两个向量中分开,因此这是我对您的问题的建议。我努力做到清晰、简单和可实现:

#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]);   
}
注意:我已经在推荐代码中对中间结果tmp0和tmp1进行了注释。 此外,正如peter在评论中建议的,并提供了一个godbolt链接,如果您的程序从内存加载,并且您不需要在向量中乘以元素,那么您可以使用以下代码:

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

}
#包括
//v0和v1是8位输入向量。r0和r1是18位乘法结果
__m256i多表8至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);
返回mm256 mullo epi16(tmp0,tmp1);
}
__m256i mul_epi8_至_16_memsrc(字符*限制a,字符*限制b){
__m128i v0=_mm_loadu_si128((u m128i*)a);
__m128i v1=_mm_loadu_si128((u m128i*)b);
将mul_epi8_返回至_16(v0,v1);
}
int main()
{
字符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};
字符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=多个epi8到16个memsrc(a0,a1);
}

您可能会看到这一点,您的意思是
\u mm256\u mul\u epi8
?还请指定所需的结果类型:16位?低阶8位?高阶8位?饱和8位结果?@PaulR,对不起,我的意思是“\u mm256\u mul\u epi8”,它不存在。如果可能的话,我希望得到16位的结果。最明显的方法是将数据解包为16位元素(由于车道交叉,对签名不方便,但您始终可以解包并使用
vpmovsx
),然后使用
\u mm256\u mullo\u epi16
。如果可以处理一个输入是无符号的,另一个是有符号的,那么还可以使用一些掩码来获取奇偶元素。(
0*anything+a*b
=
a*b
,因此您有一个加宽的乘法,只需要一个和设置的指令。)@KaraUL:仔细阅读文档:
maddubs
接受一个有符号输入和一个无符号输入。如果其中一个无符号输入的范围有限,如始终为0..127,则可以将其用作有符号输入。您可能希望使用128位输入向量,这样就可以编译为
vpmovsxbw ymm0,[mem]
加载,而不会在shuffle端口上出现瓶颈,也可以使用
\u mm256\u extractf128\u si256
解包。此外,这将使您的助手函数只返回一个
\uuuum256i
。一些负输入将有助于您的测试用例,以表明
\umm256\ucvtepi8\uepi16
是正确的符号扩展,而不是使用零或
\umm256\ucvtepu8\uepi16
@PeterCordes,嘿,Peter,实际上,我认为在向量中呈现的元素上使用
vpmovsxbw
,其本质是
\uuuum256i\umm256\ucvtepi8\uepi16(\uuuuum128i a)
,可能并不比提取更好。因为我必须写入缓存,然后再从中读取。编译器可以将
\u mm\u loadu\u si128
折叠到
\u mm256\u cvtepi8\u epi16
的内存源中。没有存储/重新加载。是一个具有128位输入的函数版本,以及一个在2次加载的结果中内联函数的包装器。OP显示它们是从内存加载的,因此经过一些计算,它们还没有
\uuuum256i
向量。
#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);

}