根据支持的说明选择要使用的程序集实现

时间:2018-11-28 02:19:36

标签: c++ c assembly static-libraries static-linking

我正在使用一个C库,该库编译/链接到.a文件,用户可以将其静态链接到其代码中。库的性能非常重要,因此我正在x86-64程序集中编写对性能至关重要的例程,以优化性能。

对于某些例程,如果我使用BMI2指令,则比使用“标准” x86-64指令集要好得多。麻烦的是,BMI2是最近才推出的,我的一些用户使用的处理器不支持这些指令。

因此,我编写了优化例程两次,一次使用BMI2指令,一次不使用它们。在我当前的设置中,我将分发.a文件的两个版本:一个“快速”版本,它需要支持BMI2指令,一个“缓慢”版本,它不需要支持BMI2指令。

我在问是否有一种方法可以通过分发单个.a文件来简化此操作,该文件将根据最终应用程序运行的CPU 是否支持BMI2动态选择正确的实现。说明。

与StackOverflow上的类似问题不同,这里有两个特点:

  • 选择功能的技术在关键路径上的开销应特别低。经过优化的程序集在汇编优化后大约需要10 ns的时间运行,因此即使单个if语句也可能很重要。
  • 需要“动态”选择的功能在开始时选择一次,然后在程序执行期间保持不变。我希望这将提供比此问题中建议的解决方案更快的解决方案:Choosing method implementation at runtime

到目前为止,我想出的最快的解决方案是执行以下操作:

  1. 使用cpuid指令检查CPU是否支持BMI2指令。
  2. 根据结果设置全局变量truefalse
  3. 在每次函数调用时分支此全局变量的值。

我对这种方法不满意,因为它有两个缺点:

  • 由于我要分发cpuid文件,因此我不确定如何自动运行.a并在程序开始时设置全局变量并且无法控制最终二进制文件中的main函数。 只要能仍与C程序链接和从其调用最终库,我很乐意在这里使用C ++,因为它提供了更好的解决方案。
  • 这会在每次 函数调用时产生开销,而理想情况下,唯一的开销是在程序启动时。

有没有比我上面详细介绍的解决方案更有效的解决方案?

2 个答案:

答案 0 :(得分:3)

x264使用init函数(在调用任何其他命令或类似操作之前,必须先调用库的用户)来基于CPUID结果设置函数指针的结构。包括考虑到pshufb在某些支持它的早期CPU上运行缓慢。

如果您的功能依赖于pdep / pext,则您可能想检测AMD与Intel,因为AMD的pdep / pext非常慢,可能不值得即使在Ryzen上也可以使用。 (有关说明表,请参见https://agner.org/optimize/。)


函数指针的开销很低,大约与在共享库或DLL中调用函数相同。 call [rel funcptr],而不是call func。 (在由编译器生成的调用您函数的asm中。)

CPU dependent code: how to avoid function pointers?在C中显示了一个非常简单的示例,并正在寻求避免的方法。借助动态链接,您可以在动态链接时进行CPU检测,因此动态链接间接也将成为您的CPU派发间接(就像glibc一样,用于选择优化的memcpy实现)。

但是使用.a的静态链接,只需将函数指针静态初始化为基准版本,然后您的CPU初始化函数(希望在取消引用任何函数指针之前运行)将它们重写为指向为当前CPU的最佳版本。

答案 1 :(得分:1)

如果使用的是gcc,则可以让编译器自动实现所有样板代码。 gcc manual page on function multiversioning