英特尔® 64 和 IA-32 架构优化参考手册§5.1 对混合整数/FP“数据类型”说了类似的话(但奇怪的是不是单数和双数):
在编写适用于整数和浮点数据的 SIMD 代码时,请使用 SIMD 转换指令或加载/存储指令的子集,以确保 XMM 寄存器中的输入操作数包含正确定义以匹配指令的数据类型。
包含交叉类型使用的代码序列在不同的实现中产生相同的结果,但会导致显着的性能损失。强烈建议不要使用 SSE/SSE2/SSE3/SSSE3/SSE44.1 指令对 XMM 寄存器中类型不匹配的 SIMD 数据进行操作。
英特尔® 64 和 IA-32 架构软件开发人员手册同样令人困惑:
SSE 和 SSE2 扩展定义了打包和标量浮点数据类型以及 128 位 SIMD 整数数据类型的类型化操作,但 IA-32 处理器不会在架构级别强制执行这种类型化。他们只在微架构级别执行它。
...
Pentium 4 和 Intel Xeon 处理器执行这些指令时不会产生无效操作数异常 (#UD) 并将在寄存器 XMM0 中产生预期的结果(即每个寄存器的高 64 位和低 64 位将被视为双精度浮点值,处理器将相应地对它们进行操作)。
...
在此示例中: XORPS 或 PXOR 可用于代替 XORPD 并产生相同的正确结果。然而,由于操作数数据类型和指令数据类型之间的类型不匹配,由于指令在微体系结构级别的实现会导致延迟损失。
使用错误类型的移动指令也可能导致延迟损失。例如,MOVAPS 和 MOVAPD 都可用于将压缩的单精度操作数从内存移动到 XMM 寄存器。但是,如果使用 MOVAPD,则当正确键入的指令尝试使用寄存器中的数据时,将产生延迟损失。
请注意,将数据从 XMM 寄存器移动到内存时不会产生这些延迟损失。
我真的不知道“他们只在微架构级别执行它”是什么意思,除了它表明不同的“数据类型”被 μarch 以不同的方式处理。我有几个猜测:
- AIUI、x86 内核由于寄存器不足,通常使用寄存器重命名。也许它们在内部为整数/单/双操作数使用不同的寄存器,因此它们可以更靠近相应的向量单元。
- FP 数字似乎也可能在内部使用不同的格式表示(例如,使用更大的指数来摆脱 denorms)并仅在必要时转换为规范位。
- CPU 使用“转发”或“绕过”,因此执行单元不必等待数据写入寄存器,然后才能被后续指令使用,通常可以节省一两个周期。这可能不会发生在整数和 FP 单元之间。