基于线索化方法的嵌入式Java虚拟机性能优化技术研究 - 李允

更新时间:2024-04-03 00:03:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

基于线索化方法的嵌入式Java虚拟机性能优化技术研究

李 允1,罗 蕾2,雷昊峰2,熊光泽2

1(西南交通大学计算机与通信工程学院,四川成都610031)

2(电子科技大学计算机科学与工程学院,四川成都610054)

E-mail: liy@ coretek. com. cn

摘 要:随着计算机的不断发展,逐渐呈现出了普适计算的模式.普遍认为, Java是适应普适计算的关键技术.分析了解释运行

中利用线索化方法进行性能优化的技术,实现了基于直接线索化方法的嵌入式Java虚拟机的解释器性能优化方案,并对嵌入

式Java虚拟机的参考实现和基于直接线索化的优化方案进行了性能对比. 关键词:嵌入式Java;线索化方法; Java虚拟机;性能优化

中图分类号:TP316;TP273 文献标识码:A 文章编号: 1000-1220(2005)03-0439-04

PerformanceOptim ization Technology Formbedded JavaV irtualM achine Based on Threaded M ethod

LIYun1, LUO Lei2, LEIHao-feng2, XIONG Guang-ze2 1(SchoolofComputer and CommunicationsEngineering,SouthwestJiaotongUniversity,Chengdu610031,China)

2(Computer Science and Engineering College,University ofE lectronic Science and Technology,Chengdu610054,China)

Abstract:As the development of computing technology, there is the requirement of anywhere computing to provide services a

anytime. In fact, Java has been the key technology of pervasive computing. Analyzed a performance optim ization technology

uses the threaded method in interpreting running modes and implements a performance optim ization of embedded Java VM

based on direct threaded technology. The paper demonstrates the comparison performance of embedded Java VM and direct threaded optim ized VM.

Key words: embedded Java; threadedmethod; JavaVM; performance optim ization 1 引 言

随着计算机技术的深入发展,网络计算和移动计算将很

快成为人们日常生活的一部分[1],并期望不断涌现出来的移 动终端能够以快速、高效和便捷的模式为人们提供服务,逐渐 呈现出普适计算的模式. Java由于其平台无关特性及其庞大 的应用开发团队等显著特点,逐渐在移动终端领域形成了强 大的需求.并且, Java还被普遍认为是使移动计算无处不在, 到处都能进行电子商务活动的关键技术.

移动终端拥有的资源越来越丰富,但同桌面系统相比,移

动终端在存储空间、处理能力和屏幕大小等方面的资源仍然 比较有限.为此,SUN推出了针对移动终端的、适合于嵌入式 应用的Java技术—J2ME.另外,移动通信技术的进步为移动 终端使用Java技术提供了广阔的空间.随着移动通信技术的 发展,网络传输速度不断提高,移动通信系统将逐渐由提供话 音为主的服务发展为以提供数据为主的服务,逐渐具备了为 移动终端提供多样性应用的服务能力.

为满足嵌入式应用的需要,尽管作为嵌入式Java的J2ME 进行了不少适应性的修改,但仍要求终端具有比较丰富的资 源.虽然能够用于移动终端的资源已经越来越丰富,但由于功 耗、成本等因素的制约,以及移动终端自身的多样性,使得移 动终端仍存在资源方面对低端CPU和有限存储空间等方面 的需求,以实现终端在功耗小、成本低、重量轻和体积小等内 容的目标[2].对终端的Java进行性能优化,不但能降低Java对 资源的需求,满足更广泛嵌入式应用的需要,还能有效降低 Java应用的功耗情况,并使移动终端进行电子商务、电子银 行和3D游戏等复杂应用成为可能.

由于解释运行的虚拟机存在系统简单、容易实现等优势, 且由于编译运行存在重复编译、编译过程需要消耗大量资源 等不足,使得嵌入式应用中大多采用Java的解释运行方式. 本文分析了基于解释运行的性能优化技术,实现了针对嵌入 式Java虚拟机的性能优化方案,并同J2ME的参考实现进行 了性能对比,获得了较好的性能优化效果. 2 线索化优化技术

在程序的解释执行中,大多采用sw itch-case解释器,通

过sw ith-case的方式来定位执行对象所对应的解释程序.这 种方式实现起来比较简单,易于编写和移植.但由于在一些解 释器中,大多数解释程序段都比较简单,指令非常少(只有几 条到十几条机器指令),使得sw itch-case语句自身的开销比 较突出.作为对传统的sw itch-case解释执行方式的改进,线 索化的解释器[3-4](threadedinterpreter)最早在Forth语言解 # defineOP-LDC 0 # defineOP-ILOAD 1 # defineOP-ISTORE 2 # defineOP-ADD 3 # defineOP-SUB 4

