SIMD为什么叫SIMD时是单条数据指令?
Why does SIMD have single data instructions when it's called SIMD?
我一直在想..它在单指令多数据中被称为SIMD。那么为什么它有条条数据指令呢?
例如vaddss
是多条数据vaddps
的单条数据等值。几乎每条 SIMD 指令都有一个数据版本。
为什么?
SIMD为什么叫SIMD时只有单条数据指令?
从这个意义上说,不是 SIMD 指令
vaddss
是一个标量 FP 数学指令,它对 FP/SIMD 寄存器 (XMM0..15) 中的数据进行运算。它存在是因为 x87 不是一个非常方便的编译器目标,它的 stack-based 寄存器通常需要 fxch
和其他怪癖。英特尔添加了一种新方法来执行标量 FP 数学以及 SSE1(浮点)和 SSE2(双精度),幸运的是这是 x86-64 的基线,所以每个人都可以使用它。
称其为 SIMD 指令的人指的是以下之一:
- 它操作的是哪个寄存器。 (XMM0 是 16 字节宽,显然是一个 SIMD 寄存器,即使您只关心保存标量值的低位元素也是如此。)
- 事实上,它是一条 AVX 指令,因此它与主要针对 SIMD 使用的 ISA 扩展一起引入,因此被称为 SIMD 扩展或指令集。
- 这也意味着它使用 MXCSR 进行舍入模式和 FP 异常记录/取消屏蔽,它可以采用的异常类型与其他 SSE/AVX Intel 记录为“SIMD [=98] 的指令相同=] Exceptions”作为简明术语以区别于传统 x87。
- 或者他们在谈论 use-case 在高元素具有实际数据时只对低元素做某事。 (非常罕见,但你可以做一些事情。也许更可能使用
sd
标量双精度,其中低双精度是 XMM 寄存器的一半。)
或者如果他们实际上是根据 Flynn's taxonomy SISD vs. SIMD vs. MIMD 等 我非常怀疑有人会不过,实际上是这个意思。 ss
和 sd
标量 FP 数学指令是 SISD,single-instruction single-data。顺便说一句,它们只存在于 FP 数学; x86 已经有像 add eax, ecx
这样的用于标量整数数学的指令,并且没有 paddb
甚至 xorps
.
的标量版本
使用单独的标量 FP 数学指令的一个原因是使用 addps
也会对 XMM 寄存器的高位元素中可能存在的任何垃圾进行操作。 这会引发额外的 FP 异常(通常被屏蔽,因此仅记录在 MXCSR (fenv.h
) 中,但如果未屏蔽会陷入 OS。)
上面的元素都是 0.0
(调用约定不需要,顺便说一句), addps
不会引发任何额外的异常,但是 divps
会除法零。
对于像小整数这样的 non-zero 垃圾,它可能是 bit-pattern 次正规浮点数,或者结果可能是次正规的,导致巨大的减速(~100 倍),因为 CPU 在许多情况下需要一个微代码帮助来处理次正规输入或输出(或者当 SSE1 在 Pentium III 中是新的时,可能是所有次正规情况)。除非你像 gcc -ffast-math
那样设置 FTZ 和 DAZ(刷新为零,非正规为零)。
对于像 xorps
或 paddq
这样不进行实际 FP 数学运算的指令,不可能有 FP 异常或微码辅助。即使您只关心 XMM 的低 32 位或 64 位,也可以只使用它们。
MMX 或 SSE2 偶尔会在 32 位代码中用于执行标量 64 位整数数学计算,高位字节为零或垃圾。 MMX paddq mm0, mm1
是 SISD 指令,但 SSE2 paddq xmm0, xmm1
是 SIMD 指令。
SSE1 是 Pentium 3 中的新功能,其中 SIMD 执行单元和寄存器只有 64 位宽。 addps
解码为 2 微指令; addss
解码为 1。因此,即使在最好的情况下,也存在性能动机。
这也可能是英特尔不幸设计的原因,其中 sqrtss
和 cvtsi2ss
以及其他合并到目的地,需要在 xor-zeroing 上花费额外的 front-end 带宽,或者冒着虚假依赖的风险:。这是一个 short-sighted 设计决定,让它们在 Pentium 3 上 single-uop,不幸的是,他们在 SSE2 中遵循 double
精度,并在有机会时坚持使用 AVX 和 AVX-512引入具有不同语义的更好版本。至少 AVX 版本采用第二个源寄存器进行合并,因此您可以选择一个“冷”寄存器作为解决方法,请参阅我对链接副本的回答。
标量FP与SIMD共享寄存器是正常的
为标量 FP 设置另一组寄存器没有必要也没有用,与 x87 FPU 或 general-purpose 整数寄存器共享会因不同的原因而变得更糟。
SIMD 寄存器与标量 FP 寄存器重叠或相同在其他 ISA 上是完全正常的;一些没有像 x87 这样奇怪的设计的 ISA(如 ARM)不需要新的架构状态来引入 SIMD。例如ARM's NEON q0..q15
16-byte registers 映射到 VFPv3 中存在的 d0..d31
double-precision FP 寄存器对。
(不过,我不确定 partial-register 别名在其他 ISA 的 SIMD 扩展中是否真的很常见。可能是一些引入了新的架构状态,或者只是将 FP double-precision 寄存器用作 64位整数 SIMD 而不是 128位。)
在 OS 内核中,您经常谈论在上下文切换时保存“FPU 状态”(而不仅仅是 general-purpose 整数寄存器),现在 short-hand FPU 和 SIMD 状态。例如在 Linux 内核中,您需要在使用 XMM/YMM/ZMM 寄存器的 运行 指令之前使用 kernel_fpu_begin()
。 (例如在 RAID5 / RAID6 驱动程序中)。
我一直在想..它在单指令多数据中被称为SIMD。那么为什么它有条条数据指令呢?
例如vaddss
是多条数据vaddps
的单条数据等值。几乎每条 SIMD 指令都有一个数据版本。
为什么?
SIMD为什么叫SIMD时只有单条数据指令?
从这个意义上说,不是 SIMD 指令
vaddss
是一个标量 FP 数学指令,它对 FP/SIMD 寄存器 (XMM0..15) 中的数据进行运算。它存在是因为 x87 不是一个非常方便的编译器目标,它的 stack-based 寄存器通常需要 fxch
和其他怪癖。英特尔添加了一种新方法来执行标量 FP 数学以及 SSE1(浮点)和 SSE2(双精度),幸运的是这是 x86-64 的基线,所以每个人都可以使用它。
称其为 SIMD 指令的人指的是以下之一:
- 它操作的是哪个寄存器。 (XMM0 是 16 字节宽,显然是一个 SIMD 寄存器,即使您只关心保存标量值的低位元素也是如此。)
- 事实上,它是一条 AVX 指令,因此它与主要针对 SIMD 使用的 ISA 扩展一起引入,因此被称为 SIMD 扩展或指令集。
- 这也意味着它使用 MXCSR 进行舍入模式和 FP 异常记录/取消屏蔽,它可以采用的异常类型与其他 SSE/AVX Intel 记录为“SIMD [=98] 的指令相同=] Exceptions”作为简明术语以区别于传统 x87。
- 或者他们在谈论 use-case 在高元素具有实际数据时只对低元素做某事。 (非常罕见,但你可以做一些事情。也许更可能使用
sd
标量双精度,其中低双精度是 XMM 寄存器的一半。)
或者如果他们实际上是根据 Flynn's taxonomy SISD vs. SIMD vs. MIMD 等 我非常怀疑有人会不过,实际上是这个意思。 ss
和 sd
标量 FP 数学指令是 SISD,single-instruction single-data。顺便说一句,它们只存在于 FP 数学; x86 已经有像 add eax, ecx
这样的用于标量整数数学的指令,并且没有 paddb
甚至 xorps
.
使用单独的标量 FP 数学指令的一个原因是使用 addps
也会对 XMM 寄存器的高位元素中可能存在的任何垃圾进行操作。 这会引发额外的 FP 异常(通常被屏蔽,因此仅记录在 MXCSR (fenv.h
) 中,但如果未屏蔽会陷入 OS。)
上面的元素都是 0.0
(调用约定不需要,顺便说一句), addps
不会引发任何额外的异常,但是 divps
会除法零。
对于像小整数这样的 non-zero 垃圾,它可能是 bit-pattern 次正规浮点数,或者结果可能是次正规的,导致巨大的减速(~100 倍),因为 CPU 在许多情况下需要一个微代码帮助来处理次正规输入或输出(或者当 SSE1 在 Pentium III 中是新的时,可能是所有次正规情况)。除非你像 gcc -ffast-math
那样设置 FTZ 和 DAZ(刷新为零,非正规为零)。
对于像 xorps
或 paddq
这样不进行实际 FP 数学运算的指令,不可能有 FP 异常或微码辅助。即使您只关心 XMM 的低 32 位或 64 位,也可以只使用它们。
MMX 或 SSE2 偶尔会在 32 位代码中用于执行标量 64 位整数数学计算,高位字节为零或垃圾。 MMX paddq mm0, mm1
是 SISD 指令,但 SSE2 paddq xmm0, xmm1
是 SIMD 指令。
SSE1 是 Pentium 3 中的新功能,其中 SIMD 执行单元和寄存器只有 64 位宽。 addps
解码为 2 微指令; addss
解码为 1。因此,即使在最好的情况下,也存在性能动机。
这也可能是英特尔不幸设计的原因,其中 sqrtss
和 cvtsi2ss
以及其他合并到目的地,需要在 xor-zeroing 上花费额外的 front-end 带宽,或者冒着虚假依赖的风险:double
精度,并在有机会时坚持使用 AVX 和 AVX-512引入具有不同语义的更好版本。至少 AVX 版本采用第二个源寄存器进行合并,因此您可以选择一个“冷”寄存器作为解决方法,请参阅我对链接副本的回答。
标量FP与SIMD共享寄存器是正常的
为标量 FP 设置另一组寄存器没有必要也没有用,与 x87 FPU 或 general-purpose 整数寄存器共享会因不同的原因而变得更糟。
SIMD 寄存器与标量 FP 寄存器重叠或相同在其他 ISA 上是完全正常的;一些没有像 x87 这样奇怪的设计的 ISA(如 ARM)不需要新的架构状态来引入 SIMD。例如ARM's NEON q0..q15
16-byte registers 映射到 VFPv3 中存在的 d0..d31
double-precision FP 寄存器对。
(不过,我不确定 partial-register 别名在其他 ISA 的 SIMD 扩展中是否真的很常见。可能是一些引入了新的架构状态,或者只是将 FP double-precision 寄存器用作 64位整数 SIMD 而不是 128位。)
在 OS 内核中,您经常谈论在上下文切换时保存“FPU 状态”(而不仅仅是 general-purpose 整数寄存器),现在 short-hand FPU 和 SIMD 状态。例如在 Linux 内核中,您需要在使用 XMM/YMM/ZMM 寄存器的 运行 指令之前使用 kernel_fpu_begin()
。 (例如在 RAID5 / RAID6 驱动程序中)。