优化Java 理论和操作外文翻译资料

 2022-12-03 11:27:37

英语原文共 13 页,剩余内容已隐藏,支付完成后下载完整资料


优化Java

理论和操作

Zoran Budimlic

Ken Kennedy

赖斯大学计算机科学系

6100 South Main, Houston TX 77005

zoran@cs.rice.edu, ken@cs.rice.edu

摘要:互联网的赫赫声望使Java编程语言成为了新星。Java的可移植性、可重用性、安全性及其简洁的设计,使它成为了基于互联网的应用程序并且成为了一个受欢迎的c 面向对象编程的语言选择。不幸的是,标准的Java实现的性能以及即时编译技术,是远远落后于当今最流行的语言。积极优化Java编译器的必要性是显而易见的。

在JavaSoft字节码优化器初步经验的基础上,本文探索了一些建立在有效的Java实现基础上出现的问题。Java语言设计过程中许多有趣的问题出现了,以致经典优化策略更难以实现。另一方面,Java语言提供了一些新的优化和独特的地方。

  1. 介绍

在本文中,我们展示的是随着莱斯大学继续研究的初步结果,基于优化Java在JavaSoft太阳分工微系统公司完成的夏季项目的结果。当我们试图优化Java编译问题时,出现了许多有趣的问题,包括:在编译时无法获得完整的程序,消除了程序间优化的机会;异常机制:限制运动优化代码和高抽象层次的Java虚拟机指令代码(字节码),其隐藏了许多机器相关的编译器的优化机会。本文讨论了这些问题并提出了一些解决方案,并提出了需要进一步研究的地方。

本文分为以下几个部分。第二部分描述了可能优化Java的方法。第三节介绍了我们当前研究编译器的设计,包括实现优化和需要应用的编译模型的优点和局限性。第四节描述了简化编译的Java优化模型的方法并展示了一些新优化的初步结果。第五节描述了一个激进的Java优化策略,讨论它的潜力和适用性。第六节介绍了Java异常机制,提出了一种优化的问题无论在哪种策略下。最后一节对这个初步工作做出总结,并提出了未来研究的方向。

  1. 最优策略

优化Java编译有三种不同的策略。对于特定的应用程序和情况,每种方法都有各自的优点和缺点。在本文中,我们将描述它们中的每一种情况,并讨论它们在不同的问题上的适用性。

第一个方法是保持在由Sun Microsystems公司定义的Java编译模型——代码生成Java虚拟机(VM),编译器每次处理一个类,其生成的代码独立于其执行的平台。这意味着编译器不能对编译器执行的机器VM代码做出任何假设,同时虚拟机也不能对用于生成虚拟机字节码的编译器做任何假设。

这个模型用于在本文第三节讨论的JavaSoft。这种方法有一个明显的优势:当前Java执行环境没有更改,因此Java技术的可移植性、安全性、功能性保存了下来。不幸的是,编译模型中,随着与Java特性的结合,只剩下有限的性能改进的余地。然而,对应用程序而言绝对可移植性和安全性是至关重要的,编译器必须留在该模型的边界。考虑到需求,无论多小的性能提升机会,每一个机会值得利用。

与此相反,另一种方法是牺牲Java虚拟机的可移植性和安全性,关注高度复杂、优化本地代码编译器的建设。尽管这将牺牲在互联网上执行VM目标代码所需要的可移植性和安全性。它的源代码将保持便携性并且在互联网上使用时,总是可以使用第一个编译策略重新编译。这种方法是为目前应用Fortran和C 开发的高性能服务应用程序提出的。第五节讨论了这种策略的一些重要方面。

第三种方法结合了前两个方法的特征,就是为了更好的性能,牺牲掉一些可移植性和功能。正如我们将在第四节说道的,放松目前编译和执行Java代码的限制,可以显著地影响性能。

通过互联网,这种模式仍将保持所有的便利和权力的执行,Java虚拟机安全模型也将执行。编译器和虚拟机彼此了解以此削弱绝对可移植性模型,所以将运用相反的知识来达到更高的性能。在这种组合下,编译过程不如在JavaSoft 模型中简单。该模型适合需要在网上执行的应用程序,需要所有Java VM的安全指令集,但愿意被限制在支持这种模式特的 编译器和虚拟机中。

正如前面提到的,每个策略各有其优点和缺点,应该根据满足目标应用程序的需要来选择特定正确的模型。

  1. 标准模型