?

uint8 bytecodes[]= {

OP-LDC,OP-ILOAD,OP-ADD,OP-ISTORE,? };

void* labels[sizeof(bytecodes)]; labels[0]=&&OP-LDC-LABEL;

/*对应bytecodes[0]=OP-LDC* / labels[1]=&&OP-ILOAD-LABEL;

/*对应bytecodes[1]=OP-ILOAD* / labels[2]=&&OP-ADD-LABEL;

/*对应bytecodes[2]=OP-ADD * / labels[3]=&&OP-ISTORE-LABEL;

/*对应bytecodes[3]=OP-ISTORE* / ?

void* instructionLabel= &labels[0]; goto* instructionLable++; OP-LDC-LABEL: interpret LDC;

goto* instructionLable++; OP-ILOAD-LABEL: interpret ILOAD;

goto* instructionLable++; OP-ISTORE-LABEL: interpret ISTORE;

goto* instructionLable++; OP-ADD-LABEL: interpretADD;

goto* instructionLable++; OP-SUB-LABEL: interpretSUB;

goto* instructionLable++; ?

图1 直接线索化解释器示意程序

释器中得到应用.线索化的解释器意味着解释器对相邻字节 码的解释执行过程通过一定的方式直接联系在一起,而 sw itch-case解释器每解释完一条字节码都要重新经过由 sw itch进行解释分发的过程.线索化解释器将字节码的处理 程序的入口地址集中保存在标号数组中,解释执行字节码时 需要至少两次内存访问: 1)读入PC指向的字节码; 2)以字节 码作为下标,读入对应的处理程序的地址.直接线索化解释 器[4](direct threaded interpreter)为对线索化解释器的一种改 进方案,在运行字节码之前将所有字节码所对应的处理程序 的地址保存在一个数组中,在解释运行时不再访问原来的字 节码数组,而是直接访问地址数组,因而在线索化解释器的基

础上减少了一次内存访问的开销.直接线索化解释器的实现 可以用图1所示的伪码表示.与sw itch-case解释器执行过程 相比,直接线索化解释器减少的开销包括一次跳转,一次访存 和对字节码的值进行的边界检查操作.通过实际的实验结果 表明,直接线索化解释器的速度比sw itch-case解释器快40% 到100%.但是,由于需要额外的内存空间来存放解释程序的 地址数组,直接线索化解释器的内存开销要大于sw itch-case 解释器和线索化解释器(需要约4倍于字节码自身大小的内 存空间).

直接线索化解释器在每条字节码解释完后仍然需要一条 跳转指令,转到下一条字节码对应的解释程序入口.这样频繁 的跳转对CPU的指令cache和分支预测机构仍然会带来很大 的影响.内联线索化的解释器[5](inlined threaded interpreter) 为直接线索化解释器的改进方案,将若干连续字节码对应的 解释程序本身(而不是标号)复制到一个连续的地址空间中, 然后运行这一段解释程序,这样就完全消除了在字节码解释 程序间的访存和跳转指令的开销.可以看出,内联线索化解释 器以一种简化的方式实现了类似JIT的代码生成工作.其代 价是需要占用大量的内存(需要约数十倍于字节码自身大小 的内存空间).

3 Java虚拟机性能优化技术

采用直接线索化解释器作为嵌入式Java虚拟机解释器 的核心实现方案,其原因是: 1)sw itch-case方式实现的字节 码解释器性能过于低下; 2)线索化解释器与sw itch-case解释 器相比性能有一定提高(7%~10% ),但提高幅度仍然有限; 3)内联线索化的解释器与sw itch-case解释器相比性能有很 大提高(150%~300% ),但内存占用太大; 4)直接线索化解释 器与sw itch-case解释器相比性能有显著提高(40%~ 100% ),同时仍然保持较少的内存占用. 图2 线索化代码数组

解释器在运行字节码之前把字节码数组翻译为直接包含 字节码对应解释程序入口的线索化代码数组,如图2所示.实 现以上翻译过程的代码可以用图3所示的伪码表示.翻译程 序按顺序读入每一个字节码,查找其对应的解释程序入口地 址,再将地址写入线索化代码数组中.

实际上,图2和图3的伪码只考虑了最简单的字节码翻译 过程,即每个字节码对应线索化代码的一条数据(通常为4字 节),其中操作码对应翻译程序标号地址,操作码后续的操作 数则直接复制到线索化代码中.无论是操作码还是操作数,每 一个字节码在翻译后都要占用4字节的存储空间.在实际的 翻译过程中,有两个问题需要解决: 1)操作数的翻译; 2)相对 跳转地址的处理.

/*保存字节码解释程序入口地址* / void* opcode-entries[MAX-OPCODE];

/*初始化解释程序入口地址表* /

opcode-entries[OP-CODE1]= &&OP1-ENTRY; opcode-entries[OP-CODE2]= &&OP2-ENTRY; opcode-entries[OP-CODE3]= &&OP3-ENTRY; ?

/*将字节码翻译为threaded code* / uint8* pc= bytecodes[];

int bytecodes-length= length of bytecodes; void** threadedPC = threadedCodes[]; while (bytecodes-length--) { if (IS-OPCODE(* pc)) {

/*该字节是操作码,翻译为解释程序入口地址* / * threadedPC = opcode-entries[* pc]; } else {

/*该字节是操作码的参数,直接保存* / * threadedPC = * pc; }

threadedPC++; pc++; }

图3 嵌入式Java虚拟机性能优化的示意程序

在32位CPU上,标号地址(类型为void* )长度为32位,

占用4个字节的存储器.一条Java操作码的后续操作数长度 从1字节到数十字节不等,这些操作数有的是16位整数,长为 2个字节;有的是32位整数,长为4个字节.如果按照图2的翻 译方式, 16位的操作数在翻译后将占用2个线索 图4 线索化代码数组

化代码单元,即8个字节;而32位操作数则要占用16个字节. 并且,在线索化代码运行时,还需要重新“组装”这些操作数. 这样做既增加了无谓的内存占用,又降低了运行效率.因此, 在翻译过程中,通常是将一个操作数的若干个字节码合并在 一条线索化代码单元中,如图4所示.

如图4所示,在翻译时对字节码操作数进行合并处理,可 以在一定程度上降低线索化代码的内存占用,同时提高了运 行效率.同时也可以看出,合并操作数使得字节码数组与线索 化代码数组的下标不再具有一一对应关系.对顺序执行的字 节码而言,这种变化没有影响,但对于进行相对地址跳转的字 节码而言,情况就发生了变化.以图4为例,假设字节码数组 中下标为2的OP-CODE2是一条跳转指令,其后的操作数是 相对地址,值为8,则OP-CODE2的执行结果为跳转到下标为 10的指令处.而在线索化代码中,OP-CODE2的解释程序则 应该跳转到对应的指令,即下标为6的线索化代码处.因此, 在对跳转指令进行翻译时,必须对操作数进行调整.

在上述内容的基础止,还可以采用字节码替换优化、线索

化代码存储空间优化和提高CPU指令Cache命中率等进一步 的优化措施.

3.1 字节码替换优化

字节码替换主要包括以下几个方面的内容:

1)将某些不指定操作数类型的指令细分并改写为针对

特定类型的指令.例如, LDC指令将常量表中指定的常量压 入Java堆栈,它的操作数(指定常量的下标)也是一个常量. 在翻译代码时可以得知不同的LDC指令所装入常量的数据 类型,并将LDC指令细分为LDC-INT, LDC-FLOAT, LDC- STRING等指令,以提高运行效率;

2 )将常量操作数直接嵌入在线索化代码中.每一个Java

方法都有自己的常量表,程序运行时通过LDC指令和一个下 标操作数来将常量表中指定的常量压入Java堆栈.可以将长 度不超过4个字节的常量直接嵌入到线索化代码中,例如整 型常量、字符串指针常量等,这样可以减少一次查表的开销; 3)合并某些功能相近或相同的指令.例如, BIPUSH和

SIPUSH指令分别向一个8位整数和16位整数压入堆栈,考 虑到8位和16位整数在线索化代码和堆栈上实际都要占用4 个字节,这两条指令可以由同一段解释程序来执行. 3.2 线索化代码存储空间优化

字节码翻译为线索化代码后,需要占用原字节码大小3~ 4倍的存储空间.相对内联线索化解释器而言,开销已经大大 减小,但对于内存资源有限的移动终端而言,这样的开销仍然 不可忽视.

线索化代码存储空间优化技术即用来降低线索化代码的 内存占用情况,其工作原理是:

1)开辟一块专门用于存放线索化代码的内存空间; 2) Java方法只在被运行之前才翻译为线索化代码; 3)通过LRU(最近最久未用)表对已翻译为线索化代码 的Java方法的被调用情况进行记录;

4)当存放线索化代码的内存空间不足以容纳新的需要

翻译的Java方法时,在LRU表中查找最近最久未调用的线索 化代码,并释放这段代码,有必要的话,重复这个过程直到可 以容纳新的Java方法为止.

通常,由于移动终端本身存储能力的限制, Java应用代

码的规模通常不会超过100Kbytes,大多数常用应用不会超过 30K~40Kbytes.在这个前提下,通过LRU表来维护线索化 代码的内存空间,能够大大降低线索化代码所占用的内存. 3.3 提高CPU指令cache命中率

Java虚拟机一共定义了204条指令,这些指令在Java程

序中出现的频率是不一样的.表1为Radhakrishnan[6]根据几 个常用的Java性能测试程序所得到的Java指令在运行时的 使用频率.

表1 Java指令使用频率 指令分类测试用Java程序

compress jess db javac mpegaudio m trt Jack

常量表访问23. 3% 21. 6% 16. 8% 14. 6% 17. 1% 20. 69% 32. 0% 堆栈访问8. 8% 3. 5% 7. 7% 5. 8% 7. 1% 4. 1% 13.%

装入变量34. 3% 35. 5% 37. 8% 37. 9% 44. 2% 28. 2% 30. 9% 保存变量10. 6% 6. 6% 8. 0% 7. 5% 8. 3% 3. 5% 2. 1% 算术运算11. 2% 6. 1% 8. 8% 12. 8% 17. 1% 7. 8% 5. 8% 分支6. 1% 9. 6% 10. 2% 8. 6% 3. 4% 5. 1% 11. 0% 跳转0. 4% 1. 1% 1. 1% 1. 3% 0. 4% 0. 8% 0. 5%

方法调用5. 4% 15. 7% 9. 2% 10. 8% 2. 5% 29. 3% 4. 1% 表操作0. 0% 0. 3% 0. 3% 0. 7% 0. 0% 0. 7% 0. 0% Radhakrishnan的统计还表明, Java程序中使用频率最

高的15条指令,其运行次数占所有指令运行次数的56%到 85%.这些使用频率最高的指令随程序不同而不完全一致.但 总的来说,常量表访问指令、变量表访问指令、算术运行指令 和分支指令的使用频率相对其他指令要高得多. 表2 性能比较

虚拟机嵌入式Java虚拟机 参考实现

基于直接线索化技术 的优化版本

测试案例运行时间44秒26. 7秒 性能比较比参考实现提高64. 7%

为此,将使用频率高的指令的解释程序安排在相邻的地

址空间中,可以提高CPU指令cache的命中率,进而改善虚拟 机的性能. 4 性能比较

嵌入式Java虚拟机性能优化是在嵌入式Java—J2ME参 考实现的基础上来实现的.表2列出了运行自行编写的综合 测试案例的结果.该测试案例包含大量的循环、整数乘除法运 算、对象分配和虚方法调用.测试是在运行于x86 PC工作站 的Linux操作系统上进行的,工作站装备了1Ghz主频的 Celeron CPU和512M RAM.

从该测试案例的结果来看,采用了基于直接线索化优化

技术实现的嵌入式Java虚拟机的性能比参考实现高出约64. 7%.

5 结束语

在目前的计算环境下, Java技术逐渐在移动终端领域形 成了广泛的应用需求.尽管移动终端的资源已经越来越丰富, 但仍需要对终端所使用的Java进行性能优化,以降低Java对 资源的需求,满足更广泛嵌入式应用的需要,并有效降低Java 应用的功耗情况,使移动终端进行电子商务、电子银行和3D 游戏等复杂应用成为可能.

本文详细分析了基于线索化方法的解释器优化技术,并

在嵌入式Java虚拟机中实现了基于直接线索化的解释器优 化方案.文中对嵌入式Java虚拟机的直接线索化优化方案进 行了详细描述,并采用了字节码替换、提高CPU指令cache命 中率等执行时间优化措施和基于线索化代码的存储空间优化 技术.通过性能对比,说明了优化后的Java虚拟机解释器的 性能得到了比较显著的提高. References:

[1 ]RandyH Katz. Adaptation andmobility in w ireless information systems[J]. Adaptation and M obility in W ireless Information Systems, August 1995, 1: 6-17.

[2 ] Brian D Noble. M obile data access[EB /OL]. CarnegieM ellon University, 1998. http: //www. cs. cmu. edu /afs/cs/ project/ coda/W eb /docdir /bnoble-thesis. pdf.

[3 ]M oore C H, Leach G C. FORTH-a language for interactive computing, 1970[EB /OL]. http: //www. ultratechnology. com / 4th-1970. htm l.

[4 ] James R Bell. Threaded code[J]. Communications of theACM , 1973, 16(6): 370-372.

[5 ] Ian Piumarta, Fabio Riccardi. Optim izing direct threaded code by selective inlining [C ]. SIGPLAN Conference on Program- m ing Language Design and Implementation, 1998: 291-300. [6 ]Radhakrishnan R, Rubio J, John L et al. Execution char-acter- istics of just-in-time compilers [R ]. Technical Report TR- 990717-01, Department of Electrical and Computer Engineer- ing, University ofTexas atAustin, 1999.

本文来源:https://www.bwwdw.com/article/26dr.html

Top