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

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

问题描述

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

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.

但是,在 C++ 中,每次在应用程序的整个持续时间内调用 foo 时,我都会支付虚函数查找成本.编译器无法优化查找,因为它无法知道具体类型在运行时不会发生变化(即使它是有史以来最出色的编译器,它也无法推测动态加载的行为)图书馆).在像 Java 或 .NET 这样的 JIT 编译语言中,JIT 可以检测到重复使用相同的类型并执行 内联缓存.我基本上是在寻找一种方法来为 C++ 中的特定指针手动执行此操作.

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++.

C++ 中有没有办法缓存这个查找?我意识到解决方案可能非常骇人听闻.如果可以编写发现 ABI/编译器相关方面的配置测试,使其实际上可移植",即使不是真正可移植的,我也愿意接受 ABI/编译器特定的技巧.

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.

更新:致反对者:如果这不值得优化,那么我怀疑现代 JIT 会这样做.您是否认为 Sun 和 MS 的工程师是在浪费时间实施内联缓存,而没有对其进行基准测试以确保有所改进?

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.

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

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.

我过去实际测试过这个,尽管是用 D 编程语言,而不是 C++.当在编译器设置中禁用内联并且我在循环中调用同一个函数数百万次时,无论函数是否为虚拟函数,时间都在彼此的 epsilon 之内.

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.

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

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天全站免登陆