你可以在C ++中缓存一个虚函数查找吗? [英] Can you cache a virtual function lookup in C++?

查看:101
本文介绍了你可以在C ++中缓存一个虚函数查找吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

说我有一个虚函数调用foo()一个抽象基类指针,mypointer-> foo()。当我的应用程序启动时,根据文件的内容,它选择实例化一个特定的具体类,并为该实例分配mypointer。对于应用程序的其余部分,mypointer将始终指向该具体类型的对象。我没有办法知道这个具体类型是什么(它可以由动态加载库中的工厂实例化)。我只知道类型将保持不变,第一次混凝土类型的实例后。指针可能不总是指向同一个对象,但是对象总是具有相同的具体类型。请注意,类型在技术上在'运行时'确定,因为它基于文件的内容,但是在启动(文件加载后)类型是固定的。



然而,在C ++中,我每次在应用程序的整个持续时间调用foo时支付虚函数查找成本。编译器不能优化查找,因为没有办法知道具体类型在运行时不会改变(即使它是最惊人的编译器,它不能推测动态加载的行为图书馆)。在JIT编译语言(如Java或.NET)中,JIT可以检测到相同类型被反复使用,并执行内联缓存< a>。我基本上正在寻找一种方法来手动做这个具体指针在C + +。



C ++中有什么办法缓存这个查找?我意识到解决方案可能是很黑的。如果可以编写发现ABI /编译器的相关方面的配置测试,即使没有真正的可移植性,也愿意接受ABI /编译器特定的攻击。<​​/ p>

更新:对naysayers:如果这不值得优化,那么我怀疑现代的JITs会做到。你认为Sun和MS的工程师在浪费他们的时间实现内联缓存,并没有对其进行基准测试以确保有改进?

解决方案

虚函数调用有两个成本:vtable lookup和函数调用。



vtable查找已经由硬件处理。现代CPU(假设您不是在一个非常简单的嵌入式CPU上工作)将预测虚拟函数在其分支预测器中的地址,并推测性地与数组查找并行执行它。 vtable查找与函数的推测执行并行发生的事实意味着,当在所描述的情况下在循环中执行时,与直接的,非内联函数调用相比,虚函数调用具有零开销



我已经在过去测试过这个,虽然在D编程语言,而不是C ++。当内联在编译器设置中禁用并且我在循环中调用相同的函数几百万次时,不管函数是否为虚函数,计时彼此都在ε内。



虚函数的第二个和更重要的代价是它们在大多数情况下阻止函数的内联。这比它听起来更重要,因为内联是一种优化,可以启用其他一些优化,例如在某些情况下的常量折叠。没有办法在不重新编译代码的情况下内联函数。 JIT解决这个问题,因为他们在应用程序执行期间不断地重新编译代码。


Say I have a virtual function call foo() on an abstract base class pointer, mypointer->foo(). When my app starts up, based on the contents of a file, it chooses to instantiate a particular concrete class and assigns mypointer to that instance. For the rest of the app's life, mypointer will always point to objects of that concrete type. I have no way to know what this concrete type is (it may be instantiated by a factory in a dynamically loaded library). I only know that the type will stay the same after the first time an instance of the concrete type is made. The pointer may not always point to the same object, but the object will always be of the same concrete type. Notice that the type is technically determined at 'runtime' because it's based on the contents of a file, but that after 'startup' (file is loaded) the type is fixed.

However, in C++ I pay the virtual function lookup cost every time foo is called for the entire duration of the app. The compiler can't optimize the look up away because there's no way for it to know that the concrete type won't vary at runtime (even if it was the most amazing compiler ever, it can't speculate on the behavior of dynamically loaded libraries). In a JIT compiled language like Java or .NET the JIT can detect that the same type is being used over and over and do inline cacheing. I'm basically looking for a way to manually do that for specific pointers in C++.

Is there any way in C++ to cache this lookup? I realize that solutions might be pretty hackish. I'm willing to accept ABI/compiler specific hacks if it's possible to write configure tests that discover the relevant aspects of the ABI/compiler so that it's "practically portable" even if not truly portable.

Update: To the naysayers: If this wasn't worth optimizing, then I doubt modern JITs would do it. Do you think Sun and MS's engineers were wasting their time implementing inline cacheing, and didn't benchmark it to ensure there was an improvement?

解决方案

There are two costs to a virtual function call: The vtable lookup and the function call.

The vtable lookup is already taken care of by the hardware. Modern CPUs (assuming you're not working on a very simple embedded CPU) will predict the address of the virtual function in their branch predictor and speculatively execute it in parallel with the array lookup. The fact that the vtable lookup happens in parallel with the speculative execution of the function means that, when executed in a loop in the situations you describe, virtual function calls have next to zero overhead compared to direct, non-inlined function calls.

I've actually tested this in the past, albeit in the D programming language, not C++. When inlining was disabled in the compiler settings and I called the same function in a loop several million times, the timings were within epsilon of each other whether the function was virtual or not.

The second and more important cost of virtual functions is that they prevent inlining of the function in most cases. This is even more important than it sounds because inlining is an optimization that can enable several other optimizations such as constant folding in some cases. There's no way to inline a function without recompiling the code. JITs get around this because they're constantly recompiling code during the execution of your application.

这篇关于你可以在C ++中缓存一个虚函数查找吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