X86 2个64位整数的SSE乘法
如何将两个64位整数乘以另外两个64位整数?X86 2个64位整数的SSE乘法,x86,sse,simd,multiplication,sse2,X86,Sse,Simd,Multiplication,Sse2,如何将两个64位整数乘以另外两个64位整数? 我找不到任何可以执行此操作的指令。您需要使用32位乘法运算实现自己的64位乘法例程。不过,这可能不会比使用标量代码更有效,尤其是当需要对向量进行大量洗牌以获得所有必需的操作时。我知道这是一个老问题,但实际上我一直在寻找这个问题。因为仍然没有相关的指令,我自己用Paul R提到的pmuldq实现了64位乘法。这就是我想到的: // requires g++ -msse4.1 ... #include <emmintrin.h> #incl
我找不到任何可以执行此操作的指令。您需要使用32位乘法运算实现自己的64位乘法例程。不过,这可能不会比使用标量代码更有效,尤其是当需要对向量进行大量洗牌以获得所有必需的操作时。我知道这是一个老问题,但实际上我一直在寻找这个问题。因为仍然没有相关的指令,我自己用Paul R提到的pmuldq实现了64位乘法。这就是我想到的:
// requires g++ -msse4.1 ...
#include <emmintrin.h>
#include <smmintrin.h>
__m128i Multiply64Bit(__m128i a, __m128i b)
{
auto ax0_ax1_ay0_ay1 = a;
auto bx0_bx1_by0_by1 = b;
// i means ignored
auto ax1_i_ay1_i = _mm_shuffle_epi32(ax0_ax1_ay0_ay1, _MM_SHUFFLE(3, 3, 1, 1));
auto bx1_i_by1_i = _mm_shuffle_epi32(bx0_bx1_by0_by1, _MM_SHUFFLE(3, 3, 1, 1));
auto ax0bx0_ay0by0 = _mm_mul_epi32(ax0_ax1_ay0_ay1, bx0_bx1_by0_by1);
auto ax0bx1_ay0by1 = _mm_mul_epi32(ax0_ax1_ay0_ay1, bx1_i_by1_i);
auto ax1bx0_ay1by0 = _mm_mul_epi32(ax1_i_ay1_i, bx0_bx1_by0_by1);
auto ax0bx1_ay0by1_32 = _mm_slli_epi64(ax0bx1_ay0by1, 32);
auto ax1bx0_ay1by0_32 = _mm_slli_epi64(ax1bx0_ay1by0, 32);
return _mm_add_epi64(ax0bx0_ay0by0, _mm_add_epi64(ax0bx1_ay0by1_32, ax1bx0_ay1by0_32));
}
//需要g++-msse4.1。。。
#包括
#包括
__m128i乘法64位(_m128i a,__M128iB)
{
自动ax0\u ax1\u ay0\u ay1=a;
自动bx0_bx1_by0_by1=b;
//我的意思是被忽视
自动ax1_i_ay1_i=_mm_shuffle_epi32(ax0_ax1_ay0_ay1,_mm_shuffle(3,3,1,1));
自动bx1_i_by1_i=_mm_shuffle_epi32(bx0_bx1_by0_by1,_mm_shuffle(3,3,1,1));
自动ax0bx0\u ay0by0=\u mm\u mul\u epi32(ax0\u ax1\u ay0\u ay1,bx0\u bx1\u by0\u by1);
自动ax0bx1_ay0by1=_mm_mul_epi32(ax0_ax1_ay0_ay1,bx1_i_by1_i);
自动ax1bx0_ay1by0=_mm_mul_epi32(ax1_i_ay1_i,bx0_bx1_by0_by1);
自动ax0bx1_ay0by1_32=_mm_slli_epi64(ax0bx1_ay0by1,32);
自动ax1bx0_ay1by0_32=_mm_slli_epi64(ax1bx0_ay1by0,32);
返回_mm_add_epi64(ax0bx0_ay0by0,_mm_add_epi64(ax0bx1_ay0by1_32,ax1bx0_ay1by0_32));
}
Godbolt at.迟来的回答,但这是Barabas发布的更好的版本 如果您曾经使用过GCC或Clang的向量扩展,这就是他们使用的例程 这与长乘法和网格乘法使用的方法相同
65
* 73
----
15 // (5 * 3)
180 // (6 * 3) * 10
350 // (5 * 7) * 10
+ 4200 // + (6 * 7) * 100
------
4745
但是,它不是以10为单位,而是以32位为单位,并省略了最后一次乘法,因为它总是会移过第64位,就像截断大于99的值时不乘以6*7一样
#include <emmintrin.h>
/*
* Grid/long multiply two 64-bit SSE lanes.
* Works for both signed and unsigned.
* ----------------.--------------.----------------.
* | | b >> 32 | a & 0xFFFFFFFF |
* |----------------|--------------|----------------|
* | d >> 32 | b*d << 64 | a*d << 32 |
* |----------------|--------------|----------------|
* | c & 0xFFFFFFFF | b*c << 32 | a*c |
* '----------------'--------------'----------------'
* Add all of them together to get the product.
*
* Because we truncate the value to 64 bits, b*d << 64 will be zero,
* so we can leave it out.
*
* We also can add a*d and b*c first and then shift because of the
* distributive property: (a << 32) + (b << 32) == (a + b) << 32.
*/
__m128i Multiply64Bit(__m128i ab, __m128i cd)
{
/* ac = (ab & 0xFFFFFFFF) * (cd & 0xFFFFFFFF); */
__m128i ac = _mm_mul_epu32(ab, cd);
/* b = ab >> 32; */
__m128i b = _mm_srli_epi64(ab, 32);
/* bc = b * (cd & 0xFFFFFFFF); */
__m128i bc = _mm_mul_epu32(b, cd);
/* d = cd >> 32; */
__m128i d = _mm_srli_epi64(cd, 32);
/* ad = (ab & 0xFFFFFFFF) * d; */
__m128i ad = _mm_mul_epu32(ab, d);
/* high = bc + ad; */
__m128i high = _mm_add_epi64(bc, ad);
/* high <<= 32; */
high = _mm_slli_epi64(high, 32);
/* return ac + high; */
return _mm_add_epi64(high, ac);
}
#包括
/*
*栅格/长乘法两个64位SSE通道。
*适用于已签名和未签名。
* ----------------.--------------.----------------.
*| | b>>32 | a&0xFFFFFFFF|
* |----------------|--------------|----------------|
*| d>>32 | b*d在本文中,“两个64位整数”是什么意思?您是指一对64位整数(la复数)还是一对64位整数表示的单个128位整数?我是指一对64位整数表示的单个m128i位整数可能与的重复。相关:对于AVX2或SSE4.1,性能分析与64位标量代码(如果你还没有SIMD向量中的数据。)从我的头顶上看,SSE4中不是有一个pmuldqq
或者添加了什么吗?SSE4中有一个pmuldq
,它是32x32=>64位的乘法,所以你可以用它作为64x64位乘法的构建块。你知道最好的标量算法吗(假设您只有32位硬件)?这就是我要做的。我会将每个数字分成其上下32位部分,然后执行(ab)=(al+ah)*(blbh)=t1+t2+t3+t4,其中t1=albl,t2=albh,t3=ahbl t4=ahbh。每个项将是一个64位数字。然后t2和t3必须再次拆分为低和高,最终数字将是(ab)l=t1+t2l+t3l,(ab)h=t4+t2h+t3h+c,其中c是(a*b)的任何进位l、 这是4个mult,7个adds。这是真的吗?我自己从来没有实现过,但它应该是你建议的方法。我无法想象它会非常有效,所以可能只有当你有其他64位SIMD操作要与之结合时才值得。在Sandy Bridge上,通用multipli阳离子和向量乘法被发布到不同的端口,因此如果您执行多组乘法,您可能可以免费获得SSE乘法。但是,加法和洗牌将是一个问题。如果您执行不太需要端口5的操作,这些操作也可能免费提供。您对cod进行过基准测试吗与使用通用寄存器相比?我对结果很感兴趣,因为我正在进行大量的64×64位乘法。我刚刚做了一些基准测试,它仍然比标量乘法(用cl/O2编译)快大约831600000次平均乘法。在我功能强大的i7 5820k上0.37秒。同时,相同的标量乘法在平均乘法上花费了1.71次。所以它大约快4倍,这有点奇怪。我想cl非常擅长优化超标量指令\u mm_mul_epi32
是SSE4.1指令。\u mm_mul_epu32
是SSE2指令。\u mm\u mul\u epu32
产生更好的代码,但它需要无符号类型。使用-march=skylake-avx512
,我们得到AVX512DQvpmulqq
:)AVX512最终引入了64位元素大小整数乘法。顺便说一句,如果没有AVX2,使用SIMD进行64x64=>64位乘法可能不值得,除非您已经有了向量中的数据。(每64位整数一个标量imul r64,r/m64
uop非常好。)。我的答案是使用mullo_epi32
(SSE4.1或AVX2)以同时获得两个低*高产品,尽管pmulld
在英特尔CPU上需要2个UOP。是的。我想指出的是,Neon使用的方法也可以做到这一点,它使用vrev64(32位字交换)和4xi32乘法、vpaddl(成对加法),左移,然后长乘法累加。如果SSE有一个成对的加法,那可能会更快,但考虑到NEON_2_SSE对该指令进行了标量化,我认为它不会。但它会解码为两个随机数,提供一个垂直的padd
uop;不使用它会更快。我还没有查看链接答案的细节,但它是does提到使用psrlq/paddq/pand(3个总计量单位)代替phadd+pshufd(3个随机计量单位+一个添加)。更多说明b