引入“向量调用约定”

在VS2013中(下载 在这里 ), 我们引入了一个新的呼叫约定,称为 ‘向量调用约定’ 但在我们继续介绍新的呼叫会议之前,让我们先看看今天的情况。

null

多个调用约定 今天已经存在,特别是在x86平台上。然而,x64平台只有一个约定。 Visual C/C++编译器支持以下调用约定 (u cdecl、u clrcall、u stdcall、u fastcall和其他) ) 在x86上。 _cdecl公司 是x86上C/C++程序的默认调用约定。但是x64只使用 _快速呼叫 电话会议。

类型u m64、u m128和u m256分别定义了适合64位mmx寄存器、128位xmm寄存器和256位ymm寄存器的SIMD数据类型。目前,Microsoft编译器部分支持这些类型。

让我们看一个例子来理解这(即部分支持)的真正含义:

图片[1]-引入“向量调用约定”-yiteyi-C++库 图1:参数在x64上通过引用隐式传递。

今天在AMD64目标上,按值传递的向量参数(如uum128/uuum256/)必须转换为临时缓冲区(即上图中的$T1,$T2,$T3)的按值传递地址,该地址分配在调用方的本地堆栈中,如上图所示。在过去的几年里,我们越来越关注这种低效率,特别是在游戏、图形、视频/音频和编解码器领域。一个具体的例子是msxna库,其中传递向量参数是XNAMath库的许多api中的常见模式。在即将推出的AVX2/AVX-512处理器和具有更宽矢量寄存器的未来处理器上,效率低下的问题将更加严重。

在X86上,约定稍微高级一点,在XMM0:XMM2寄存器中传递前3个通过值向量传递的参数。但是,不允许使用第四个或更大的向量参数,这将导致 C2719型 错误。今天的开发人员被迫手动将其转换为passed by reference参数,以绕过X86的限制。

新的调用约定侧重于利用向量寄存器传递向量类型参数。对于向量调用约定,设计考虑的是避免创建完全不同的约定,并与整数和浮点参数的现有约定兼容。这种设计考虑被进一步扩展,以避免改变堆栈布局或处理填充和对齐。请注意,向量调用约定仅支持本机amd64/x86目标,而且它不适用于MSIL(/clr)目标。

新的呼叫约定可以通过以下两种方式触发:

  • _矢量调用 :使用新的 _矢量调用 关键字来控制特定函数的调用约定。例如,请看下面的图2: 图片[2]-引入“向量调用约定”-yiteyi-C++库

图2: ‘矢量调用’ 表示使用向量调用约定

  • 另一种使用向量调用约定的方法是指定/Gv编译器开关。使用/Gv compiler选项会导致模块中的每个函数编译为vectorcall,除非该函数是用冲突的属性声明的,或者函数名是main。

除SIMD数据类型外,向量调用约定还可用于同构向量聚合数据类型(HVA)和同构浮点聚合数据类型(HFA)。HVA/HFA数据类型是一种复合类型,其中组成该类型的成员的所有基本数据类型都是相同的,并且是向量或浮点数据类型(__m128,uu m256,浮动/双)。一个HVA/HFA数据类型最多可以有四个成员。下面列出了构成HVA/HFA数据类型的一些示例。

图片[3]-引入“向量调用约定”-yiteyi-C++库 图3:HVA/HFA示例

对于这两种体系结构(x86和amd64),HVA/HFA参数将通过 价值 在向量寄存器中,如果未分配的易失性向量寄存器(XMM0:XMM5/YMM0:YMM5)足以容纳整个聚合集 . 它们将以与现有公约相同的方式通过引用传递。  HVA/HFA类型的返回值通过XMM0(/YMM0):XMM3(/YMM3)返回,每个元素一个寄存器。

现在我们了解了向量调用约定的真正含义。让我们看看下图中使用向量调用约定进行的反汇编。

图片[4]-引入“向量调用约定”-yiteyi-C++库

如您所见,由于使用“向量调用约定”而生成的“反汇编”被简化了。下面显示有无向量调用约定的指令数。

图片[5]-引入“向量调用约定”-yiteyi-C++库 除了保存的指令数之外,还有通过使用向量调用约定保存的堆栈分配(96字节,分配$T1、$T2和$T3),这增加了一般的优点,从而提高了性能。

总结

这个博客应该向你介绍向量调用约定的意义。正如您所观察到的,如果您在代码中执行大量向量计算(尤其是在x64平台上),那么使用这种约定有很多好处。在不改变源代码的情况下使用向量调用约定来验证性能增益的一种快速方法是使用/Gv编译器开关。在这一点上,你应该有一切你需要开始! 此外,如果您想让我们在博客上介绍一些其他的编译器技术,请让我们知道我们总是有兴趣从您的反馈中学习。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享