SSE/AVX - VMULPD 为小整数输入生成全零?
SSE/AVX - VMULPD produces all zeros for small integer inputs?
我正在使用 X64dbg 测试 SSE/AVX 汇编指令,以便在使用它们编写代码之前更好地了解它们的行为。我已经能够以这种方式毫无问题地测试 vmovapd、vbroadcastsd、vsubpd 和 vaddpd 指令。
我加载了 YMM 寄存器如下:
YMM0: 0000000000000004000000000000000400000000000000040000000000000004
YMM1: 0000000000000002000000000000000200000000000000020000000000000002
YMM2: 0101010101010101010101010101010101010101010101010101010101010101
然后,我执行这条指令:
VMULPD ymm2, ymm1, ymm0
我正在尝试将 YMM0 与 YMM1 相乘,并将结果存储在 YMM2 中,但是在我执行此指令后,YMM2 包含以下内容:
0000000000000000000000000000000000000000000000000000000000000000
但我希望这样:
0000000000000008000000000000000800000000000000080000000000000008
(That's four 8's from 4.0 * 2.0)
根据第 798 页的 Intel 64 and IA-32 Software Developer's Manual,这应该有效:
VMULPD ymm1, ymm2, ymm3/m256
Multiply packed double-precision floating-point values
in ymm3/m256 with ymm2 and store result in ymm1.
那么我在这里错过了什么?
您加载的值表示异常(非常小)double
s。他们的产品下溢到 +0.0,即使没有启用 FTZ / DAZ(刷新为零/非正规零)。
顺便说一句,将 0
写入 ymm2
不是 "does nothing"。 VMULPD
的目标操作数是只写的,因此看到它从您加载的调试标记变为全零证明它确实做了一些事情。
如果您正在寻找 64 位压缩整数乘法,那就太难了。 AVX2 没有打包的 64 位乘法。它确实具有 32x32 -> 64 位乘法 (vpmuludq
) 和打包 32x32 -> 32 位(VPMULLD
在许多 CPU 上为 2 微指令)。使用 AVX2 向量化 64x64 -> 64 位乘法 可能 有利可图,参见 。
AVX512 有 64x64 -> 64 位乘法。
如果您的整数(及其乘积)可以用 double
精确表示,那么使用压缩转换到 double
并在其上使用 vmulpd
可能是值得的,因为硬件具有出色的 add / mul / FMA with packed double
吞吐量。 (Haswell/Skylake:每个时钟 2 个向量,比 vpmulld
好很多)
有趣的事实:
IEEE float/double 有一个有趣的 属性(除了符号位)作为整数进行比较 "works"。这就是指数有偏差的原因,因此将 1
加到二进制表示中的整数会产生下一个可表示的值。 (在 SSE 上实施 nextafterf
很有趣;I looked at it a while ago 但从来没有抽出时间发送补丁。)
另请参阅 https://www.h-schmidt.net/FloatConverter/IEEE754.html for int -> float binary representation. (for single-precision, but https://en.wikipedia.org/wiki/Double-precision_floating-point_formatIEEE binary64 与 binary32 的工作原理相同。)
我正在使用 X64dbg 测试 SSE/AVX 汇编指令,以便在使用它们编写代码之前更好地了解它们的行为。我已经能够以这种方式毫无问题地测试 vmovapd、vbroadcastsd、vsubpd 和 vaddpd 指令。
我加载了 YMM 寄存器如下:
YMM0: 0000000000000004000000000000000400000000000000040000000000000004
YMM1: 0000000000000002000000000000000200000000000000020000000000000002
YMM2: 0101010101010101010101010101010101010101010101010101010101010101
然后,我执行这条指令:
VMULPD ymm2, ymm1, ymm0
我正在尝试将 YMM0 与 YMM1 相乘,并将结果存储在 YMM2 中,但是在我执行此指令后,YMM2 包含以下内容:
0000000000000000000000000000000000000000000000000000000000000000
但我希望这样:
0000000000000008000000000000000800000000000000080000000000000008
(That's four 8's from 4.0 * 2.0)
根据第 798 页的 Intel 64 and IA-32 Software Developer's Manual,这应该有效:
VMULPD ymm1, ymm2, ymm3/m256
Multiply packed double-precision floating-point values
in ymm3/m256 with ymm2 and store result in ymm1.
那么我在这里错过了什么?
您加载的值表示异常(非常小)double
s。他们的产品下溢到 +0.0,即使没有启用 FTZ / DAZ(刷新为零/非正规零)。
顺便说一句,将 0
写入 ymm2
不是 "does nothing"。 VMULPD
的目标操作数是只写的,因此看到它从您加载的调试标记变为全零证明它确实做了一些事情。
如果您正在寻找 64 位压缩整数乘法,那就太难了。 AVX2 没有打包的 64 位乘法。它确实具有 32x32 -> 64 位乘法 (vpmuludq
) 和打包 32x32 -> 32 位(VPMULLD
在许多 CPU 上为 2 微指令)。使用 AVX2 向量化 64x64 -> 64 位乘法 可能 有利可图,参见
AVX512 有 64x64 -> 64 位乘法。
如果您的整数(及其乘积)可以用 double
精确表示,那么使用压缩转换到 double
并在其上使用 vmulpd
可能是值得的,因为硬件具有出色的 add / mul / FMA with packed double
吞吐量。 (Haswell/Skylake:每个时钟 2 个向量,比 vpmulld
好很多)
有趣的事实:
IEEE float/double 有一个有趣的 属性(除了符号位)作为整数进行比较 "works"。这就是指数有偏差的原因,因此将 1
加到二进制表示中的整数会产生下一个可表示的值。 (在 SSE 上实施 nextafterf
很有趣;I looked at it a while ago 但从来没有抽出时间发送补丁。)
另请参阅 https://www.h-schmidt.net/FloatConverter/IEEE754.html for int -> float binary representation. (for single-precision, but https://en.wikipedia.org/wiki/Double-precision_floating-point_formatIEEE binary64 与 binary32 的工作原理相同。)