Gcc 重磅特性介绍: function multiversionning


#1

今天给大家介绍一个 gcc 重量级特性。

function multiversion

function multiversion 是一个重量级的特性。很多时候我们写程序,需要针对 CPU 平台优化。对不同的 CPU 选择执行不同的代码。比如有 SSE 的,跑 SSE 优化的版本,有 AVX 的就跑 AVX 优化的版本。 你们还在用 low 掉牙的 if else 来选择路径么?

默默:额? 你有更好的选择么

当然!!!

默默 show me

low 的是 if else 选择,稍微高级点的就是启动的时候初始化一个跳转表。然后直接调用跳转表里的函数指针。 这些其实都是 low 的做法。

正确的做法是使用 gcc 提供的 function multi version 功能。 使用方法很简单。 就是写 N 个同名的函数,然后标记为不同的 CPU 平台。就可以了。 然后使用的地方就直接调用就可以了。 gcc 自动生成运行时跳转表,根据 cpu 平台自动执行对应的函数。

标记方法就是 attribute((target(“default”))

这个用于标记默认函数。一般在这里写 C 实现。 然后写几个同名的,标记为 attribute((target(“sse4.2”))

然后写几个同名的,标记为 attribute((target(“sse4.1”))

然后写几个同名的,标记为 attribute((target(“sse4.2”)) 然后写几个同名的,标记为 attribute((target(“avx”))

然后写几个同名的,标记为 attribute((target(“avx2”))

就可以了。

用的地方就直接调用就可以了。 你只要知道在运行时, 是会自动执行对应平台的代码的。

用法就在这里了。 这里就用了 gcc 的 function multi version 实现在运行时自动根据 CPU 是否支持 SSE4.2 指令选择软件 crc32 实现还是 SSE 汇编优化的版本。 使用 gcc 的 function multi version 好处有3个

  • 第一,方便,简单,快捷。不需要自己调用 CPUID 判定 cpu 类型。
  • 第二,标记为 target(sse4.2) 的函数,哪怕编译参数没有开 SSE , 对应的函数都会启用 SSE 指令,因此对应的函数甚至可以完全不需要手写汇编,直接原样把 C 版本抄下来。gcc 就会为同样的 C 代码生成没有 SSE 的结果和有 SSE 的结果。
  • 第三,gcc 生成的跳转是通过在运行时直接修改代码实现的,并不是函数表跳转,不会损失性能。这个要比你自己用 if else 或者 函数指针要高效。

原理是:gcc 会生成一个 patch 代码。 这个代码在 main() 执行前被运行时的代码调用。这个 patch 代码通过调用 cpuid 判定好 cpu 平台特性,然后直接把调用处的地址给改了。 所以不存在间接调用损失。

gcc 的这个功能被 glibc 这样的低层库大量使用哦!各种 memcpy strcpy sqrt … 函数都是用这招在运行时自动选择好相应的版本。而且还没有间接跳转带来的损失。


PS 这个跳转代码修正机制其实是 glibc 实现的,叫 IFUNC


#2

这特性不错,然而对我来说没什么卵用,一般不会有对性能要求这么高的场景


#3

不错,不过没做那么底层,后期性能优化应该可以试试