动态优化字节编码程序的方法和装置的制作方法

文档序号:6414408阅读:210来源:国知局
专利名称:动态优化字节编码程序的方法和装置的制作方法
技术领域
本发明一般涉及优化软件应用程序执行的方法和装置。更具体来说,本发明涉及在运行期间为了优化软件应用程序而动态地决定部分代码应当被解释还是被编译的方法和装置。
用计算机系统共享计算机系统网络(例如局域网、内连网和因特网)资源的情况正在增加。相应地,软件应用程序或计算机程序可能要以不同的格式传递给不同的计算机系统,这是因为,有的计算机系统通常要求软件应用程序要采用专门适合该特定计算机系统的格式。另外,也可以将计算机程序以与机器无关的格式(即按字节代码)传递给计算机系统,以便使一种格式的计算机程序能被许多不同的计算机系统使用。
如果计算机程序是以与机器无关的格式传递的,则可以直接解释程序,或者,也可以将程序翻译成与机器有关的代码,即“机器代码”。直接解释的程序比翻译成机器代码的程序占用较少的计算机系统空间。然而在多数情况下,直接解释的程序比翻译成机器代码的程序的执行速度慢。因此,决定直接解释计算机程序还是将计算机程序翻译成机器代码,经常是根据空间与执行速度之间的相对重要性。
如上所述,可以按字节代码将计算机程序传递给计算机系统。接收字节代码的计算机系统一般含有用于在运行时编译字节代码的编译程序。在运行时编译字节代码的结果是将字节代码翻译成机器代码。

