Assembly 128位到512位寄存器的用途是什么?
在查看了x86/x64体系结构中的寄存器表之后,我注意到有一整段128、256和512位寄存器,我从未见过它们被用于汇编或反编译的C/C++代码:XMM(0-15)代表128,YMM(0-15)代表256,ZMM(0-31)512Assembly 128位到512位寄存器的用途是什么?,assembly,x86-64,sse,simd,cpu-registers,Assembly,X86 64,Sse,Simd,Cpu Registers,在查看了x86/x64体系结构中的寄存器表之后,我注意到有一整段128、256和512位寄存器,我从未见过它们被用于汇编或反编译的C/C++代码:XMM(0-15)代表128,YMM(0-15)代表256,ZMM(0-31)512 在做了一些挖掘之后,我收集到的是,为了对128位数字执行数学运算,必须使用2个64位运算,而不是使用泛型的add,sub,mul,div运算。如果是这种情况,那么拥有这些扩展寄存器集的具体用途是什么?您是否可以使用任何汇编操作来操作它们?这些寄存器是SSE、AVX和A
在做了一些挖掘之后,我收集到的是,为了对128位数字执行数学运算,必须使用2个64位运算,而不是使用泛型的
add
,sub
,mul
,div
运算。如果是这种情况,那么拥有这些扩展寄存器集的具体用途是什么?您是否可以使用任何汇编操作来操作它们?这些寄存器是SSE、AVX和AVX512指令集扩展的一部分。您的C编译器应该至少将它们中较低的64位用于ABI中指定的浮点操作
这些寄存器是SIMD(单指令多数据)寄存器,主要用于高性能代码。处理器支持特殊的SIMD指令,可同时处理多个数据,处理单个数据所需的时间与通常所需的时间相同。大多数使用这些寄存器的代码都是在汇编程序中编写的,或者使用特殊的内在函数,因为编译器不善于单独使用SIMD指令。使编译器在这方面做得更好(一种称为自动矢量化的优化)是一个活跃的研究领域
例如,假设一个程序要对双精度浮点数进行矩阵乘法。使用AVX寄存器ymm0到ymm15,一次可以处理4个数字,与正常实现相比,算法速度提高了4倍。那是相当不同的
有关使用这些寄存器的指令,请参阅指令集参考。以可访问的方式列出所有这些选项。如果您想使用它们,我建议您使用内部函数,因为它们比汇编更易于使用。这些函数在
- 浮点运算
- 一次对多个数据执行操作
关于它们的用法,首先它们用于标量浮点操作。因此,如果在C或C++中有<代码>浮点<代码>或<代码>双< /代码>,那么它们很可能被存储在XMM寄存器的低端,并由在<代码> SS <代码>(标量单)或<代码> SD(标量双)中的指令操作。 事实上,还有另外一组80位
ST(x)
寄存器,可用于进行浮点运算。然而,它们很慢,而且不太可预测。速度慢,因为默认情况下操作的精度更高,这本身就需要更多的工作,必要时也需要更多的工作。不可预测也是因为高精度。这一点一开始可能会感到奇怪,但很容易解释,例如,某些操作在float
或double
精度中溢出或下溢,但在long double
精度中则不然。这会在32位和64位build1中导致许多错误或意外结果
转向更快、更一致的SSE寄存器是原因之一
然后英特尔推出了for操作,它使用相同的
ST(x)
寄存器,并使用新名称MMX
。MMX可能代表多重数学扩展或矩阵数学扩展,但IMHO最有可能是多媒体扩展,因为多媒体和互联网在当时变得越来越重要。在多媒体解决方案中,您通常必须对每个像素、纹理、声音样本执行相同的操作。。。像这样
for (int i = 0; i < 100000; ++i)
{
A[i] = B[i] + C[i];
D[i] = E[i] * F[i];
}
for(int i=0;i<100000;++i)
{
A[i]=B[i]+C[i];
D[i]=E[i]*F[i];
}
我们可以通过一次执行多个元素来提高速度,而不是单独对每个元素进行操作。这就是人们发明SIMD的原因。使用MMX,您可以同时增加8个像素通道的亮度,或四个16位声音采样的音量。。。调用单个元素上的操作,完整寄存器称为向量,向量是一组标量值
由于MMX的缺点(如重复使用ST
寄存器,或缺乏浮点支持),在使用Intel扩展SIMD指令集时,决定为它们提供一组全新的寄存器,名为XMM,其长度为两倍(128位),因此现在我们可以一次操作16字节。它还支持同时执行多个浮点操作。然后Intel在中将XMM延长到256位YMM,并在中再次将长度增加了一倍(这一次它还将64位模式下的寄存器数量增加到32个)。现在您可以一次处理16个32位整数
通过以上内容,您可能了解了这些寄存器的第二个也是最重要的作用:使用一条指令并行地对多个数据执行操作。例如,在一组已介绍的。现在您可以计算字符串长度,查找子字符串。。。一次检查多个字节的速度要快得多。您还可以更快地复制或比较内存。现代memcpy
实现根据最大寄存器宽度一次移动16、32或64字节,而不是像最简单的C解决方案那样逐个移动
不幸的是,编译器在将标量代码转换为并行代码方面仍然很差,因此大多数时候我们必须帮助他们,尽管自动矢量化仍然在变得更好、更智能
for (int i = 0; i < 100000; ++i)
{
A[i] = B[i] + C[i];
D[i] = E[i] * F[i];
}