在查看了x86/x64体系结构中的寄存器表之后,我注意到有完整的128位,256位和512位寄存器部分,我从未见过它们在汇编或反编译的C/C++代码中使用: XMM(0-15)代表128,YMM(0-15)代表256,ZMM(0-31)512。
在做了一点挖掘之后,我必须使用2个64位运算来对128位数字进行数学运算,而不是使用通用的add
,sub
,mul
和div
运算。如果是这种情况,那么拥有这些扩展的寄存器集到底有什么用,并且可以使用任何汇编操作来对其进行操作?
最佳答案
这些用于
you have to use 2 64 bit operations in order to perform math on a 128 bit number
不,它们不是用于此目的的,您不能轻松地将它们用于128位数字。仅用2条指令添加128位数字的速度要快得多:如果处理XMM寄存器,则使用
add rax, rbx; adc rdx, rcx
而不是大量指令。看关于它们的用法,首先将它们用于标量浮点运算。因此,如果您在C或C++中具有
float
或double
,则很可能将它们存储在XMM寄存器的下部,并通过以ss
(标量单)或sd
(标量双)结尾的指令进行操作实际上,还有另一组八个80位
ST(x)
寄存器,可与x87 co-processor一起使用来进行浮点数学运算。但是,它们速度慢且难以预测。速度慢,因为默认情况下操作会以更高的精度进行,这固有地需要更多的工作,并且在必要时还需要requires a store then load to round to lower precision。高精度也是不可预测的。乍一看这很奇怪,但是很容易解释,例如某些操作在float
或double
精度上上溢或下溢,但在long double
精度上不上。这会导致32和64位build1中的许多错误或意外结果Here is a floating-point example on both sets of registers
// f = x/z + y*z
x87:
fld dword ptr [esp + 12]
fld st(0)
fdivr dword ptr [esp + 4]
fxch st(1)
fmul dword ptr [esp + 8]
faddp st(1)
ret
SSE:
divss xmm0, xmm2
mulss xmm1, xmm2
addss xmm0, xmm1
ret
AVX:
vdivss xmm0, xmm0, xmm2
vmulss xmm1, xmm1, xmm2
vaddss xmm0, xmm0, xmm1
ret
转向更快,更一致的SSE寄存器是the 80-bit extended precision
long double
type is not available in MSVC anymore的原因之一然后,英特尔为MMX instruction set操作引入了SIMD,该操作使用具有新名称
ST(x)
的相同MMX
寄存器。 MMX可能代表Multiple Math eXtension或Matrix Math eXtension,但是恕我直言,它很可能是MultiMedia eXtension,因为多媒体和互联网在当时变得越来越重要。在多媒体解决方案中,您经常必须对每个像素,纹理像素,声音样本执行相同的操作……for (int i = 0; i < 100000; ++i)
{
A[i] = B[i] + C[i];
D[i] = E[i] * F[i];
}
无需单独对每个元素进行操作,我们可以一次执行多个元素来加快速度。这就是人们发明SIMD的原因。使用MMX,您可以一次增加8个像素 channel 的亮度,或者一次增加四个16位声音样本的音量...单个元素上的操作称为scalar,完整寄存器称为矢量,它是一组标量值(value)观
由于MMX的缺点(例如重用
ST
寄存器或缺少浮点支持),当使用Streaming SIMD Extensions (SSE)扩展SIMD指令集时,英特尔决定为它们提供一套全新的名为XMM的寄存器,该寄存器集的长度是原来的两倍(128位) ),因此我们现在可以一次处理16个字节。而且它还一次支持多个浮点运算。然后,Intel在Advanced Vector Extensions (AVX)中将XMM扩展为256位YMM,并在AVX-512中将其长度再次加倍(这也使寄存器的数量在64位模式下增加到32个)。现在您可以一次处理十六个32位整数通过上面的内容,您可能会了解这些寄存器的第二个也是最重要的角色:与单个指令并行执行多个数据的操作。例如,在SSE4中引入了一组instructions to work on C strings。现在您可以计算字符串长度,查找子字符串...通过一次检查多个字节来更快。您还可以更快地复制或比较内存。现代
memcpy
实现一次最多可移动16、32或64个字节,具体取决于最大的寄存器宽度,而不是像最简单的C解决方案那样一一对应。不幸的是,尽管自动矢量化仍在变得越来越智能,但编译器仍然无法从标量代码转换为并行代码,因此大多数时候我们都必须提供帮助。
由于SIMD的重要性,当今几乎所有高性能架构都具有自己的SIMD版本,例如PowerPC上的Altivec或ARM上的Neon。
1一些例子:
关于assembly - 128位至512位寄存器有什么用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52932539/