运行时的C ++ devirtualization?

时间:2015-05-18 23:58:43

标签: c++ optimization llvm virtual-functions llvm-c++-api

是否存在允许具有类层次结构(具有virtual函数)的灵活性的技术/库,一旦在运行时确定了对象类型,就允许虚拟化函数调用?

举一个简单的例子,假设我有一个程序从一些配置文件中读取形状类型(圆形,矩形,三角形等),以构造所述形状的一些数据结构(例如,vector<shape*>):

class shape {
public:
    virtual void draw() const = 0;
    // ...
};

class circle : public shape {
public:
    void draw() const;
    // ...
};

// ...
vector<shape*> shapes;

显然,如果我想绘制所有形状,我可以这样做:

for ( auto&& s : shapes )
    s->draw();

每次对shapes进行此类迭代时,都会调用virtual函数调用每个形状的draw()

但是假设一旦shapes被创建,它的永远不会在程序的生命周期中再次发生变化;并进一步假设draw()将被称为许多次。如果知道实际形状后,有一种方法可以实现&#34; devirtualize&#34;在运行时调用draw()

我知道在编译时虚拟化virtual函数调用的优化技术,但我询问这一点。

如果有一个聪明的黑客直接在C ++中执行此操作,我会非常感到惊讶,因为这样做的一种方法是在运行时修改内存中的机器代码。但是有没有一些C ++库可以实现这样的功能呢?

我认为这样的可能可以通过LLVM实现,因为它允许在运行时生成机器代码。有没有人使用LLVM?也许一个框架在LLVM之上分层?

注意:解决方案必须是跨平台,即至少使用gcc / clang VC ++。

1 个答案:

答案 0 :(得分:2)

我非常肯定没有魔法,“这里的编译器,我没有更多的子类我将要定义,这个列表不会改变,所以消除虚函数调用开销“有点事。

在极其严重的性能关键情况下,您可以做的有助于虚拟调用的一件事是按照子类型对形状列表进行排序。例如,您需要重新排列这些类型,如:圆形,圆形,圆形,圆形,...,方形,而不是像圆形,矩形,三角形,矩形,三角形,正方形等零星的零星模式。 square,square,...等。这有效地优化了分支预测。我不知道这种方法是否仍然适用或者使用最新的体系结构和优化器产生了很多里程数,但是在我活着的时候至少有一段时间它非常有用。

关于JIT,我一直在探索那个领域。我不一定会建议尝试找到一个JIT解决方案来神奇地使您的C ++代码更快。

相反,我一直在探索它,因为我的软件已经有了一个特定于域的语言,一种可视化的节点GUI编程语言,你在节点(函数)之间绘制连接而不是编写代码来创建像着色器这样的新东西。图像过滤器(类似于虚幻引擎4的BluePrint)。它目前远不及手写本机代码那么快,这就是我有兴趣探索代码生成/ JIT路由的原因。它目前更像是一名翻译。

我已经为这些尝试了Tiny C和LCC,但有一件事我发现它们的优化程度并不像你的商业生产编译器那么复杂。我经常得到的结果平均比MSVC或GCC慢3到4倍。它们非常精彩,因为它们非常轻巧且易于嵌入。

LLVM似乎是一场精彩的比赛,除了它是巨大的。我们在我们的核心级别区域拥有这种旧式美学,其中最大程度重用的代码应尽可能少地重用(以避免偶尔依赖外部包)。我已经很难将其减少到足以通过这些标准的轻量级,但我仍然在研究它。