通过直接内联代码加快继承

本文关键字:代码 继承 | 更新日期: 2023-09-27 18:28:00

我已经想出了一种方法(不一定是新的)来替换switch语句的虚拟表。这种方法允许以增加内存为代价内联虚拟函数。

取而代之的是使用表格查找,使用一种开关

switch (objecttype)
{
   case objectA: inlined virtual function call for foo from objectA; break;
   case objectB: inlined virtual function call for foo from objectB; break;
   case objectC: inlined virtual function call for foo from objectC; break;
   default: vtable call;
}

因此,不使用指针查找和调用,而是进行比较。可以对已知类进行代码嵌入。

为了使其正常工作(避免不仅仅是函数调用),对象需要存储其类型。类型需要按顺序排列。

例如:

class A
{
    ushort objectType; // internal id, say for class A it is 1000
    ushort objectInc; // internal. represents a sort of offset into the jump table
}
class B : A
{
    ushort objectInc; // one more than A's objectInc, has the same objectType
}
etc...

然后,可以将switch语句制作成一个有效的跳转表,在objectType上进行比较(确保它是正确的),并使用objectInc和代码大小直接跳转到虚拟函数的代码(而不是一堆比较)。

据我所知,这种方案的缺点是内存更多(类更大,内联函数更多),编译器更复杂,但虚拟函数可以直接内联(整个switch语句可以),因此没有包装调用。由于一些比较和跳跃(即O(1)),唯一的额外开销应该只是额外的几个周期。

有人对这样一个方案的性能以及为什么不使用它有任何有用的评论吗(我相信我不是第一个想到这个的人)?我认为它会非常高效,除了可能由于比较而导致缓存无效之外,但我认为对于基类来说,它可以在直接内联方法调用的几个周期内完成。

顺便说一句,该表可以看作是每个对象派生的内联虚拟函数调用的列表。

假设我们有以下内容:

class A
{
    void foo();
}
class B : A
{
    override void foo();
}
class C : A
{
    override void foo();
}

A a = new C();
a.foo();         // but calls fooWrap

/// internal
void fooWrap(A a)
{
    switch(a.Type)
    {
       case A: a.foo(); break; // A.foo() can be inlined here
       case B: b.foo(); break; // B.foo() can be inlined here
       case C: c.foo(); break; // C.foo() can be inlined here
       default: // use vtable lookup, a's type is not known at compile time
    }
}

(通常fooWrap将是vtable查找)

现在fooWrap也可以直接内联,在这种情况下,对foo的调用成本只是switch语句,可以通过使用有效的跳转列表来进一步优化它。

通过直接内联代码加快继承

我认为这效率较低,因为它需要比较或跳转表或其他什么,而通过vtable的间接方法调用速度很快:vtable中的偏移量可以在调用中硬编码,间接方法调用在大多数处理器上都可以作为直接机器操作使用。

此外,每次向系统中添加另一个子系统时,您的方法都需要重新编译。因此,对于像Java或.net这样的系统,即使在应用程序运行时也允许从互联网加载代码,在运行时可能需要重新编译一些代码。老实说,这已经是为了撤销一些优化,但这只是你必须这样做的又一种情况

关于"对象必须存储其类型":在.net和Java中,情况已经是这样了:每个对象都包含一个指向其类定义的指针,其中包含vtable等信息。因此,每个类只有一个vtable,而不是每个对象。

Smart Eiffel使用了一种描述非常相似的技术,大约80%的虚拟函数都是内联的。该方法不允许vtable调度作为默认值,这会阻止动态链接,因此这可能是一个更适合一般用途的想法。

http://smarteiffel.loria.fr/papers/oopsla97.pdf