如何解决类成员函数的函数指针与任意函数的指针之间的区别
我正在尝试测试最快的方法来调用函数指针以绕过有限数量的参数的模板。我写了这个基准测试:https://gcc.godbolt.org/z/T1qzTd
我注意到指向类成员函数的函数指针增加了很多我难以理解的开销。我的意思是:
具有如下定义的结构条和函数foo:
body
第一个选项(在Godbolt代码的GET
中)通过索引指向定义为的类成员函数的函数指针数组来调用模板化函数
template<uint64_t r>
struct bar {
template<uint64_t n>
uint64_t __attribute__((noinline))
foo() {
return r * n;
}
// ... function pointers with pointers to versions of foo below
但是,此组件似乎有很多绒毛:
#define DO_DIRECT
我在理解上有困难。
相反, /* all of this inside of struct bar */
typedef uint64_t (bar::*foo_wrapper_direct)();
const foo_wrapper_direct call_foo_direct[NUM_FUNCS] = {
&bar::foo<0>,// a bunch more function pointers to templated foo...
};
// to call templated foo for non compile time input
uint64_t __attribute__((noinline)) foo_direct(uint64_t v) {
return (this->*call_foo_direct[v])();
}
方法定义为:
bar<9ul>::foo_direct(unsigned long):
salq $4,%rsi
movq 264(%rsi,%rdi),%r8
movq 256(%rsi,%rax
addq %rdi,%r8
testb $1,%al
je .L96
movq (%r8),%rdx
movq -1(%rdx,%rax),%rax
.L96:
movq %r8,%rdi
jmp *%rax
具有一些非常简单的汇编:
#define DO_INDIRECT
我试图理解为什么直接使用指向类成员函数的函数指针的// forward declare bar and call_foo_wrapper
template<uint64_t r>
struct bar;
template<uint64_t r,uint64_t n>
uint64_t call_foo_wrapper(bar<r> * b);
/* inside of struct bar */
typedef uint64_t (*foo_wrapper_indirect)(bar<r> *);
const foo_wrapper_indirect call_foo_indirect[NUM_FUNCS] = {
&call_foo_wrapper<r,0>
// a lot more templated versions of foo ...
};
uint64_t __attribute__((noinline)) foo_indirect(uint64_t v) {
return call_foo_indirect[v](this);
}
/* no longer inside struct bar */
template<uint64_t r,uint64_t n>
uint64_t
call_foo_wrapper(bar<r> * b) {
return b->template foo<n>();
}
方法具有如此多的绒毛,以及如何(如果可能的话)对其进行更改以消除绒毛。
注意:我使用bar<9ul>::foo_indirect(unsigned long):
jmp *(%rdi,%rsi,8)
只是为了简化检查程序集。
谢谢。
p.s,如果有更好的方法可以将运行时参数转换为模板参数,我希望在示例/联机帮助页中找到一个链接。
解决方法
C ++成员函数指针必须能够指向非虚拟函数或虚拟函数。在典型的vtable / vptr实现中,调用虚拟函数涉及从对象表达式中的vptr查找正确的代码地址,并可能将偏移量应用于对象参数地址。
g ++使用Itanium ABI,因此foo_direct
的程序集正在按section 2.3中所述解释所访问的指针到成员函数的值。如果函数是虚拟的,它将通过对象表达式的vptr查找代码地址;如果不是虚拟的,则仅从指针到成员的值中复制代码地址。
我认为,如果优化可以看到类类型没有虚拟函数并且为final
,则可能跳过虚拟函数调用逻辑。不过,我不知道g ++或其他编译器是否进行了任何此类优化。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。