查看了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
操作。如果是这种情况,那么拥有这些扩展的寄存器集到底有什么用?您可以使用任何汇编操作来操纵它们吗?
答案 0 :(得分:9)
那些用于
您必须使用2个64位运算才能对128位数字进行数学运算
不,它们不是为了这个目的,您不能轻易将它们用于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位build 1
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个像素通道的亮度,或者一次增加四个16位声音样本的音量...对单个元素的操作称为scalar,而完整的寄存器称为矢量,即一组标量值
由于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 一些示例:
答案 1 :(得分:4)
这些寄存器是SSE,AVX和AVX512指令集扩展的一部分。 C编译器至少应将它们的低64位用于ABI中指定的浮动操作。
这些寄存器是SIMD(单指令多数据)寄存器,主要用于高性能代码。该处理器支持特殊的SIMD指令,这些指令可以同时处理多个数据,所需的时间与处理单个数据所需的时间一样多。使用这些寄存器的大多数代码都是用汇编或特殊的固有函数编写的,因为编译器在自行使用SIMD指令方面非常不利。使编译器更好地进行此工作(一种称为 auto vectorization 的优化)是一个活跃的研究领域。
作为一个例子,假设一个程序想要对双精度浮点数进行矩阵乘法。使用AVX寄存器ymm0
至ymm15
,一次可以处理4个数字,与普通实现相比,该算法的速度提高了4倍。完全不同。
有关使用这些寄存器的指令,请参考指令集参考。 This website以可访问的方式列出所有这些文件。如果要使用它们,我建议您使用内在函数,因为它们比汇编要容易使用。