C++ GCC和Clang都不会通过编译时已知的函数指针数组内联调用;为什么?
编译器资源管理器上的示例代码: 我试图使用函数指针数组作为跳转表,而不是开关,因为我发现它更干净。然而,令我惊讶的是,无论是GCC还是clangcompiler似乎都不能内联这个 有没有具体的原因 死链接情况下的示例代码:C++ GCC和Clang都不会通过编译时已知的函数指针数组内联调用;为什么?,c++,gcc,C++,Gcc,编译器资源管理器上的示例代码: 我试图使用函数指针数组作为跳转表,而不是开关,因为我发现它更干净。然而,令我惊讶的是,无论是GCC还是clangcompiler似乎都不能内联这个 有没有具体的原因 死链接情况下的示例代码: namespace{ template<int N> int bar(){ return N; } int foo1(int n){ if(n < 0 || n > 5){ __builtin_unreachable
namespace{
template<int N>
int bar(){
return N;
}
int foo1(int n){
if(n < 0 || n > 5){
__builtin_unreachable();
}
#if __clang__
__builtin_assume(n >= 0 && n <= 5);
#endif
static int (* const fns[])() = {
bar<0>, bar<1>, bar<2>, bar<3>, bar<4>, bar<5>
};
return fns[n]();
}
int foo2(int n){
#if __clang__
__builtin_assume(n >= 0 && n <= 5);
#endif
switch(n){
case 0:
return bar<0>();
case 1:
return bar<1>();
case 2:
return bar<2>();
case 3:
return bar<3>();
case 4:
return bar<4>();
case 5:
return bar<5>();
default:
__builtin_unreachable();
}
}
}
int main(int argc, char** argv){
volatile int n = foo1(argc);
volatile int p = foo2(argc);
}
名称空间{
模板
整型条(){
返回N;
}
int foo1(int n){
如果(n<0 | | n>5){
__内置的不可访问();
}
#如果叮当作响__
__内置\u假设(n>=0&&n=0&&n编译器无法在foo1中内联调用,因为调用未使用编译时常量被调用方。如果编译器知道在编译时通过内联将常量参数传递给foo1,它将内联正确的函数
考虑这个例子:
namespace{
template<int N>
int bar(){
return N;
}
int foo1(int n){
if(n < 0 || n > 5){
__builtin_unreachable();
}
#if __clang__
__builtin_assume(n >= 0 && n <= 5);
#endif
static int (* const fns[])() = {
bar<0>, bar<1>, bar<2>, bar<3>, bar<4>, bar<5>
};
return fns[n]();
}
}
int main(int argc, char** argv){
int n = foo1(3);
return n;
}
在foo2的例子中,编译器从5个不同的调用开始,使用常量被调用方,所有这些调用都是内联的。然后它进一步优化生成的代码,如果它认为有利,则生成自己的跳转表
我猜编译器可以尝试从跳转表中提取一个开关,然后内联所有内容,但这将非常复杂,在一般情况下不太可能产生性能改进,因此gcc和clang似乎都不能做到这一点。查找表的平均效率高于处理器t上的5个测试和分支操作hat可能支持流水线?这可能是我在这方面的知识不足,但您希望函数内联到哪里?您专门设置了函数指针,指向要在运行时解析的函数。在开关情况下,开关解析所需的操作,函数内联到案例代码块中。@dani否,你会用常数时间查找来换取对数。你会用下降。查找表只需要一次内存提取。它们非常便宜,特别是当目标代码已经在缓存中时。@dani上述两种方法都相当于使用向量。如果你查看优化的汇编程序输出,你会看到这一点。如果你删除foo1代码,它确实变成了一个查找表。如果我们通过添加足够的constexpr来计算n的值,它就会得到优化。因此,我怀疑只是缺少一些优化过程或一些意外的排序。
main:
mov eax, 3
ret