在本节中,我们描述第一种方法即在我们当前的编译器使用的方法。这个项目是由在JavaSoft 暑期实习的学生做的,在设计和实施本项目时,许多有趣的问题在优化java时出现了,揭示了以前用的编译模型的一些局限性。

我们研的究编译器基于分布式Sun Java编译器,它包括一个独立的、字节码的优化器。选这个结构有两个原因,首先,这个项目是在JavaSoft开始时,因为优化器独立于当前构建的JavaSoft的 javac编译器,所以它更容易开发和调试。第二,这个结构也使得优化器独立于任何编译器,它可以用来优化通过其他Java编译器生成的代码,以及任何其他编译为Java VM的语言。第三,它可以为许多现在广泛使用的即时(JIT) 运行时编译器做优化准备。

编译器本身被组织成一系列彼此是松散连接的阶段:1、解析输入类并把它加载到内存中;2、把堆栈机器字节码转换成传统表达式树并构造控制流图 (CFG);3、把CFG转换为静态单一作业(SSA)的形式;4、除去一些标准最优化无用代码并在SSA不断扩展;5、把CFG从SSA中恢复,如果有必要,重命名本地的变量;6、在CFG 上执行本地优化,消除公共子表达式,复制推广和登记配置;7、Java VM字节码生成最终的程序,执行窥孔优化(堆栈分配,更换更便宜的运营商)。 在接下来的几小节中,我们将讨论其中的一些操作。

3.1高水平资源恢复

Java字节码并不是一个表示优化的特别合适的媒介。大多数文献中的优化适用于相似的表达代码,以及数量、寄存器而不是一个堆栈。最常见的中介表示是三地址和二地址指令序列,类似于组装语言[Aho等1986]。

Java VM堆栈机使得数据流分析更复杂。跟踪变量的运算符栈用法是我们遇到的最常见的困难。这让我们产生了积极性来开发一种更适合分析的表达-把Java VM的字节码翻译成表达式树。

转换过程非常简单。通过扫描分支的Java VM的字节码定义基本块并识别分支目标。当CFG的结构已经建立,我们继续在基本块中把字节码转换到表达式树。

表达式树的转换是通过使用一个表达式堆栈,模拟虚拟机器的行为来遍历基本块。有以下三种情况:当遇到一个构建的字节码,相应的常量或变量被推进一个表达堆栈,这个字节码创建常数或把一个局部变量推进堆栈;当模拟一个算术运算,从表达堆栈中取出适当数量的操作数,生成表达式再把它推入表达式堆栈;在堆栈的顶部存储值的字节码将被模拟,通过从表达式堆栈弹出一个局部变量和构建一项任务的形式。

图1示例字节码和生成的表达式。

图1

为每个基本块构造表达式树后,表达式栈必须在每一个基本块条中合并。当构建表达式时,表达式树构造器给局部变量任意分配“注册”名字。如果两个或两个以上的模块在他们退出时有非空的表达式栈,他们在CFG的同一点合并在一起,那么这个表达式栈必须是统一的。

这是通过统一每个栈的表达式和其他堆栈上适当的表达式做到的。必须被统一的一个复杂表达式被转换成一个临时变量和任务,那么变量与其他表达式统一起来。统一作业的代码被插入到成功合并的块的开始位置。在CFG分裂时也做类似的合并操作,在块分裂前,代码插入(如果有必要) 到块的底部。

3.2 SSA构建与使用

已知最快的算法的许多编译器优化使用SSA的形式来表示被优化。这是我们选择它作为我们编译器媒介的的主要原因。SSA建设和重建算法其他详细[Cytron 等1991][Briggs 等 1995]。

在Java项目的SSA建设和重建中仍有一个问题尚未解决。我们目前只在SSA的名字中转变方法的局部变量,忽略所有的实例变量。这是因为一些代码优化(循环移动不变的代码,例如)当恢复CFG时,要求SSA重建算法来重命名变量。当然,这对于实例和Java全局变量来说是不可接受的,因为这可能改变接口的类。我们相信从SSA中遗漏实例变量(从而在其中优化),生成的代码的质量会显著下降。我们计划使用布里格斯Schillingburg和辛普森[布里格斯等1995]的技术,此技术使用标签的内存位置处理实例变量。所有的SSA优化会意识到实例变量标签并将采取相应措施,所以重建CFG后,类接口将不会改变。

