使用多版本控制时的编译时 AVX 检测
Posted
技术标签:
【中文标题】使用多版本控制时的编译时 AVX 检测【英文标题】:Compile-time AVX detection when using multi-versioning 【发布时间】:2020-05-17 20:11:37 【问题描述】:我为两种不同的架构编译了相当大的函数:
__attribute__ ((target ("arch=broadwell"))) void doStuff()
doStuffImpl()
__attribute__ ((target ("arch=nocona"))) void doStuff()
doStuffImpl();
__attribute__((always_inline)) void doStuffImpl()
(...)
我知道这是进行多版本控制的旧方法,但我使用的是 gcc 4.9.3。实际上 doStuffImpl() 不是单个函数,而是一堆内联函数,其中 doStuff() 是最后一个实际函数调用,但我认为它不会改变任何东西。
函数包含一些由编译器自动矢量化的代码,但我还需要在那里添加一些手工制作的内在函数。显然是两种不同的口味。 问题是:如何在编译时识别哪些 SIMD 扩展可用? 我正在尝试类似:
#ifdef __AVX2__
AVX_intrinsics();
#elif defined __SSE4.2__
SSE_intrinsics();
#endif
但似乎定义来自“全局”-march 标志,而不是来自多版本覆盖的标志。
Godbolt(本质是垃圾,但说明了我的观点)
我可以提取这部分并执行单独的多版本化函数,但这会增加调度和函数调用的成本。 有什么方法可以对函数的两个多版本变体进行编译时区分吗?
【问题讨论】:
#ifdef __AVX2__
使用 C 预处理器(至少在逻辑上)输出其结果以供编译器正确读取。所以__attribute__
的东西不可能影响它。您也许可以传递包装器传递的编译时常量参数,因此 if(use_avx) ... else ...
将得到优化。但是即使在if(false)
分支中,GCC 也可能会拒绝 AVX 内在函数,从而在死代码消除可以摆脱它之前导致编译时错误。 ://
我建议将每个 CPU 目标移动到一个单独的翻译单元,该翻译单元使用相应的编译器标志进行编译。常见的doStuffImpl
函数可以在包含在每个 TU 中的标头中实现。在该标头中,您可以使用 __AVX__
等预定义的宏来测试可用的 ISA 扩展。在这种情况下可以删除__attribute__((target))
属性。
@AndreySemashev:这是否适用于 GCC 的 ifunc
调度,它允许您定义具有相同名称的函数的多个版本?但是,是的,这似乎是个好主意,或者可能是其他 CPP / 宏 / #include
hacks。
@PeterCordes 是的,为什么ifunc
不工作。你编写函数解析器例程,它可以返回它想要的任何函数指针。
【参考方案1】:
如 cmets 中的回答:
我建议将每个 CPU 目标移动到单独的翻译单元,该翻译单元使用相应的编译器标志进行编译。常见的doStuffImpl
函数可以在包含在每个 TU 中的标头中实现。在该标头中,您可以使用 __AVX__
等预定义的宏来测试可用的 ISA 扩展。 __attribute__((target))
属性不再需要,在这种情况下可以删除。
【讨论】:
以上是关于使用多版本控制时的编译时 AVX 检测的主要内容,如果未能解决你的问题,请参考以下文章