使用新的CPU指令支持构建向后兼容的二进制文件

使用新的CPU指令支持构建向后兼容的二进制文件,c,gcc,x86,compiler-optimization,inline-assembly,C,Gcc,X86,Compiler Optimization,Inline Assembly,实现使用特定CPU指令的同一函数的多个版本(如果在运行时测试可用)的最佳方法是什么?如果不可用,则返回较慢的实现 例如,x86 BMI2提供了一条非常有用的指令。我如何编写一个C代码,以便它在启动时测试执行CPU的BMI2可用性,并使用两种实现中的一种——一种使用-mbmi2提供的_pdep_u64调用,另一种使用C代码手动执行位操作。这种情况是否有内置的支持?我如何使GCC在为较旧的arch编译的同时提供对较新的内部版本的访问?我怀疑如果通过全局函数指针调用函数,而不是每次调用if/else,

实现使用特定CPU指令的同一函数的多个版本(如果在运行时测试可用)的最佳方法是什么?如果不可用,则返回较慢的实现


例如,x86 BMI2提供了一条非常有用的指令。我如何编写一个C代码,以便它在启动时测试执行CPU的BMI2可用性,并使用两种实现中的一种——一种使用-mbmi2提供的_pdep_u64调用,另一种使用C代码手动执行位操作。这种情况是否有内置的支持?我如何使GCC在为较旧的arch编译的同时提供对较新的内部版本的访问?我怀疑如果通过全局函数指针调用函数,而不是每次调用if/else,执行速度会更快。

您可以声明函数指针,并在程序启动时通过调用cpuid确定当前体系结构,将其指向正确的版本

但最好利用许多现代编译器的支持。英特尔的ICC很久以前就必须为每种体系结构选择优化版本。我不知道细节,但看起来它只适用于英特尔的库。此外,它只发送到英特尔CPU上的高效版本,因此将是。在中有许多补丁和解决方法

后来,在中引入了一个名为的功能。它添加了将在函数的每个版本上声明的目标属性

__attribute__ ((target ("sse4.2")))
int foo() { return 1; }

__attribute__ ((target ("arch=atom")))
int foo() { return 2; }

int main() {
    int (*p)() = &foo;
    return foo() + p();
}
这会复制大量代码,而且很麻烦,因此GCC6添加了target_克隆,告知GCC将函数编译为多个克隆。例如_属性_目标_clonesavx2,arch=atom,默认void foo{}将创建3个不同的foo版本。有关它们的更多信息,请参阅

该语法随后被和采用。性能甚至可以比全局函数指针更好,因为它不是运行时。这是跑步的原因之一。ICC也可以在自动矢量化过程中创建

这里有一个例子,是关于popcnt的,但是你明白了

__attribute__((target_clones("popcnt","default")))
int runPopcount64_builtin_multiarch_loop(const uint8_t* bitfield, int64_t size, int repeat) {
    int res = 0;
    const uint64_t* data = (const uint64_t*)bitfield;

    for (int r=0; r<repeat; r++)
    for (int i=0; i<size/8; i++) {
        res += popcount64_builtin_multiarch_loop(data[i]);
    }

    return res;
}

请注意,它们应该只在英特尔上启用

看看操作系统内核是如何做到这一点的提示:对于x86来说,它有大量的功能是基于cpuid指令返回的。popcount64_builtin_multiarch_loop对于u builtin_popcountll周围的包装器来说是一个非常奇怪的名字,因为它只是一个包装器,不包含循环。也不需要使用包装器函数,它仍然直接在循环中使用内置的popcountll进行编译。您可能希望包装它以便于移植到非GNU编译器或其他任何用例,例如使用std::bitset::count。但这对于这个例子来说是多余的,它只在GNU C/C++中工作,即GCC或CLAN。并用ICC编译,但没有用popcnt制作版本。我也不知道为什么它是这样包装的。可能作者认为移植到其他编译器更容易?ICC:。哦,而且clang也不是多版本的,但是它可以用SSE2自动矢量化。与popcnt相比,可能不太有利,但可能与其标量回退比特黑客相比。无论如何,clang10.0说的是未知属性“target_clones”,所以对于使用这种紧凑语法的clang来说还不是。在第一个例子中,如果我在一个与两个属性都不匹配的平台上调用foo会发生什么情况?@Yurik我没有检查过,但通常你必须提供一个默认版本