使用多版本控制时的编译时 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 检测的主要内容,如果未能解决你的问题,请参考以下文章

HTTP Restful 语义版本控制

如何对前端项目进行版本控制?

git使用时的一下简单命令

解决IntelliJ IDEA控制台乱码问题[包含程序运行时的log4j日志以及tomcat日志乱码]

编译 C++ 代码时如何解决一些版本控制问题?

当有这么多版本控制系统时该怎么办?