我读过这篇文章,做过非标准化标志,比如非标准化为零,影响了相等性的比较,我了解FTZ和daz标志之间的用法和区别。
DAZ应用于FP操作的输入,FTZ应用于输出。
让我困惑的是,如果设置了FTZ,那么非正规值在程序集视图中从哪里来。我认为它只能是常数值,要么作为立即数操作数,要么来自部分.rodata
(通过RIP相对寻址访问(。
但我发现在我的二进制文件中,这些地方没有非正规值,但它仍然存在FP-ASSIST问题,导致性能不佳。
如果我同时设置DAZ和FTZ,问题就会消失,性能也会变得更好。事实上,我甚至在源代码中都找不到任何非规范化的输入。我真的很困惑,非正规值是从哪里来的?
另一个问题是,对于指令vmovsd 0x9498(%rip),%xmm0
,假设0x9498(%rip)
是一个非正规值,如果我们分别设置FTZ或DAZ,那么在该指令执行后,xmm0
会发生什么?
在我的理解中,DAZ会使它以0x9498(%rip)
为零,并将0
移动到xmm0;FTZ会将0x9498(%rip)
移动到xmm0,并发现它是非正规的,所以将xmm0
刷新为零。我不确定,这是正确的吗?
非正规(也称为亚正规(是IEEE二进制格式中指数字段为0的值。https://en.wikipedia.org/wiki/Double-precision_floating-point_format
当FP数学指令(非移动或纯逐位布尔(读取这样一个数字作为输入操作数时,当尾数与其他操作数对齐时,以及当应用指数为0或非零所隐含的尾数的隐式高位时,它必须处理这种特殊情况。
是的,由于大多数浮点值都是其他FP计算的结果,所以输出上的FTZ在大多数情况下都是足够的。是的,FTZ是必要的,因为正态数上的mul/div/add/sub可以创建一个次正态结果。(对于加法,输入需要相反的符号(。IEEE的另一个"基本"精确取整运算sqrt无法创建子规范,因为它使数字接近1.0。
显而易见的是,使用perf record
来找出你在哪里获得FP辅助,并在那里添加一些额外的检查来打印,或者当你在那里发现异常时。(然后在该分支中设置一个断点,以便检查情况。(
FTZ设置的非标准化(非详尽(的可能来源,即FP数学运算之外的其他来源:
- 浮动字符串,用于构建具有扩展精度整数的FP位模式,如Glibc的
strtod
- 如果您正在读取二进制数据,请输入文件/网络
- 其他线程或通过来自未使用FTZ运行的其他进程的共享内存。(MXCSR中的FTZ/DAZ和舍入模式是每线程的体系结构状态。说到这一点,如果在启动另一个线程后仅在主线程中设置FTZ,则对已启动的线程无效。(
- 可能是像
nextafter
这样的FP位模式的整数操作。也可能是exp
实现内部的一部分,该实现将整数填充到double
的指数字段中 - 编译时间常量值。不过,它们不必以文字值的形式出现在源代码中。例如CCD_ 14将是编译时的非正规化。但你会在
.rodata
或.data
中找到它们。非常数非零静态/全局变量进入.data
显然,任何使用整数的FP位模式的手动操作也可以做到这一点。如何在没有AVX2的情况下使用字节中的位来设置ymm寄存器中的双字?如果我没有花额外的指令来避免的话,(vmovmskps的逆(可能会产生一个比较的非规范化输入,但这是一个不寻常的手动矢量化技巧,编译器不会为你做。
立即数
x86没有FP立即数;则必须是mov rax, imm64
/movq xmm0, rax
或类似的。但编译器不这么做,因为从.rodata
加载通常更高效。
指令
vmovsd 0x9498(%rip),%xmm0
vmovsd
只是一个负载,并且总是精确地复制64位;在架构上等效于CCD_ 23 SIMD整数负载。
它不通过ALU运行值,因此MXCSR位对vmovsd
、FP混洗等没有任何影响。只有进行实际FP数学运算并可能引发FP异常的指令才会受到影响。您可以通过查看asm手动条目的异常部分来判断。例如CCD_ 25确实服从DAZ在根据指定模式对其进行舍入之前可能将输入舍入为零。