图1A表示一个具有字节代码编译程序的计算机系统的框图。字节代码104可以以计算机程序的形式被传递或用其它方式提供给计算机系统105。字节代码104一般可以由各种不同的源提供。当字节代码104被执行时,字节代码104要在运行时用编译程序106编译。编译程序106生成的编译代码108一般是系统105专用的、并能在该系统105中执行的与机器有关的代码。就是说,编译程序106在运行时将字节代码106翻译成编译代码108。
有些计算机系统能在发现部分以前编译的代码要被反复执行时,使这部分以前编译的代码被“重新编译”成以更高效率执行的格式。换言之,可以用成本更高的第二个编译过程来编译被反复调用的以前编译的代码以便能更高效率地执行该反复调用的代码。图1b是表示使用两个编译过程的计算机系统的框图。计算机系统115包括第一个编译程序116和第二个编译程序122。字节代码114被提供给计算机系统115执行。运行时,第一个编译程序116将字节代码114翻译成与机器有关的编译代码118或日机器代码。
执行与机器有关的编译代码118,追踪与机器有关的编译代码118所含的不同子程序(method),以确定何时用第二个编译程序编译最经常被调用的子程序。当确认了高度执行的(或者说反复执行的)编译代码120时,就重新编译该高度执行的编译代码120,以提高计算机程序的总体执行速度。因此,第二个编译程序122要将高度执行的编译代码120翻译成重新编译的高度执行代码124。
第二个编译程序122经常是比第一个编译程序116慢的编译程序,尽管用第二个编译程序122编译的代码一般比第一个编译程序116编译的代码执行的效率更高。因此,确定何时重新编译高度执行的编译代码120涉及在总体运行时间中额外的编译开销与重新编译的高度执行代码124带来的效率提高之间作出权衡。
为提高效率而允许将预先编译的代码重新编译的一个系统,要根据特定部分的编译代码(例如子程序)被调用的次数来确定是否重新编译预先编译的代码。如果该子程序被调用的次数超过一个固定的界限值,该子程序就被重新编译。这个固定的界限值本质上是一个固定阈值,它反映该子程序被重新编译以提高执行效率之前,该子程序被调用的次数。
在运行时如果用编译程序将字节代码翻译成与机器有关的代码,总体程序执行中经常加入了很大的编译开销。因此,尽管机器代码可能比解释代码执行的速度快,要是总是将程序的所有部分都编译成机器代码后再执行机器代码,如果增加的执行速度不能弥补与编译该程序关联的开销,则这种做法并不可取。换言之,如果花费在编译该程序上的时间不能在程序的执行期间得到弥补,则程序以解释代码执行起来会更快。
在一个如上所述的现有系统中,尽管重新编译被编译过的程序中的例程可能具有使程序执行效率更高的作用,用固定界限值来确定例程何时应当被重新编译反而会导致低效率的(即非优化的)执行。例如,如果这样设定固定界限值,使程序中的每一个例程实际上都要被重新编译,则由重新编译获得的提高的执行速度可能不会弥补与重新编译关联的编译开销。
因此,需要一种能高效地执行字节代码格式的程序的方法。更具体来说,需要一种能动态地确定何时应解释部分计算机程序、何时应将部分计算机程序翻译成机器代码,由此保证高效地执行计算机程序的方法。
本文介绍用于在运行期间动态地确定应解释还是编译部分字节代码,以优化软件应用程序的方法和装置。根据本发明的一个方面,当一个选定的子程序(method)被调用时,它起初是被解释的。一个调用跟踪器(invokation tracker)跟踪该选定子程序的调用次数。当该选定子程序的调用次数超过一个阈值时,该子程序被编译。通过独立地跟踪各种子程序或其它代码段的使用情况,就能更加智能地决定哪些子程序应当被编译,哪些子程序应当依然被解释。在有些实施例中,可以对所有子程序采用一个阈值;在其它实施例中,可以采用多个阈值,每个阈值与一个或多个子程序关联。在后一种安排中,不同子程序关联的阈值可以不同。
根据本发明的另一个方面,测量编译某个别子程序相关的开销。这个开销然后要与一个可接受开销参数进行比较,如果该开销不在可接受的范围内,就对启动编译该特定子程序的阈值进行调整。本发明这方面的内容可以应用于该子程序的初始编译和/或该子程序的重新编译。
根据本发明的再一个方面,用于执行组织成多个子程序的字节代码的一个计算机系统中,包括一个适于解释各种子程序的解释程序,以及一个对某选定子程序的解释进行计数的跟踪机制。该跟踪机制用于确定该选定子程序何时适于编译。计算机系统进一步包括一个适于编译各种子程序的编译程序。在一个实施例中,计算机系统包括一个阈值调整机制,它与跟踪机制合作,以确定该选定子程序何时适于编译。在另一个实施例中,计算机系统进一步包括另一个计算该选定子程序被编译后的调用次数的跟踪机制,用于确定该选定子程序何时适于重新编译。
本发明的这些优点和其它优点,表现在以下的详细说明和各附图中。
以下说明结合附图来阐述本发明。附图简述图1a是表示一个具有字节代码编译程序的计算机系统的框图。
图1b是表示使用两个编译过程的计算机系统的框图。
图2a是表示按照本发明的一个实施例的一个动态地编译代码的计算机系统的框图。
图2b是表示按照本发明的一个实施例,处理字节代码的相关步骤的过程流图。
图3是表示按照本发明的一个实施例,执行一个程序相关的步骤(即图2b的步骤206)的过程流图。
图4是表示按照本发明的一个实施例,执行一个程序的相关步骤(即图3的步骤320)的过程流图。
图5是表示按照本发明的一个实施例,执行一个重新编译程序的相关步骤(即图4的步骤420)的过程流图。
图6是表示按照本发明的一个实施例,增大一个阈值的相关步骤(即图5的步骤516)的过程流图。
图7是表示按照本发明的一个实施例,执行一个阈值监测程序的相关步骤(即图2b的步骤204)的过程流图。
图8是表示按照本发明的一个实施例,减小一个阈值的相关步骤(即图7的步骤712)的过程流图。
图9是表示按照本发明的一个实施例,计算一个解释开销的相关步骤的过程流图。
图10是表示一个适于实现本发明的通用计算机系统的示意图。
图11是表示一个由图10的通用计算机系统支持、适于实现本发明的虚拟机的示意图。
字节编码的计算机程序被提供给计算机系统时,程序可以被直接解释,或者,程序可以在运行时被翻译成机器代码执行。被直接解释的程序占用的计算机内存一般比被翻译成机器代码的程序少。另一方面,被翻译成机器代码的程序执行的速度一般比被直接解释的程序快。然而在有些情况下,如果用编译程序生成机器代码,增加到程序总体运行时间的编译开销并不总是能在程序执行期间得到补偿。在这些情况下,直接解释该程序可能速度更快,效率更高。总之,要确定程序被直接解释时执行的效率更高还是被翻译成机器代码时执行的效率更高是困难的。因此,程序往往不能以最快的速度或者最可能高效率地执行。
允许字节编码的计算机程序既能被直接解释,又能被翻译成机器代码,能使计算机程序的执行速度比纯粹解释的程序所能达到的执行速度有所提高。解释的代码与机器代码(即编译的代码)的混合使用,对内存的需求比单纯执行机器代码时低。因此,解释的代码与编译的代码的混合使用,提高了程序执行的效率。
字节编码的程序中每个子程序都可以被监测,以确定编译该子程序是否有益于程序的总体执行。在一个实施例中,监测子程序时要记录该子程序作为解释的子程序被调用的次数。当对该解释的子程序的调用达到某个水平或阈值,表明如果该子程序被编译,它有可能弥补编译的代价时,该子程序就被动态地编译。为了实质上保证将与对程序中子程序编译关联的编译开销维持在一个可以接受的水平,程序的编译开销和解释开销二者都要监测。如果认为编译开销太高,那么就可以进行调整,减少被编译的子程序的数量,以降低编译开销。所作的调整包括提高用于确定何时对一个解释的子程序进行编译的阈值。通过允许修改自适应的阈值,字节编码的程序的执行就会得到显著的优化。换言之,可以调整解释的代码与动态编译的代码的混合比例,使执行时间更短,并在程序占用的内存空间与程序的执行速度之间取得更好的平衡。
下面结合图2a,描述按照本发明的一个实施例的一个计算机系统,该计算机系统用自适应阈值来确定字节代码应当在何时被动态编译。字节代码144在运行时被作为输入提供给计算机系统146。字节代码144可以被组织成与机器无关的计算机程序,一般被组织成子程序或例程。在一个实施例中,字节代码144可以被一个虚拟机内的编译时环境提供给同一虚拟机内的一个运行时环境-例如计算机系统146。实现本发明所在的一种适合的虚拟机,本文将在以后结合图11加以详述。
字节代码144被提供给计算机系统146时,字节代码144可以被解释程序148处理。另外,字节代码144也可以被编译程序150编译,生成编译的代码。一般来说,字节代码144实际上可以直接地既输入到解释程序148,又输入到编译程序150,但是在所述实施例中,字节代码144只被提供给解释程序148处理。下文将针对图2b讨论与处理字节代码关联的步骤。
一个子程序每次被调用时,与该子程序关联的字节代码144就被用解释程序148解释。在所述实施例中,保持着对子程序被解释的次数的记录。任何适当的措施都可以用来跟踪子程序被解释的次数。适当的措施中包括,用一个计数器来记录子程序被解释的次数,但不限于采用计数器。这种计数器在某个子程序内是这样实现的该子程序每当被解释一次,计数器的值递增1。
当子程序被解释的次数超过一个阈值(即界限值)时,就可以用编译程序150编译该子程序。反复解释一个包含在频繁执行的代码158中的子程序,效率可能很低,这是因为解释的字节代码154一般比编译代码执行得慢,或者说效率更低。编译频繁执行的代码158一般能使频繁执行的代码158中包含的子程序的执行效率更高,因为通过编译该子程序所节省的时间可能会弥补与编译过程关联的编译开销。
一般来说,在多数计算机程序的执行过程中,有些子程序反复执行,其它的则不常执行。频繁执行的代码158一般被视为含有构成程序执行的重要部分的子程序的代码段。频繁执行的代码158被编译程序150编译时,生成频繁执行的代码的编译版本162。因此,当频繁执行的编译代码162中的子程序被调用时,编译子程序(compiledmethods)就被调用。在一个实施例中,频繁执行的代码158包含一组子程序,它们实际占50%以上的总体执行时间,例如大约90%的总体执行时间。
有些计算机系统中包括单级编译,另一些计算机系统中包括多级编译。包括有多级编译的计算机系统可以被安排在确定重新编译子程序会提高这些子程序相关的执行效率的时候去重新编译编译子程序。例如,将可以用编译程序150完成的一个第一级编译,与某个“中间”级编译关联。中间级编译可以被安排去将字节代码编译成某种编译形式,这种编译形式尽管比解释字节代码的效率高,却可能没有“最终”级编译的效率高。中间级编译本质上会牺牲执行中间级编译子程序的某些执行效率去换取编译速度,而最终级编译的编译时间虽然更长,执行效率却更高。
在一个实施例中,可以用跟踪机构来识别包含在频繁执行的编译代码162中的最经常执行的子程序。换言之,在频繁执行的编译代码162内,可以识别出高度执行的编译代码166。当高度执行的编译代码166内的子程序执行的次数超过阈值时,就用另一个编译程序170重新编译高度执行的编译代码166,生成高度执行的代码的重新编译版本174。应当明白,另一个编译程序170可能是与编译程序150不同的编译程序。另外,编译程序170与编译程序150也可能实际上是相同的编译程序,但用不同的编译程序参数执行。应当明白,尽管以上只描述了两级编译,但系统146一般可以包含任意级数编译。
图2b是表示根据本发明的一个实施例处理字节代码-即与机器无关的计算机程序-的相关步骤的过程流图。过程202从启动阈值监测程序(threshhold monitor)的步骤204开始。阈值监测程序与计算机程序并发执行,下文将会说明,它记录一个阈值,该阈值表示某特定子程序在被考虑进行编译之前要执行的次数。阈值监测程序的一个实施例将在下文中结合图7说明。
在步骤206,执行一个由字节代码组成的计算机程序。该步骤将在以后说明图3时讨论。计算机程序执行后,处理字节代码的子程序就结束。应当明白,在所述实施例中,阈值监测程序与该程序实际上是基本上同时(即作为并行过程)执行的。换言之,步骤204和206实际上是并行执行的。
下面参见图3,说明按照本发明的一个实施例执行一个程序的相关步骤,即图2b的步骤206的相关步骤。在步骤308,对阈值初始化。在一个实施例中,阈值是个上限值,表示在考虑对子程序进行编译之前该特定子程序要执行的次数。阈值的大小一般变化很大,要取决于特定系统的要求,例如内存要求和效能要求。例如可以将阈值初始化在1,000~10,000范围内。
在一个实施例中,一个程序执行所关联的所有子程序只用一个阈值,即阈值可是个全局常量或全局变量。不过应当明白,其它实施例中可以有多个阈值,例如,阈值可以是子程序专用的常量或子程序专用的变量。
阈值在步骤308被初始化后,在步骤312,含有无执行计数器的子程序解释代码被加载,并将执行计数器加到这些子程序。换言之,子程序被分配,计数器被加到这些新分配的子程序。一旦加入计数器后,在步骤316,新分配的子程序中的计数器被初始化,例如设置为0。
过程从步骤316继续到步骤320,执行加载代码中的子程序,直到代码执行完成或者加载代码中所有适当的子程序基本上都已经执行。在代码执行期间,控制流可能到达以前未曾加载的子程序,在这种情况下,代码可以被动态加载,本领域的熟练人员明白这一点。下文将针对图4更详细地说明执行子程序M的相关步骤。在代码执行的过程中,步骤324判断,是否还有解释代码要加载。如果判断还有解释代码要加载,则过程返回到步骤312去加载其它代码,反之,如果判断没有其它解释代码要加载,则继续步骤320的代码执行。
图4是表示按照本发明的一个实施例,执行一个程序的相关步骤(即图3的步骤320)的过程流图。子程序M的执行从步骤404开始,此时子程序M的计数器即执行计数器递增。计数器递增后,在步骤408将该子程序M计数器与阈值比较。如上所述,阈值指示的是在考虑编译子程序M之前子程序M要执行的次数。
在步骤412,判断子程序M计数器是否超过阈值。如果子程序M计数器不超过阈值,表示没有必要编译子程序M。相应地,过程流从步骤412转移到步骤416,用解释程序执行子程序M。反之,如果步骤412判断结果是子程序M计数器超过阈值,则表示编译而不是解释子程序M会提高执行总体程序的效率。于是,如果判定子程序M计数器超过阈值,则在步骤420执行一个重新编译程序(recompiler)。下文将结合图5更详细地说明执行重新编译程序的相关步骤。
步骤420执行重新编译程序后,过程流继续到步骤424,判断子程序M是否被编译。就是说,判断子程序M是否因为在步骤420执行重新编译程序而被编译。如果判断子程序M不被编译,过程流继续到步骤416,用解释程序执行子程序M。如果步骤424判断子程序M已经编译,则在步骤428执行子程序M的编译代码。
如上所述,在一个实施例中,用一个重新编译程序来确定编译程序将如何处理一个给定的子程序,例如子程序M。如果判断编译对执行总体程序有益,则安排这种重新编译程序去实际调用一个编译程序来编译子程序M。图5是表示按照本发明的一个实施例,执行一个重新编译程序(即图4的步骤420)的相关步骤的过程流图。重新编译程序的执行开始于步骤504,该步骤搜寻子程序M的直接调用程序(direct caller)。一般来说,搜寻该调用程序,即调用子程序M的子程序,必然要检查调用堆栈(call stack),本领域的熟练人员都知道这一点。
搜寻子程序M的调用程序后,在步骤506,判断子程序M的调用程序是否存在。如果判断子程序M的调用程序存在,过程继续到步骤508,该步骤判断总体程序关联的编译程序是否要将子程序M直接插入(inline)调用程序。将子程序M直接插入调用程序,一般要复制一份子程序M的解释代码,并将复制的代码合并到调用程序中,合并之前要剔除不必要的部分代码,例如涉及调用程序向子程序M传递的参数或变量说明的代码。如果调用程序反复执行,由此使得子程序M要反复执行,或者如果调用程序反复调用子程序M,则将子程序M直接插入调用程序并编译该调用程序就可能会提高总体程序执行的效率。不过,将子程序M直接插入调用程序并不删除子程序M的未直接插入版本,这是因为其它子程序也可能会调用子程序M,所以子程序M必须保持对其它子程序开放。因此,将子程序M直接插入调用程序并不必然提高总体程序执行的效率。这是因为直接插入子程序M,增加了该程序相关的代码的物理量。
如果在步骤508确定编译程序要将子程序M直接插入调用程序,则在所述实施例中,暗示总体程序执行效率提高的好处实际上大于增加额外程序代码的坏处。结果,过程流从步骤508转移到步骤510,调用编译程序来直接插入子程序M,并编译已插入子程序M的子程序M的调用程序。
将子程序M直接插入子程序M的调用程序的决定涉及各种不同的因素。这些因素例如包括子程序M的大小、调用程序的大小、与子程序M和调用程序相关的参数的值;以及调用子程序M的次数,但不局限于这些因素。总之应当明白,直接插入子程序M不仅会将子程序M直接插入子程序M的调用程序,也会将子程序M的调用程序直接插入调用程序的调用程序。换言之,可以在堆栈中搜寻任意多级的调用程序。
直接插入子程序M并编译调用程序后,在步骤512检查编译开销。一般来说,检查编译开销总要监测与运行期间编译子程序相关的开销。该编译开销然后与最大期望编译开销比较。在一个实施例中,最大期望编译开销,是运行期间编译子程序或以其它方式将子程序翻译成机器代码的花费占整个系统开销(例如中央处理单元或花费的时间)的某个最大百分比。因此检查编译开销必然要确定实际用于在运行时编译子程序的系统开销量。
在步骤514判断编译开销是否超过最大期望编译开销。如果判定编译开销超过最大期望编译开销,则表示被编译的子程序数量太多,于是过程流转移到步骤516,增大阈值。增大阈值一般能有效地降低编译开销,这是因为增大阈值增加了在考虑编译子程序之前子程序要被调用的次数。例如,如果阈值的设定值为1000,在程序执行期间几乎所有子程序都至少被调用1000次,然后将阈值提高到10,000,结果会降低编译开销,因为被编译的子程序少了。下文将结合图6更详细地说明增大阈值的相关步骤。在步骤516增大阈值后,执行重新编译程序的过程结束。
如果在步骤514判定编译开销小于最大期望编译开销,则认为该阈值是可以接受的。就是说,编译子程序和执行编译的子程序相关的开销被认为处于一个可以接受的水平。这样,重新编译程序的执行结束。
返回步骤508,如果判定与总体程序的执行关联的编译程序不会将子程序M直接插入调用程序,则在步骤518,调用编译程序来编译子程序M。单独编译子程序M而不将子程序M插入调用程序,一般会减少整个程序相关的代码量。如上所述,插入子程序M实际上导致复制一份子程序M。以后,如果子程序M被不同的子程序调用,子程序M最终可能会被插入各个不同的子程序中。这样,子程序M相关的代码的复制品会在整个程序中迅速增加,编译插入代码相关的编译工作量会非常大。因此,在一个实施例中,为了节省存储空间,减少编译工作量,如果子程序M可能被多个子程序调用的话,就单独编译子程序M,而不是将子程序M直接插入其调用程序。子程序M在步骤518被编译后,过程流转移到步骤512,检查编译开销。要确定子程序M是否可能被多个子程序调用,就要研究调用链(call chains),本领域的熟练人员知道这一点。
回过来看步骤506,该步骤判断是否存在子程序M的调用程序,如果不存在子程序M的调用程序,或者由于其它原因在搜寻调用程序时没有发现,则过程流继续到步骤518,编译子程序M。如果没有发现调用程序,则表示子程序M是被运行时系统调用的,而不是被其它子程序调用的。
如上所述,如果总体程序执行相关的编译开销大于所希望的最大开销,就要增大阈值。使用这种自适应阈值一般允许按特定应用程序的需要而修改阈值。对于有许多子程序或函数都超过一定调用次数的应用程序来说,如果对应的阈值太低,则会导致人们所不希望的、效率(即时间和存储空间)上的高编译开销。于是,设置较高的阈值,就能使编译开销符合要求,即降低编译开销。
在程序执行期间增大阈值,也能防止以紧密的间隔编译子程序。例如,如果几乎同时超过特定阈值的不同子程序的数量相对较多,则在总体程序的执行的期间可能会发生暂停。由于程序执行期间的暂停是不利的,所以在编译前几个子程序后增大阈值能防止一系列编译的间隔太密,籍此基本消除编译产生的停顿。
下面结合图6,描述按照本发明的一个实施例,增大一个阈值的相关步骤(即图5的步骤516)。换言之,现在说明图5的步骤516。应当明白,对于阈值是个全局值的实施例来说,增大阈值将增大该全局值。反之,如果阈值是子程序专用的值,则增大阈值将只是增大特定子程序或子程序组的阈值。
增大阈值的过程开始于步骤602,该步骤判断阈值是否处于阈值极限,即上限阈值。阈值极限与正在执行的总体程序的要求有关,一般是认为可以接受的阈值的最大值。
如果在步骤602判定阈值处于阈值极限,那么,在所述的实施例中,就不得增大阈值。这样,增大阈值的过程视为完成。例如,阈值极限大约在1000~5000个数量级上的调用,对于现存系统是合适的。反之,如果在步骤602判定阈值不处于阈值极限,那么在步骤604,判断阈值极限在当前时段内是否曾经向上调整过。在一个实施例中,判断阈值极限在当前时段内是否向上调整过,涉及判断相关的间隔变化标志(interval change flag)是否指示最近已经作过调整。下文将描述用于设置和清除间隔变化标志的表示机制。
如果在步骤604判定阈值极限在当前时段内(例如最近的过去)向上调整过,则不再增大阈值。不再增大最近已经调整过的阈值,是为了防止阈值被过分调整。如果在步骤604判定不再增大阈值,则增大阈值的过程视为完成。
如果在步骤604判定阈值极限在当前时段内未曾向上调整过,则过程流转移到步骤606,阈值被乘以一个阈值系数。各阈值系数一般都依赖总体计算机程序的要求而大不相同。例如,大约1.2~2量级的阈值系数就比较适合当前系统。在阈值被乘以一个阈值系数,阈值增大之后,在步骤608设置间隔变化标志。间隔变化标志用于指示,阈值在当前时段内已经作过调整。增大阈值的过程在设置间隔变化标志后完成。
在所述实施例中,正如说明图2b时所述,程序执行的开始与阈值监测程序的开始并行发生。就是说,在程序执行期间,监测程序也在执行。图7是表示按照本发明的一个实施例,开始执行一个阈值监测程序的相关步骤(即图2b的步骤204)的过程流图。阈值监测程序的执行开始于步骤702,此时阈值监测程序接到操作系统的一个定时器信号。定时器信号实际上是一种周期性地中断程序执行的时钟“滴答”。本领域熟练人员知道,可以将阈值监测程序的执行挂起,即让阈值监测程序“睡着”,一直到收到定时器信号。
一旦收到定时器信号,就在步骤704更新编译开销和解释开销的滑动平均数(sliding averages)。换言之,在一定时段内(例如最近的过去)花在编译上的时间量和花在编译上的时间量,用该时段的时间间隔个数平均后,都可以表达为该时段的百分数。
滑动平均数被更新后,间隔变化标志在步骤706被复位。复位间隔变化标志一般是将间隔变化标志设置成表示阈值在给定的时段内未被调整过。间隔变化标志被复位后,在标志708,判断编译开销是否超过最大期望编译开销。最好将编译开销保持在最小期望编译开销至最大期望编译开销的范围内。最小期望编译开销和最大期望编译开销的值的变动范围很大,但人们发现在有些系统中它们的合适值是最小期望编译开销大约在5%~25%,最大期望编译开销大约在20%~65%。在一个特定实施例中,最小期望编译开销是给定时段的大约10%,最大期望编译开销是给定时段的大约50%。
如果在步骤708判定编译开销大于最大期望编译开销,则过程流转移到步骤716,增大阈值(例如全局阈值)。前文说明图6时描述过一种增大阈值的适当方法。一旦增大阈值后,过程流转移到步骤702,过程在此等待从操作系统接收新的定时器信号。
如果在步骤708判定编译开销小于最大期望编译开销,则在步骤710判断编译开销是否小于最小期望编译开销。如果编译开销小于最小期望编译开销,则说明阈值可能定的太高,因此,为了使编译开销保持在最小期望编译开销至最大期望编译开销的范围内,要在步骤712减小阈值。减小阈值使阈值更容易达到,结果增加了对子程序的编译(关于减小阈值下文在讨论图8时将有详细说明)。这样就能将编译开销提高到最小期望编译开销以上。减小阈值后,过程流返回到步骤702,在该步骤中,执行阈值监测程序的过程在接收到定时器信号之前实际上一直被挂起。
如果判定编译开销在最小期望编译开销至最大期望编译开销的范围内,则在步骤714判断解释开销是否大于最大期望解释开销。关于计算解释开销的一个适当方法,下文在讨论图9时将有详细说明与最大期望编译开销的情况一样,最大期望解释开销的值的范围变动也很大。另外,最小期望解释开销的值的范围变动也可以变动很大。例如,最大期望解释开销可以是给定时段的约20%,最小期望解释开销可以是给定时段的约5%。在一个实施例中,最大、最小期望解释开销可以基本上与最大、最小期望编译开销相同。
如果解释开销大于最大期望解释开销,则在步骤712减小阈值。减小阈值的过程中的一个典型过程在下文讨论图8时叙述。减小阈值一般使更多的子程序被编译,从而减少了被解释的子程序的数目。另一方面,如果判定解释开销小于最大期望解释开销,则在步骤718判断解释开销是否小于最小期望解释开销。如果解释开销小于最小期望解释开销,则在步骤716增大阈值。如果解释开销在最小期望解释开销至最大期望解释开销限定的可接受的解释开销的范围内,则在步骤702继续阈值监测程序的执行过程,接收操作系统的新的定时器信号。
下面结合图8,描述按照本发明的一个实施例,减小一个阈值(即图7的步骤712)的相关步骤。减少阈值的过程开始于步骤804,该步骤判断阈值是否处于阈值下限。阈值下限一般被视为对阈值是合理的最小值。例如,尽管阈值下限的范围变动很大,阈值下限大约在100~1000次的量级是比较合适的。在一个实施例中,阈值下限是大约500次调用。
如果步骤804判定阈值处于阈值下限,这暗示不能再减小阈值了。则减小阈值的过程视为完成。反之,如果步骤804判定阈值不处于阈值下限,则在步骤808判断阈值在当前时段是否曾经被向下调整过。
如果判定阈值在最近的过去(或者当前时段内)向下调整过,则在所述实施例中将不再减小阈值。在给定时段不允许向下调整阈值超过一次,是为了防止阈值被过分调整。如果判定不再减小阈值,则减小阈值的过程视为完成。
如果阈值在当前时段内未曾向下调整过,则过程流从步骤808转移到步骤812,在步骤812中,阈值被除以一个阈值系数。各阈值系数一般都依赖总体计算机程序的要求而大不相同。在一个实施例中,阈值被除以的阈值系数等于讨论图6时所述增大阈值时用作乘数的阈值系数。在阈值被除以阈值系数之后,在步骤816设置间隔变化标志。在所述实施例中,间隔变化标志的置位用于指示阈值在当前时段已经向下作过调整。设置间隔变化标志后,减小阈值的过程完成。
除了监测在执行期间程序相关的编译开销之外,程序相关的解释也要监测。监测解释开销实际上涉及监测花费在解释子程序上的总体程序开销量。如前面讨论图7时所述,监测解释开销的目的,至少部分是为了判断是否要增大阈值或减小阈值。例如,如果解释开销太高,这暗示应当编译更多子程序,要减小阈值以允许更多的子程序被编译。
一般来说,可以用任何适当的过程来计算解释开销。以下结合图9,描述按照本发明的一个实施例用于计算解释开销的一种适当过程。该过程开始于步骤904,该步骤接收操作系统的一个定时器信号。接收定时器信号后,在步骤908获得应用程序过程的当前程序计数器。在一个实施例中,程序计数器中是一个参考,它能确定总体程序的执行当前使用的是解释代码还是编译代码。
一旦获得当前程序计数器后,在步骤912判断程序计数器是否引用(即指向)解释程序。就是说,判断当前是否正在执行解释代码。如果判定程序计数器不是指向解释程序,这表示程序计数器正指向某个编译程序,于是该计算解释开销的过程完成。如果在步骤912判定程序计数器确实指向解释程序,则在步骤916,递增解释程序开销计数器。最后在步骤918,用该解释程序开销计数器来计算解释程序开销的滑动平均数,然后该计算解释开销的过程完成图10表示一个适于实现本发明的通用计算机系统。计算机系统1030包括任意数量的处理器1032(也称中央处理单元或CPU),处理器与内存设备相连,内存设备包括基本存储器1034(一般是只读存储器即ROM)和基本存储器1036(一般是随机存取存储器即RAM)。
计算机系统1030,更具体来说,CPU1032,可以被安排去支持一个虚拟机,这一点本领域的熟练人员都知道。下文讨论图11时将描述一例计算机系统1030支持的虚拟机。本领域众所周知,ROM的作用是向CPU1032单向传输数据和指令,而RAM一般则用于双向传输数据和指令。CPU1032一般可以包括任意数量的处理器,两个基本存储器1034和1036都可以包括任何计算机可读的合适的介质。二级存储器1038一般是大容量存储器,它也双向地连接CPU1032,提供额外的存储器量。大容量存储器1038是一种计算机可读的介质,可用于存储包括计算机代码、数据等的程序。一般来说,大容量存储器1038是一种诸如硬盘或磁带的存储介质,速度一般比基本存储器1034、1036慢。大容量存储器1038的形式可以是磁带机、纸带机或其它已知设备。应当明白,大容量存储器1038中存储器的数据,在适当的情况下可以采用与RAM 1036相同的方式而充当虚拟内存。诸如CD-ROM的特殊基本存储器1034也可以单向地向CPU1032传递数据。
CPU1032也连接一个或多个输入/输出设备1040。输入/输出设备1040包括但不限于视频监视器、轨迹球、鼠标、键盘、麦克风、触摸屏、穿卡机、磁带机、纸带机、书写板、记录笔、声音或手书识别器或其它已知的输入设备,当然也包括其它计算机。最后,CPU1032最好用图中1012简单表示的网络连接,连接到计算机网络或通信网络,例如局域网、内连网或因特网。有了这种网络连接,就可以考虑在执行以上所述方法步骤的过程中用CPU1032从网络接收信息,或者向网络输出信息。这种信息经常表现为要由CPU1032执行的指令序列,例如以在载波中体现的计算机数据信号的形式,既可以从网络接收,也可以向网络输出。计算机软件和硬件的熟练人员都知道上述设备和材料。
如上所述,计算机系统1030上可以运行一个虚拟机。图11是表示一个由图10的通用计算机系统1030支持的虚拟机的示意图。当计算机程序(例如用JavaTM程序设计语言编写的计算机程序)执行时,源代码1110被提供给编译时环境1105内的编译程序1120,Java是由美国加州Palo Alto的Sun Microsystems公司开发的一种程序设计语言。编译程序1120将源代码1110翻译成字节代码1130。一般来说,源代码1110是在软件开发人创建源代码1110的时候被翻译成字节代码1130的。
字节代码1130一般可以被复制、下载、或通过网络(例如图10的网络1012)以其它方式分发、或者在图10基本存储器1034之类的存储器上存储。在所述实施例中,字节代码1130是独立于平台的。就是说,字节代码1130可以在运行适当的虚拟机1140的几乎任何计算机系统上执行。例如,在JavaTM环境中,字节代码1130可以在运行JavaTM虚拟机的计算机系统上执行。
字节代码1130被提供给包含虚拟机1140的运行时环境1135。运行时环境1135一般可用图10的CPU1032之类的处理器执行。虚拟机1140包括编译程序1142、解释程序1144和运行时系统1146。字节代码1130一般既可以提供给编译程序1142,也可以提供给解释程序1144。
如果将字节代码1130提供给编译程序1142,如上所述,字节代码1130中含有的子程序被翻译成机器指令。另一方面,如果将字节代码1140提供给解释程序1144,则字节代码1130被读入解释程序1144,一次一个字节代码。解释程序1144然后一边读入各字节代码一边执行由各字节代码定义的操作。一般来说,解释程序1144几乎是连续地处理字节代码1130并执行字节代码1130相关的操作。
当操作系统1160调用一个子程序时,如果判定调用的子程序是个被解释的子程序,运行时系统1146就可以从解释程序1144获得该子程序。另一方面,如果判定调用的子程序是个编译子程序,运行时系统1146就启动编译程序1146。编译程序1146然后根据字节代码1130输出机器指令,并执行这些机器语言指令。一般来说,当虚拟机1140停止时,机器语言指令就被丢弃。
尽管本文只描述了本发明的几个实施例,应当明白,在不偏离本发明本质或范围的前提下,本发明可以以许多其它方式实施。举例来说,执行重新编译程序涉及的诸步骤可以调换顺序,也可以增、减。此外在某些实施例中,将子程序直接插入子程序的调用程序这一选择也可以弃之不用。一般来说,在不偏离本发明本质或范围的前提下,本发明的诸多方法所涉及的诸步骤均可以调换顺序,也可以增、减。
尽管本文在描述重新编译程序的执行时,说的是判断编译程序是否要编译一个给定子程序以及编译给定子程序,不过应当明白,对给定子程序的编译可能要推迟到判定应当编译该给定子程序之后进行。如果有多个子程序几乎同时在编译,则对给定子程序的编译可能要推迟到只有很少子程序在编译时,例如编译开销降到最小期望编译开销以下时。推迟对给定子程序的编译有助于防止编译开销超过最大期望编译开销。此外,推迟编译还有助于防止程序执行期间发生较长时间的编译暂停。在一个实施例中,对程序的子程序的编译可能会推迟到总体程序执行中有暂停的时候。在这种实施例中,在总体程序执行期间,可以将子程序插入一个在暂停期间或低工作量期间被访问的队列。
此外,本文在描述本发明时,说的是所有子程序初始时都被解释,不过应当明白,当字节代码形式的计算机程序最初被提供到运行时环境时,至少有一些与该程序相关的子程序可以立即编译。例如,为了在执行开始时程序能符合期望的编译开销,有些子程序初始时被编译,有些子程序初始时被解释,直到对这些解释子程序的调用次数超过某个阈值,这时才编译这些解释子程序。
尽管在某个解释子程序内设置的计数器(如上文说明图3时所述)一般可以设置在子程序内的任何位置,不过如果在子程序运行期间访问该计数器,代价是昂贵的。因此,如果要在解释子程序内设置计数器,可以将计数器设置在子程序的开头,这样就可以更容易地访问计数器。在有些实施例中,由于涉及到与访问子程序中的计数器有关的代价,所有子程序使用一个全局计数器。当全局计数器达到阈值时可以考虑对正在运行的子程序进行重新编译,如同当前子程序达到其调用计数器阈值一样。
一般来说,计数器可以在调用含有计数器的子程序期间的任何时候递增。例如,计数器可以在子程序内部循环中执行向后转移时递增。在循环的向后转移时或者循环的几乎任何位置递增计数器,有利于发现子程序中的长运行循环。在子程序的一次调用期间,长运行循环可能会反复执行。通过在循环内-例如循环的向后转移的位置-设置计数器,每次循环都作为调用该子程序而被计数,由此能使占用大量执行时间的子程序更可能会被编译。应当明白,在有些实施例中,循环中的计数器递增一个比子程序内部计数器更容易访问的“全局”循环计数器,这时因为在子程序运行时访问子程序计数器的代价是昂贵的。
在一个使用“循环计数器阈值”的实施例中,循环计数器阈值不同于上述的调用阈值。循环计数器阈值也可以用不同的阈值系数进行增减。然而,一般来说,循环计数器阈值的调整与调用阈值的调整实际上是合作进行的。
计数器一般都被描述成是设置在子程序的内部。然而应当明白,计数器除了设置在子程序内部,也可以设置在每次调用子程序时能访问的数据库或表中。在对应某计数器的子程序每次被调用时,该数据库被更新,更确切地说,数据库中的该计数器被更新一这并不偏离本发明的本质或范围。
一般来说,根据本发明的阈值,其范围变动很大。例如,尽管阈值为1000时对有些执行是足够的,但是对几乎所有子程序执行次数都超过1000的执行来说,采用更高的阈值才合适。与此类似,最小、最大期望编译开销以及最小、最大期望解释开销,根据特定系统的要求,它们值的范围变动也很大。
阈值大小的选择依据,有各种不同的因素。这些因素其中包括但不限于程序执行的百分率。换言之,阈值的选择要使得占总体程序执行的一定百分率(例如一半以上)的子程序被编译。例如,可以这样调整阈值,使得占总体程序执行的95%的子程序被编译。
此外,尽管本文在描述优化字节编码程序时,说的是采用一个编译级或两个编译级,不过,执行字节编码程序相关的编译级的个数的范围变动一般可以很大-这并不偏离本发明的本质或范围。一般来说,许多不同因素决定着执行字节编码程序相关的编译级的个数,这些因素包括但不限于对优化程序的期望程度。每个编译级都有独立的阈值,尽管几乎所有编译级的阈值可以是一样的。对于有多个编译级的系统来说,每次调用子程序时递增的计数器可以被设置在各编译子程序中。这些计数器与以上所述设置在解释子程序中的计数器一样,可用来判断何时适于对编译子程序进行重新编译。
此外,尽管本文在对判断是否要编译子程序的说明中,所述的判断要根据子程序相关的计数器的阈值而定,不过,判断是否要编译子程序也可以根据任意数量的不同因素而定。例如,整个操作系统中的时间“滴答”就可以用来发现那些执行费时、编译有益的子程序。就是说,在很多时间“滴答”中执行的子程序,应当考虑对其编译,以减少该执行该过程花费的时间。
执行计数器或调用计数器一般是每当子程序被访问时就递增一次的计数器。应当明白,在有些实施例中,调用计数器会随时间推移而衰减。例如,如果某子程序在程序执行的开始部分被反复调用,但是在相当长的时间内没有再被调用,那么调用计数器就会衰减,以降低如果该子程序以后被调用则要编译该子程序的可能性。一旦某子程序的调用计数器已经衰减,例如指数级地衰减,就不再有理由编译该子程序。因此,本文的例子应视为是解释性的而不是限制性的,本发明也不局限于本文的所述的细节,可以在权利要求的范围内作出改进。
权利要求
1.一个在运行期间处理计算机程序的计算机实现的过程,其中程序包含字节代码,该计算机实现的过程包括调用一个从多个子程序中选定的子程序,其中,调用该选定子程序包括解释该选定子程序;更新一个被安排去跟踪该选定子程序的调用的调用跟踪器;判定调用跟踪器何时指示选定子程序的调用次数超过某个阈值;当判定调用跟踪器指示选定子程序的调用次数超过该阈值时,编译该选定子程序。
2.权利要求1所述的计算机实现的过程,进一步包括检测编译该选定子程序相关的编译开销。
3.权利要求2所述的计算机实现的过程,进一步包括判定编译开销何时处于某个可接受的范围内;当判定编译开销不在可接受的范围内时,就调整该阈值。
4.权利要求2和3中之一所述的计算机实现的过程,其中,检测编译开销包括计算编译开销的一个滑动平均数。
5.权利要求2~4所述的计算机实现的过程,进一步包括检测解释选定子程序相关的解释开销。
6.权利要求5所述的计算机实现的过程,进一步包括判定解释开销何时处于某个可接受的范围内;当判定解释开销不在该可接受的范围内时,就调整该阈值。
7.上述中各权利要求中任一个权利要求所述的计算机实现的过程,其中,调整该阈值包括增大该阈值和减小该阈值。
8.上述中各权利要求中任一个权利要求所述的计算机实现的过程,其中,更新调用跟踪器包括递增在选定子程序中设置的一个计数器。
9.上述中各权利要求中任一个权利要求所述的计算机实现的过程,进一步包括调用该编译的选定子程序;更新一个用来记录该编译的选定子程序的调用次数的跟踪机构;判断跟踪机构何时指示该编译的选定子程序的调用次数超过了某界限值;如果判定该跟踪机构指示该编译的选定子程序的调用次数超过了该界限值,重新编译该编译的选定子程序。
10.一种用于执行计算机程序的计算机实现的过程,其中计算机程序包括多个子程序,这多个子程序包括优化状态不同的选定子程序,该计算机实现的过程包括在计算机程序的执行期间从多个子程序中选择一个子程序,其中该子程序处于第一种优化状态;在计算机程序的执行期间将该子程序从第一种优化状态转换到第二种优化状态,其中该第二种优化状态比第一种优化状态更优化,将该第一子程序从第一种优化状态到第二种优化状态的转换包括平衡转换该子程序的代价与转换该子程序的好处。
11.权利要求11所述的计算机实现的过程,进一步包括执行该子程序,其中执行第一种优化状态下的该子程序比执行第二种优化状态下的该第一子程序慢。
12.权利要求10和11中之一所述的计算机实现的过程,其中第一种优化状态,是从由一个解释状态和一个编译状态组成的状态组中选择的一种状态。
13.权利要求10~12中任意一项权利要求所述的计算机实现的过程,其中,从多个子程序中选择该子程序包括在该计算机程序的执行期间检测该第一子程序的调用次数。
14.一种计算机实现的执行计算机程序的方法,其中计算机程序包含多个子程序,该计算机实现的方法包括调用从多个子程序中选定的某第一子程序;判断该第一所选子程序何时要被某编译程序处理,其中,判断该第一所选子程序何时要被该编译程序处理根据的是与该第一所选子程序的调用相关的一个阈值;如果判定该第一子程序要被该编译程序处理,则用该编译程序处理该第一子程序;如果判定该第一子程序要被该编译程序处理,调整与该计算机程序关联的某种开销的一个量度;判断该种开销的量度是否表示该种开销是可以接受的;如果判定该种开销是不可接受的,调整该阈值。
15.一个执行字节代码的计算机系统,其中字节代码被组织成多个子程序,该计算机系统包括一个解释程序,它被安排去解释从该多个子程序中选定的第一子程序;一个第一种跟踪机构,它被安排去为该第一选定子程序的解释计数,该第一跟踪机构进一步被安排去判断该第一选定子程序何时适合编译;一个编译程序,它被安排在判定该第一种选定子程序适合编译时,编译该第一选定子程序。
16.权利要求15所述的计算机系统,进一步包括一个阈值机构,它被安排与第一种跟踪机构合作判断该第一选定子程序何时适合编译,其中该阈值机构被安排去设置一个指示该第一选定子程序何时适合编译的阈值。
17.权利要求15和16中之一所述的计算机系统,进一步包括一个第二种跟踪机构,它被安排去在该第一选定子程序被编译时为该第一选定子程序的调用计数,该第二种跟踪机构进一步被安排去判断该第一选定子程序何时适合重新编译;一个重新编译程序,它被安排在判定该第一选定子程序适合重新编译时,重新编译该第一选定子程序。
18.一种用于处理计算机程序的计算机程序产品,其中计算机程序包括字节代码,字节代码被组织成多个子程序,该计算机程序产品包括调用从多个子程序中选择的第一子程序的计算机代码,其中调用该第一选定子程序包括解释该第一选定子程序;更新某个被安排去跟踪该第一选定子程序的调用的调用跟踪器的计算机代码;判断调用跟踪器何时指示该第一选定子程序的调用超过某个阈值的计算机代码;当判定该调用跟踪器指示该第一选定子程序的调用超过某个阈值时编译该第一选定子程序的计算机代码。
19.根据权利要求18的计算机程序产品,其中,计算机程序产品是在载波中实现的数据信号。
全文摘要
本文介绍用于在运行期间动态地确定应当解释还是编译部分代码,以优化软件应用程序的方法和装置。本发明的一个内容是,对包含组织成多个子程序的字节代码的计算机程序进行运行时处理的计算机实现的方法中,包括一个对该多个子程序中第一个选定子程序的调用。调用该第一个选定子程序,包括解释该第一个选定子程序。被安排去跟踪该第一个选定子程序的调用次数的一个调用跟踪器被更新,对调用跟踪器何时指示第一个选定子程序的调用次数超过某个阈值进行判定。当判定调用跟踪器指示第一个选定子程序的调用次数超过某个阈值时,该子程序被编译。定期调整阈值,以保持编译和解释开销在可接受的范围内。
文档编号G06F9/45GK1234551SQ9811829
公开日1999年11月10日 申请日期1998年10月6日 优先权日1997年10月6日
发明者U·赫尔茨勒, R·格雷瑟梅尔, D·格里斯沃尔德 申请人:太阳微系统有限公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1