我如何将 MMX mulH 和 mulL 用于两个 64 位整数以获得一个 128 位整数
How do i use MMX mulH and mulL for two 64 bit integers to get one 128 bit integer
Hello, I'm working on yet another arbitrary precision integer library. I wanted to implement multiplication but I got stuck when _m_pmulhw
in <mmintrin.h>
just didn't work. there is very little documentation 在 MMX 指令上。当我测试它时,当我乘以两个 UINT64_MAXs 时,它只会给我乱码。
uint_fast64_t mulH(const uint_fast64_t &a, const uint_fast64_t &b) {
return (uint_fast64_t)_m_pmulhw((__m64)a,(__m64)b);
}
uint_fast64_t mulL(const uint_fast64_t &a, const uint_fast64_t &b) {
return (uint_fast64_t)_m_pmullw((__m64)a,(__m64)b);
}
int main() {
uint64_t a = UINT64_MAX;
uint64_t b = UINT64_MAX;
std::cout << std::bitset<64>(mulH(a,b)) << std::bitset<64>(mulL(a,b));
}
output: 00000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000100000000000000010000000000000001
我不知道为什么它不工作我有一个 A6-4400M APU...
coreinfo's 输出:MMX * Supports MMX instruction set
所以我想我可以说,它并非不受支持。如果有人可以给我一些如何使这项工作的提示,谢谢。
编译器:gcc
IDE: visual studio 代码
我不是这方面的专家,但根据 https://www.felixcloutier.com/x86/pmulhw,这些指令不会执行 64x64->128 乘法;他们做了四次 16x16->32 乘法。请注意描述中的单词 "packed"。此外,它是一个带符号的乘法。
因此您的 64 位 UINT64_MAX
值被解释为 0xffff
的四个字,也就是说 -1
。因此,您将 -1
乘以 -1
四次。当然每一个的数字答案是1
。 pmulhw
指令的结果是结果的高半部分(即0x0000
的四个字),pmullw
是结果的低半部分(即0x0001
的四个字) .
这正是你所得到的,所以在我看来,这些说明工作得很好。
如果你想对两个 64 位整数进行无符号乘法运算,普通的老式 mul
指令就可以满足你的目的,而让 gcc
生成它的最简单方法是可能通过将输入转换为 __uint128_t
并与通常的 *
运算符相乘。
我认为您误解了 _m_pmulhw
的作用。它实际上非常清楚地记录在英特尔的内部指南中:https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_pmulhw&expand=4340. The corresponding instruction is pmulhw
, which is also clearly documented on e.g. Felix Cloutier's x86 instructions guide: https://www.felixcloutier.com/x86/pmulhw
它将四对16位整数乘以packed在两个操作数中,然后产生所有的高半部分四次乘法(压缩高位乘法 - 字)。这意味着,对于输入 0x12345678abcdef01、0x9876543210fedcba,它将乘以 0x1234 * 0x9876
、0x5678 * 0x5432
、0xabcd * 0x10fe
、0xef01 * 0xdcba
,并将每个结果的高 16 位打包到输出中。
对于您的示例,您将 0xffff * 0xffff
乘以四次,产生 32 位结果 0x00000001
(-1 * -1
,因为这是一个带符号的 16 位乘法),因此在高半部分得到 0x0000000000000000
,在低半部分得到 0x0001000100010001
- 这正是你在 bitset
输出中看到的。
如果您正在寻找 128 位乘法,实际上并没有一个内在的(_mulx_u64
除外,但它使用了新的 mulx
指令,这不是那个广泛)。 Microsoft 具有非标准的 _mul128
内在函数,但在其他平台上,您可以只使用 __int128
类型(或本地等效类型)来获得 64x64=>128 位乘法。
此外,我强烈建议使用 SSE 指令集而不是旧的 MMX 指令集; SSE 指令在大多数情况下速度更快,使您能够在更广泛的向量类型上运行(现在 256 位是标准的,AVX512 现在可用),这可以显着提高速度。
Hello, I'm working on yet another arbitrary precision integer library. I wanted to implement multiplication but I got stuck when _m_pmulhw
in <mmintrin.h>
just didn't work. there is very little documentation 在 MMX 指令上。当我测试它时,当我乘以两个 UINT64_MAXs 时,它只会给我乱码。
uint_fast64_t mulH(const uint_fast64_t &a, const uint_fast64_t &b) {
return (uint_fast64_t)_m_pmulhw((__m64)a,(__m64)b);
}
uint_fast64_t mulL(const uint_fast64_t &a, const uint_fast64_t &b) {
return (uint_fast64_t)_m_pmullw((__m64)a,(__m64)b);
}
int main() {
uint64_t a = UINT64_MAX;
uint64_t b = UINT64_MAX;
std::cout << std::bitset<64>(mulH(a,b)) << std::bitset<64>(mulL(a,b));
}
output: 00000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000100000000000000010000000000000001
我不知道为什么它不工作我有一个 A6-4400M APU...
coreinfo's 输出:MMX * Supports MMX instruction set
所以我想我可以说,它并非不受支持。如果有人可以给我一些如何使这项工作的提示,谢谢。
编译器:gcc
IDE: visual studio 代码
我不是这方面的专家,但根据 https://www.felixcloutier.com/x86/pmulhw,这些指令不会执行 64x64->128 乘法;他们做了四次 16x16->32 乘法。请注意描述中的单词 "packed"。此外,它是一个带符号的乘法。
因此您的 64 位 UINT64_MAX
值被解释为 0xffff
的四个字,也就是说 -1
。因此,您将 -1
乘以 -1
四次。当然每一个的数字答案是1
。 pmulhw
指令的结果是结果的高半部分(即0x0000
的四个字),pmullw
是结果的低半部分(即0x0001
的四个字) .
这正是你所得到的,所以在我看来,这些说明工作得很好。
如果你想对两个 64 位整数进行无符号乘法运算,普通的老式 mul
指令就可以满足你的目的,而让 gcc
生成它的最简单方法是可能通过将输入转换为 __uint128_t
并与通常的 *
运算符相乘。
我认为您误解了 _m_pmulhw
的作用。它实际上非常清楚地记录在英特尔的内部指南中:https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_m_pmulhw&expand=4340. The corresponding instruction is pmulhw
, which is also clearly documented on e.g. Felix Cloutier's x86 instructions guide: https://www.felixcloutier.com/x86/pmulhw
它将四对16位整数乘以packed在两个操作数中,然后产生所有的高半部分四次乘法(压缩高位乘法 - 字)。这意味着,对于输入 0x12345678abcdef01、0x9876543210fedcba,它将乘以 0x1234 * 0x9876
、0x5678 * 0x5432
、0xabcd * 0x10fe
、0xef01 * 0xdcba
,并将每个结果的高 16 位打包到输出中。
对于您的示例,您将 0xffff * 0xffff
乘以四次,产生 32 位结果 0x00000001
(-1 * -1
,因为这是一个带符号的 16 位乘法),因此在高半部分得到 0x0000000000000000
,在低半部分得到 0x0001000100010001
- 这正是你在 bitset
输出中看到的。
如果您正在寻找 128 位乘法,实际上并没有一个内在的(_mulx_u64
除外,但它使用了新的 mulx
指令,这不是那个广泛)。 Microsoft 具有非标准的 _mul128
内在函数,但在其他平台上,您可以只使用 __int128
类型(或本地等效类型)来获得 64x64=>128 位乘法。
此外,我强烈建议使用 SSE 指令集而不是旧的 MMX 指令集; SSE 指令在大多数情况下速度更快,使您能够在更广泛的向量类型上运行(现在 256 位是标准的,AVX512 现在可用),这可以显着提高速度。