3.3死码的消除和数量繁殖

在传统语言中,死码消除是一个重要的和容易理解的优化[Kennedy 1981] ,[Briggs 等 1993] 。在应用这个优化于Java中时,理解Java异常机制是很重要的是。因为许多Java指令可能会导致一个异常,死码的消除算法准备工作必须标注所有的指令是至关重要的(例如不可删除),从而限制死码消除的机会。一些方法处理异常将在稍后讨论。对持续传播我们使用众所周知的Wegman-Zadeck算法[wegmans Zadeck 1985]。

3.4本地公有子表达式消除

在我们实现当地常见的子表达式消除中,我们使用一个众所周知的编号算法[Simpson 1996] ,[Briggs 等1996]。价值编号为公有子表达式消除提供了一个很自然的框架。所有的项目中的变量都有编号,如果两个变量有相同的值,他们就有相同编号。当两个表达式有完全相同的结构且他们操作数有相同的值,它们将有相同的值(并获得相同的值号)。在发现值数据后,发现常见的子程式语法、存储给定的子表达式值一次和替换所有的后续使用简单的加载计算都是很简单的。

3.5登陆和堆分配

Java虚拟机是一个堆栈机,正是如此,它没有通常意义上的登记概念。此外,它没有记忆的概念,只有一个潜在的巨大数量的局部变量来存储临时值和一个堆栈操作。然而,当访问堆栈和局部变量时,在速度有实质性的区别。

四个局部变量(0 - 3)有特别的代码加载和短于正常值的分配给他们的存储操作。这导致了使用这些变量时有更短的代码和更快的执行。同时,第一个64局部变量可以放入寄存器缓存一些正在开发中Java微处理器。大多数专门的Java微处理器,以及为传统的处理器运行时环境可以在他们的寄存器中隐藏一些临时变量,这是可以期待的。虽然速度差异不够显著,但是相对于寄存器与传统处理器、缓存和内存的访问时间而言,为实现一个完整的图色分配算法[Briggs 等1994],重要的是足以促使我们实现一个简单但快速、有效的启发式教育来探索不对称。寄存器分配算法如下:

bull;如果编译方法足够大,要为寄存器分配和重新装置考虑到输入值(最初存储在从0开始的局部变量中),否则他们会被留在原来的位置;

bull;对于每一个基本块,列出可用的寄存器;

bull;遍历每个块,分配可用的最低寄存器给已分配的变量值,取代局部变量和他们分配的寄存器的使用;

bull;如果当前指令最后一次使用变量,把相应的寄存器放到空闲列表中;

bull;为每个基本块分配寄存器后,寄存器被分配给总值。生存在基本块中被认为是全球性的每个局部变量,为了整个类函数,被分配一个独特的寄存器。寄存器从而被分配给这些变量,从分配在基本块寄存器最大的寄存器数值开始。虽然不如图表着色分配器那么积极和有效,这个简单的策略是相当有实践性的,特别是具有非常常见的面向对象编程风格的短方法。

正如我们已经指出的,操作栈通常是Java虚拟机中最快的存储器。尽可能利用堆栈存储中间值可大幅加速代码运行速度。我们通过代码使用一个非常简单的途径来发现重用栈的可能性:

bull;如果该值存储在一些恰好被使用的局部变量中,一旦下一个指令下达,我们消除存储和随后的加载,把值给栈。这是最常见的情况,

因为它是由以下情况导致的,即将大的表达式分成子表达式并将它们分别计算。

bull;如果存储在局部变量中的值被接下来的两个指令使用两次,存储和两个后加载的值由栈中值的副本替换。这种情况是3.1节中描述的一种常见的统一算法的结果,也是公共子表达式消除算法的结果。

再一次说明,这是一个非常简单的方法,虽然有更有效的算法代码生成堆栈,即最优化使用栈[Bruno Lassagne1975],但是,我们发现此方法很适合我们的需求。

3.6性能

<str

剩余内容已隐藏,支付完成后下载完整资料</str


资料编号:[25501],资料为PDF文档或Word文档,PDF文档可免费转换为Word

您需要先支付 30元 才能查看全部内容!立即支付

课题毕业论文、外文翻译、任务书、文献综述、开题报告、程序设计、图纸设计等资料可联系客服协助查找。