是否有人使用JIT技巧来提高静态编译语言(如C ++)的运行时性能?看起来热点分析和基于运行时期间观察的分支预测可以提高任何代码的性能,但也许有一些基本的战略原因,为什么在运行时进行这样的观察和实现更改只能在虚拟机中实现。我清楚地记得听到C ++编译器编写者嘀咕着“你也可以为用C ++编写的程序做到这一点”,同时听动态语言爱好者谈论收集统计数据和重新安排代码,但我的网络搜索支持这种记忆的证据已经枯竭了。
答案 0 :(得分:32)
配置文件引导优化与运行时优化不同。优化仍然是基于分析信息离线完成的,但是一旦二进制文件出厂,就没有持续优化,因此如果配置文件引导优化阶段的使用模式不能准确反映实际使用情况,那么结果将是不完美,程序也不会适应不同的使用模式。
您可能很有兴趣寻找有关HP's Dynamo的信息,尽管该系统专注于原生二进制 - >原生二进制翻译,虽然由于C ++几乎完全编译为本机代码,我认为这正是你所寻找的。 p>
您可能还想看一下LLVM,这是一个支持JIT编译和运行时优化的编译器框架和中间表示,尽管我不确定是否实际存在任何基于LLVM的运行时可以编译C ++并执行+运行时优化它。
答案 1 :(得分:10)
在过去的几年里,我做了很多这样的优化。这是我实现的图形渲染API。由于API定义了数千种不同的绘图模式,因为通用功能是缓慢的。
我最终为域特定语言编写了自己的小Jit编译器(非常接近asm,但是引入了一些高级控制结构和局部变量)。
我得到的性能提升在10到60之间(取决于编译代码的复杂性),因此额外的工作得到了很大的回报。
在PC上,我不会开始编写自己的jit-compiler,而是使用LIBJIT或LLVM进行jit编译。在我的情况下,由于我正在研究LIBJIT / LLVM不支持的非主流嵌入式处理器,所以我不得不发明自己的。
答案 2 :(得分:8)
答案更有可能:没有人为C ++做过PGO,因为好处可能不明显。
让我详细说明: JIT 引擎/运行时从开发人员的角度来看既有瑕疵又有弊端:他们在运行时有更多的信息,但分析的时间却很少。 一些优化非常昂贵,如果没有对开始时间产生巨大影响,你很可能会看到:循环展开,自动矢量化(在大多数情况下也基于循环展开),指令选择(使用SSE4.1 for使用SSE4.1的CPU结合指令调度和重新排序(使用更好的超标量CPU)。这种优化与C代码(可从C ++访问)相结合。
进行高级编译的单个完整编译器体系结构(据我所知)是使用分层编译(Java Azul Hotspot 编译和体系结构。 >系统,当天流行的 JaegerMonkey JS引擎)。
但是运行时最大的优化之一是:
多态内联缓存(意味着如果你运行第一个带有某些类型的循环,第二次,循环的代码将是来自前一个循环的特殊类型,以及 JIT 将放置一个保护并将默认分支作为内联类型,并基于它,使用基于 SSA -form引擎的这种专用形式将应用常量折叠/传播,内联,死代码消除优化,以及取决于 JIT 的“高级”,将改进或改善CPU寄存器的分配。) 正如您可能已经注意到的那样, JIT (热点)将主要改进分支代码,并且运行时信息将比C ++代码更好,但是静态编译器,在它的时候进行分析,指令重新排序,对于简单的循环,可能会获得更好的性能。此外,通常,C ++代码,需要快速的区域往往不是OOP,因此 JIT 优化的信息不会带来如此惊人的改进。
JIT 的另一个优点是 JIT 可以跨程序集工作,因此如果它想要进行内联,它会有更多信息。
让我详细说明一下:假设你有一个基类A,你有只有一个的实现,即另一个包/ assembly / gem / etc中的B.并动态加载。
JIT ,因为它看到B是A的唯一实现,它可以替换A内部表示中的任何地方A调用B代码,并且方法调用不会执行调度(看起来在vtable)但将是直接电话。这些直接电话也可以内联。例如,这个B有一个方法:getLength()
返回2,getLength()
的所有调用可以全部减少到常数2。最后,C ++代码将无法跳过来自另一个dll的B的虚拟调用。
C ++的某些实现不支持优化更多的.cpp文件(即使在最近版本的 GCC 中也存在-lto标志,这使得这成为可能)。但是如果你是一个关心速度的C ++开发人员,你可能会把所有敏感类放在同一个静态库中,甚至放在同一个文件中,这样编译器就可以很好地内联它,使JIT拥有它的额外信息。 ,由开发者自己提供,所以没有性能损失。
答案 3 :(得分:5)
visual studio可以选择进行运行时分析,然后可以用来优化代码。
“配置文件引导优化”
答案 4 :(得分:5)
Microsoft Visual Studio将此称为“profile guided optimization”;您可以在MSDN了解有关它的更多信息。基本上,您运行程序多次使用附加的分析器来记录其热点和其他性能特征,然后您可以将分析器的输出提供给编译器以获得适当的优化。
答案 5 :(得分:3)
我相信LLVM会尝试做一些这样的事情。它试图在程序的整个生命周期内进行优化(编译时,链接时和运行时)。
答案 6 :(得分:2)
合理的问题 - 但有一个可疑的前提。
正如在Nils的回答中,有时“优化”意味着“低级优化”,这本身就是一个很好的主题。
然而,它基于“热点”的概念,它与通常给出的相关性远远不够。
定义:热点是代码的一个小区域,其中进程的程序计数器花费大部分时间。
如果存在热点,例如占用大量时间的紧密内循环,则值得尝试在低级别进行优化,如果它是在您控制的代码中(即不在第三方中)库)。
现在假设内部循环包含对函数,任何函数的调用。现在程序计数器不太可能在那里找到,因为它更有可能在函数中。因此,虽然代码可能浪费,但它不再是热点。
有许多常见的方法可以使软件变慢,其中热点是其中之一。但是,根据我的经验,这是大多数程序员都知道的唯一一个,也是唯一适用于低级优化的程序。