四、性能比较
作者使用了三个不同复杂程度的表达来测试以上两种不同的实现方式,它们的结果如图1所示。启动程序的命令如下所示。为了能正常的运行性能测试程序,读者需要自己适当的调整classpath。
java -cp janino.jar;. janinotest.eval.JaninoTestEval
可想而知,表达式复杂性越大,Janino编译器表现出的优势越强。对中等复杂性的表达式而言,Janino编译时可以提高7倍的速度。当然,图1并不能提供一个性能提升基准,但能为开发人员提供一个可预见性的性能提高参考。
1. 0 (相同的)
2. 100 * x + 20 / 2 (简单的)
3. (x + y)/(x - y) * 100/(x * y) (适度的)


对于正希望将Janino应用于自己项目的任何人而言,有一点很重要,就是将Janino用来优化已经建立的程序,其效果甚微。在前面作者建立的评价程序中,采用了$(令牌-名称)来代替令牌来测试Janino的性能加速情况。下图2所示,比较了如下的实现方式:
naive-map
一种使用Java的正则表达式取代令牌从地图生成字符串的简单但高效的实现方式。
fast-map
此方式是基于naïve-map并进行了优化,它可以将令牌化的字符串进行预先存储分解。
janino-map
可对代码进行预编译并能从地图值生成字符串。
fast-object
使用反射机制直接从POJO(Plain Old Java Object,普通Java类)抽取令牌值的优化实现方式。
janino-object
可对代码进行预编译并能从地图值生成字符串。
hardwired-object
此实现方式直接从特殊Java类抽取令牌值,以做为最可能的时间基准。

出人意料的是,采用JDK编译的代码(仅在此例中)其执行效率居然胜过了Janino的效率。同时,优化过的实现也快过编译版本的代码。这得归功于Sun的Java编译器而不是Janino。特别地,这两种不同的编译器所采用的实现策略是完全不一样的。但可喜的是,Janino的字符串处理功能已经优化了,同时它的性能可以与javac相媲美。
如果想亲自运行此性能测试程序,可以下载源程序(见资源部分),进行编译,并按如下的命令执行它(同样classpath要进行适当的修改):
java -cp janino.jar;. janinotest.sub.JaninoSubTest
因此,我们可得出结论,采用动态编译的优化技术是很有意义的,但只适合应用于性能可以明确提高的情况。因为,毕竟世上没有解决所有问题的方法。
五、结论
在平时的开发工作中,有很多情况可以用到动态编译的技术。如下则列出了一些基本的参考意见:
1. 使用动态编译类来取代Java代理类,可避免所有的反射,因此可显著的提高性能。
2. 对于用户自定义功能等请求,可以像Java代码一样进行编译与评价,而之前只能采用域定义语言(domain-specific language)来解析与评价。
3. 如果数据记录的字段固定,但不知道编译所需时间,此时是HashMaps应用的典型场景。利用Janino,已知的字段可用于构建私有字段。再加上一些额外的工作,可通过Map接口将字段暴露出来。如果字段类型相对记录数量来说很少时,内存的保存就显得很重要了。
在程序的性能优化方面Janino还有更多的应用场合,对于想更好的提高Java代码性能的开发人员,可以更加深入的研究Janino。