【发布时间】:2013-08-16 16:12:36
【问题描述】:
我想出了一种方法(不一定是新方法)来替换 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)),唯一的额外开销应该只是额外的几个周期。
有没有人对这种方案的性能有任何有用的 cmets 以及为什么不使用它(我敢肯定我不是第一个想到这个的人)?我认为它会相当有效,除非由于比较可能导致缓存失效,但我认为平均而言,对于基类,它可以在直接内联方法调用的几个周期内完成。
顺便说一下,该表可以看作是每个对象派生的内联虚函数调用列表。
假设我们有以下内容:
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 语句,可以通过使用有效的跳转列表进一步优化。
【问题讨论】:
标签: c# virtual-functions