请记住,以下内容仅是比较本机编译和JIT编译之间的差异,并不涵盖任何特定语言或框架的细节。可能有合理的理由选择除此以外的特定平台。
当我们声称本机代码更快时,我们谈论的是本机编译代码与JIT编译代码的典型用例,其中JIT编译应用程序的典型使用将由用户运行,并立即获得结果(例如,首先等待编译器)。在那种情况下,我认为没有人可以直言不讳地宣称JIT编译代码可以匹配或击败本机代码。
假设我们有一个用X语言编写的程序,我们可以使用本机编译器,再使用JIT编译器对其进行编译。每个工作流程都有相同的阶段,可以概括为(代码->中间表示->机器代码->执行)。两者之间的最大区别是用户可以看到哪个阶段,程序员可以看到哪个阶段。使用本机编译,程序员可以看到除执行阶段以外的所有内容,而对于JIT解决方案,除了执行之外,用户还可以看到对机器代码的编译。
如用户所见,A比B快的说法是指程序运行所花费的时间。如果我们假设这两段代码在执行阶段的性能相同,那么我们必须假设JIT工作流程对用户而言较慢,因为他还必须看到编译为机器代码的时间T,其中T> 0。 ,为了使JIT工作流程执行与本机工作流程相同的任何可能性,我们必须减少代码的执行时间,以使执行+编译到机器代码的时间少于仅执行阶段本地工作流程。这意味着我们必须在JIT编译中比在本地编译中更好地优化代码。
但是,这是不可行的,因为要执行必要的优化以加快执行速度,我们必须在编译到机器代码的阶段花费更多的时间,因此,由于优化的代码而导致的任何保存时间实际上都会丢失,因为我们将其添加到编辑中。换句话说,基于JIT的解决方案的“缓慢”不仅是因为JIT编译需要花费更多的时间,而且该编译产生的代码比本地解决方案执行起来还要慢。
我将使用一个示例:寄存器分配。由于内存访问比寄存器访问慢数千倍,因此理想情况下,我们希望尽可能使用寄存器,并尽可能减少内存访问,但是寄存器数量有限,并且在需要时必须将状态溢出到内存中一个寄存器。如果我们使用的寄存器分配算法需要200毫秒的计算时间,因此可以节省2毫秒的执行时间-我们没有充分利用JIT编译器的时间。像Chaitin的算法这样的解决方案可能会产生高度优化的代码,这是不合适的。
JIT编译器的作用是在编译时间和所生成代码的质量之间取得最佳平衡,但是,由于您不想让用户等待,因此在快速编译时间上存在很大的偏差。在JIT情况下,正在执行的代码的性能会较慢,因为本机编译器在优化代码方面不受时间的束缚,因此可以自由使用最佳算法。JIT编译器的总体编译+执行仅可以击败本机编译代码的执行时间的可能性实际上为0。
但是我们的VM不仅限于JIT编译。他们采用提前编译技术,缓存,热插拔和自适应优化。因此,让我们修改一下性能是用户所看到的说法,并将其限制为执行程序所需的时间(假设我们已经编译了AOT)。我们可以有效地使执行代码等效于本机编译器(或更好)。VM的一个重要主张是,与本地编译器相比,它们可能能够产生质量更好的代码,因为它可以访问更多信息-正在运行的进程的信息,例如可以执行某个功能的频率。然后,VM可以通过热交换将自适应优化应用于最基本的代码。
但是,此参数存在一个问题-它假定配置文件引导的优化等是VM独有的特性,这是不正确的。我们也可以将其应用于本机编译-通过启用分析功能来编译我们的应用程序,记录信息,然后使用该配置文件重新编译该应用程序。可能还值得指出的是,代码热交换不是仅JIT编译器就能做到的,我们也可以为本地代码做到这一点-尽管基于JIT的解决方案更容易获得,并且对开发人员来说更容易。因此,最大的问题是:VM可以为我们提供一些本机编译无法提供的信息,从而可以提高代码的性能吗?
我自己看不到。我们也可以将典型VM的大多数技术应用于本机代码-尽管过程更多。同样,我们可以将本机编译器的所有优化应用回使用AOT编译或自适应优化的VM。现实情况是,本机运行的代码与在VM中运行的代码之间的差异并不像我们已经相信的那么大。它们最终会导致相同的结果,但是他们采用了不同的方法来达到目标。VM使用迭代方法来生成优化的代码,其中本机编译器从一开始就希望它(可以通过迭代方法进行改进)。
C ++程序员可能会争辩说,他需要从一开始就进行优化,因此根本不应该等待VM确定如何进行优化。但是,这可能是我们当前技术的一个正确点,因为我们虚拟机中的当前优化水平不如本机编译器可以提供的优化水平,但是,如果我们的虚拟机中的AOT解决方案得到改进,则并非总是如此。