gpt4 book ai didi

c++ - 为什么需要虚拟 thunk?

转载 作者:行者123 更新时间:2023-12-01 14:44:39 31 4
gpt4 key购买 nike

这个问题是关于虚函数调用的(可能的)实现(我相信它被 gcc 使用)。

考虑以下场景:

  1. F 类继承自 D 类(可能还有其他类),而 D 类继承自 B 类(并非虚拟)。 D重写了B中声明的虚方法f();实例化类型 F 的对象
  2. F 类继承自 D 类(可能还有其他类),D 类继承自 B 类(虚拟地)。 D重写了B中声明的虚方法f();实例化类型 F 的对象

(这两种场景唯一的区别是类B的继承方式不同)

在场景 1 中,在对象 B 的 vtable 中,在指定给 f() 的位置现在有一个(非虚拟)thunk 说:

if you want to call f(), first change the this pointer with offset

(实际上是 D 把这个 thunk 放在那里)

在场景 2 中,在对象 B 的 vtable 中,在指定给 f() 的位置现在有一个(virtual)thunk 说:

if you want to call f(), first change the this pointer with the value stored at addr

(D无法准确地告诉B需要调整多少this指针,因为它不知道B对象在F对象的最终内存布局中的位置)

这些假设是通过查看 g++ -fdump-class-hierarchy 结合 g++ -S 的输出做出的。它们正确吗?

现在我的问题是:为什么需要一个virtual thunk?为什么 F 不能将 non-virtual thunk 放入 B 的虚拟表中(在 f() 的位置)?因为当一个F对象需要被实例化时,编译器知道f()是在B中声明的,但是在D中被重写了。而且它还知道对象B之间的确切偏移量(-in -F) 和对象 D (-in-F)(我认为这首先是 virtual thunk 的原因)。

编辑(添加了 g++ -fdump-class-hierarchyg++ -S 的输出)

场景 1:

g++ -fdump-class-hierarchy:

Vtable for F

...

48 (int (*)(...))D::_ZThn8_N1D1fEv (de-mangled: non-virtual thunk to D::f())

g++ -S:

_ZThn8_N1D1fEv:

.LFB16:

.cfi_startproc

subq $8, %rdi #,

jmp .LTHUNK0 #

.cfi_endproc

场景 2:

g++ -fdump-class-hierarchy:

Vtable for F

...

64 (int (*)(...))D::_ZTv0_n24_N1D1fEv (de-mangled: virtual thunk to D::f())

g++ -S:

_ZTv0_n24_N1D1fEv:

.LFB16:

.cfi_startproc

movq (%rdi), %r10 #,

addq -24(%r10), %rdi #,

jmp .LTHUNK0 #

.cfi_endproc

最佳答案

我想我找到了答案 here :

"...There are several possible implementations of the thunks given the above information. Note in the following that we assume that prior to calling any vtable entry, the this pointer has been adjusted to point to the subobject corresponding to the vtable from which the vptr is fetched.

A. Since the offsets are always known at compile time, even for virtual bases, each thunk could be distinct, adding the known offset to this and branching to the target function. This would result in a thunk for each overrider at a distinct offset. As a result, a branch mispredict and possibly an instruction cache miss would occur each time the actual type changed for a reference at any given point in the code.

B. In the case of virtual inheritance, the offset, although known when the overrider is declared, may differ depending on derivations from the overrider's class. H and I above are the simplest example. H is a primary base for I, but the int member of I means that A is at a different offset from H in I than it was from a standalone H. Because of this, the ABI specifies that the secondary vtable for a virtual base A contain a vcall offset to H, so that a shared thunk can load the vcall offset, adding it to this, and branch to the target function H::f. This would result in fewer thunks, since for a inheritance hierarchy where A is a virtual base of H, and H::f overrides A::f, all instances of H in a larger hierarchy can use the same thunk. As a result, these thunks will cause fewer branch mispredictions and instruction cache misses. The tradeoff is that they must do a load before the offset add. Since the offset is smaller than the code for a thunk, the load should miss in cache less frequently, so better cache miss behavior should produce better results in spite of the 2 or more cycles required for the vcall offset load...."

似乎虚拟 thunk 的存在只是出于性能原因。如果我说错了,请指正。

关于c++ - 为什么需要虚拟 thunk?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44397573/

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