物理处理器寄存器与英特尔内在函数中使用的变量(例如__m128)之间有什么关系?
解释SIMD的图表通常显示2个寄存器,但英特尔forums上的参考“注册压力”,并且在此question中“注册着色”表示还有更多的事情发生。
是否可以声明代表寄存器的任意数量的变量?当它们与有限的物理资源紧密联系时,它又怎么可能呢?关于如何选择物理寄存器,应该注意什么?如果声明的寄存器多于存在会发生什么?
多个寄存器对可以同时激活吗?
是否有不同类型的物理寄存器?
答案 0 :(得分:2)
_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
),这反过来会生成相应的指令版本。
类似"和"这是一个很好的例子,因为无论类型如何,结果操作都是相同的 - 按位"和"。但根据我在英特尔文档中所读到的内容,使用错误的类型可能会导致延迟。整数指令和浮点指令具有单独的执行队列,并且每当数据必须从一个执行队列移动到另一个执行队列时,就会受到惩罚。因此,通常最好选择适当的数据类型,以便生成适当的指令,并保持在该数据类型的范围内。