根据支持的指令选择要使用的程序集实现

Choose assembly implementation to use based on supported instructions

我正在开发一个 C 库,它 compiles/links 到一个 .a 文件,用户可以静态地 link 到他们的代码中。该库的性能非常重要,因此我正在 x86-64 汇编中编写性能关键例程以优化性能。

对于某些例程,如果我使用 BMI2 指令,我可以获得比我坚持使用 "standard" x86-64 指令集更好的性能。问题是,BMI2 是最近才推出的,我的一些用户使用的处理器不支持这些指令。

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

我问是否有一种方法可以通过分发单个 .a 文件来简化此过程,该文件将根据 CPU 应用程序 运行s 支持 BMI2 指令。

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

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

  1. 检查CPU是否支持使用cpuid指令的BMI2指令
  2. 根据结果设置全局变量truefalse
  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 中显示了一个非常简单的示例,并寻求 避免 它的方法。使用动态 linking,您可以在动态 link 时间进行 CPU 检测,因此动态-linking 间接也成为您的 CPU-dispatch 间接(就像 glibc 选择优化的 memcpy 实现一样。)

但是对于 .a 的静态 linking,只需创建静态初始化为基线版本的函数指针,以及您的 CPU 初始化函数(希望在任何之前运行的函数指针被取消引用)重写它们以指向当前 CPU.

的最佳版本

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