物理寄存器和英特尔SIMD变量之间的关系

Relationship between physical registers and Intel SIMD variables?

本文关键字:变量 之间 关系 SIMD 英特尔 寄存器      更新时间:2023-10-16

物理处理器寄存器与英特尔内部函数(如__m128)中使用的变量之间的关系是什么?

解释SIMD的图表通常显示2个寄存器,但英特尔论坛上提到的"寄存器压力"answers"寄存器着色"表明还有更多的问题

可以声明任意数量的表示寄存器的变量吗?当它们与有限的物理资源紧密相连时,这怎么可能呢?关于如何选择物理寄存器,应该注意什么?如果声明的寄存器比存在的寄存器多,会发生什么?

可以同时激活多对寄存器吗?

是否存在不同类型的物理寄存器?

_m128、_m128i、_m128d等变量类型。。。主要是为了保护你。它们确保您不会试图使用标准运算符,如+、-、&、|、==、。。。并确保如果您试图分配错误的类型,编译器将抛出错误。这些类型迫使编译器将自己加载到适当的寄存器(在这种情况下为XMM*)中,但仍然让编译器可以自由选择哪一个,或者如果所有适当的寄存器都被占用,则将它们本地存储在堆栈上。它们还确保在任何时候存储在堆栈上时,它们都保持正确的对齐(在这种情况下是16字节对齐),这样依赖对齐的内在指令就不会导致GPF。

如果您喜欢使用asm构造,您可以将其中一个变量紧密地绑定到物理寄存器:

__m128i myXMM1 asm( "%xmm1" );

但最好让编译器发挥其魔力,为您选择寄存器,以实现更好的优化。

这些变量中的任意数量都可以声明,即使超额预订XMM寄存器存储也可能不会导致使用堆栈空间,只要寄存器的工作集仍然很小。编译器作用域通常会在不再使用某个值时实现,并允许优化器不将其存储回堆栈。有时,您可以通过创建自己的作用域堆栈框架来帮助编译器:

__m128i storedVar;
{
__m128i tempVar1, tempVar2, tempVar3;
// do some operations with tempVar1 -> 3
storedVar = tempVar1;
}
{
__m128i tempVar4, tempVar5, tempVar6, tempVar7, tempVar8;
// do some operations with tempVar4 -> 8
storedVar = tempVar4;
}
return storedVar;

由于变量在闭大括号处超出了作用域,编译器会发现以前包含这些值的寄存器现在已经释放,因此不需要超过可用XMM寄存器的总数。

如果您确实超额预订了寄存器存储,并且需要维护所有值,那么编译器将在堆栈上分配适当的大小,并确保其正确对齐,XMM寄存器的值将交换到堆栈中,为新值腾出空间。请记住,堆栈空间缓存良好,因此写入和读取不会像您预期的那样有害。你真正受到的打击是需要额外的移动操作来交换它们。

按宽度(64位、128位、256位、512位)有不同类型的物理寄存器,显然与相应的C/C++内部数据类型有关。给定宽度("__m128i"、"__m128d"、…)的不同"风味"实际上都可以驻留在给定宽度的任何寄存器中。该类型强制您使用适当的内在类型(例如,_mm_and_si128_mm_and_pd),这反过来又会生成适当版本的指令。

像"and"这样的东西是一个很好的例子,因为无论类型如何,结果操作都是相同的——按位"and"。但根据我在英特尔文档中读到的内容,使用错误的类型可能会导致延迟。整数指令和浮点指令有单独的执行队列,每当数据必须从一个执行队列移动到另一个执行排队时,都会受到惩罚。因此,通常情况下,选择适当的数据类型是一种很好的做法,这样就可以生成适当的指令,并保持在该数据类型的范围内。