gpt4 book ai didi

c++ - 类成员函数的函数指针与任意函数的指针之间的区别

转载 作者:行者123 更新时间:2023-12-02 09:51:40 32 4
gpt4 key购买 nike

我正在尝试测试最快的方法来调用函数指针,以绕过有限数量的参数的模板。我写了这个基准:https://gcc.godbolt.org/z/T1qzTd
我注意到,指向类成员函数的函数指针增加了很多我难以理解的开销。我的意思是:
使用结构条和函数foo定义如下:

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
第一个选项(godbolt代码中的 #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,%rdi), %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)
我试图理解为什么直接使用指向类成员函数的函数指针的 DO_DIRECT方法有太多绒毛,以及如何(如果可能的话)更改它以消除绒毛。
注意:我有 __attribute__((noinline))只是为了使其更容易检查程序集。
谢谢。
附言:如果有一种更好的将运行时参数转换为模板参数的方法,我希望在示例/联机帮助页中找到一个链接。

最佳答案

C++指向成员函数的指针必须能够指向非虚函数或虚函数。在典型的vtable / vptr实现中,调用虚拟函数涉及从对象表达式中的vptr查找正确的代码地址,并可能将偏移量应用于对象参数地址。
g++使用了Itanium ABI,因此foo_direct的程序集正在按照section 2.3中的描述解释所访问的指针到成员函数的值。如果函数是虚拟的,它将通过对象表达式的vptr查找代码地址;如果不是虚拟的,则仅从指针到成员的值中复制代码地址。
我想如果一个优化可以看到类类型没有虚拟函数并且是final,那么它也许可以跳过虚拟函数调用逻辑。不过,我不知道g++或其他编译器是否进行了任何此类优化。

关于c++ - 类成员函数的函数指针与任意函数的指针之间的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64093449/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com