Java 寻找如何在特定测试中提高groovy性能的想法
在将现有批处理过程从Java 寻找如何在特定测试中提高groovy性能的想法,java,performance,groovy,Java,Performance,Groovy,在将现有批处理过程从Java转换为Groovy时,我遇到了一些相当严重的性能问题。用Java编写的现有批处理过程定期运行,从不同的数据源读取数据并执行一些数据转换。我们发现,在将Java代码转换为Groovy后,性能显著下降,差距高达10倍以上 处的代码是一个简化的示例,显示了通过简单循环和使用集合闭包过滤发现的问题之一。它被设置为Mavenproject,可以在本地轻松克隆并执行 下面是Groovy代码的亮点: List items = (0..length).collect()
Java
转换为Groovy
时,我遇到了一些相当严重的性能问题。用Java
编写的现有批处理过程定期运行,从不同的数据源读取数据并执行一些数据转换。我们发现,在将Java
代码转换为Groovy
后,性能显著下降,差距高达10倍以上
处的代码是一个简化的示例,显示了通过简单循环和使用集合闭包过滤发现的问题之一。它被设置为Maven
project,可以在本地轻松克隆并执行
下面是Groovy代码的亮点:
List items = (0..length).collect()
List even = items.findAll { item -> item > 0 && item.longValue() % 2 == 0 }
Java代码:
List<Long> items = new ArrayList(length);
for (int i = 0; i < length; i++) {
items.add(Long.valueOf(i + 1));
}
List<Long> even = new ArrayList<Long>();
for(Long item : items){
if (item > 0 && item % 2 == 0) {
even.add(item);
}
}
如果您对如何提高Groovy性能有什么建议,请告诉我。我们的团队正在考虑迁移到Groovy,因为它提供了一些高级功能,但由于我们目前遇到的性能差距太大,所以很难证明这一点
下面是我的硬件配置文件,由系统\u档案器SPHardwareDataType
报告:
Hardware Overview:
Model Name: MacBook Pro
Model Identifier: MacBookPro11,3
Processor Name: Intel Core i7
Processor Speed: 2.5 GHz
Number of Processors: 1
Total Number of Cores: 4
L2 Cache (per Core): 256 KB
L3 Cache: 6 MB
Memory: 16 GB
Boot ROM Version: MBP112.0138.B11
SMC Version (system): 2.19f12
下面是Java
版本:
java version "1.7.0_72"
Java(TM) SE Runtime Environment (build 1.7.0_72-b14)
Java HotSpot(TM) 64-Bit Server VM (build 24.72-b04, mixed mode)
Groovy
版本是中定义的2.3.7
更新
Groovy
代码进行了建议修改:
List items = (0..length)
List even = items.findAll { int item -> item > 0 && item % 2 == 0 }
预热的测试方法调用的重复
/speed test.sh
,它分别运行groovy
和java
测试。测试中从未包括启动jvm
下面是我能够看到的在同一个jvm进程中运行相同方法10次的最佳结果,允许进行预热:
/speed-test.sh
Java test
Java testUsingInterface: 500000 elapsed: 44
Java testUsingInterface: 500000 elapsed: 43
Java testUsingInterface: 500000 elapsed: 28
Java testUsingInterface: 500000 elapsed: 11
Java testUsingInterface: 500000 elapsed: 31
Java testUsingInterface: 500000 elapsed: 10
Java testUsingInterface: 500000 elapsed: 9
Java testUsingInterface: 500000 elapsed: 11
Java testUsingInterface: 500000 elapsed: 19
Java testUsingInterface: 500000 elapsed: 19
JavaTest: for testSize=1000000 and repeat=10 total elapsed: 226
Groovy Test
GroovyTest: 500000 elapsed: 199
GroovyTest: 500000 elapsed: 76
GroovyTest: 500000 elapsed: 91
GroovyTest: 500000 elapsed: 80
GroovyTest: 500000 elapsed: 58
GroovyTest: 500000 elapsed: 83
GroovyTest: 500000 elapsed: 91
GroovyTest: 500000 elapsed: 58
GroovyTest: 500000 elapsed: 58
GroovyTest: 500000 elapsed: 67
GroovyTest: for testSize=1000000 and repeat=10 total elapsed: 1073
正如@blackdrag所指出的,
Groovy
需要更长的时间来预热。在预热循环后,执行仍需约5倍的时间(即使不包括初始预热循环)。更新后的代码位于分支功能/选项-1
上,如果有人想查看它。我大致有以下性能测试指南:
- 确保测量时间超过1秒,以避免计算机时钟出现计时错误
- 始终提供Groovy和Java版本以及计算机规格,以便能够进行比较
- 始终有足够长的热身阶段
- 不要同时运行多个微基准测试
- 测量多个迭代以获得平均值要优于测量单个迭代
@CompileStatic
可以提供帮助,但我们还不能总是阻止加载groovy元类系统。因此,即使这样,也可能会有预热成本。更不用说JVM本身有预热成本
原始代码需要在我的计算机上的原始代码约752ms。只增加一次迭代的预热,这将下降到14-20ms
还有一些逻辑上的断开List items=(0..length).collect()
范围已经是一个列表,因此不需要在此处调用collect。这只会通过复制每个元素来生成一个新列表。collect()不会将元素转换为long。由于我们处理的是整数对象,因此实际上不需要通过调用longValue()
转换为long。仅纠正这两件事就可以将执行时间减少一半(至少在我的计算机上,而且没有预热阶段)。但是热身阶段在这里确实起了作用。因此,通过热身和这些修正,我已经达到10毫秒(50k元素)。要进行比较,Java版本需要5毫秒。我发现它已经短到无法测试了。因此,如果我用100万个元素重新进行测试,我会看到73ms(Java)与200ms(Groovy)的对比。当然,我也将Java版本更改为使用Integer
添加类型提示以启用基本优化List even=items.findAll{int item->item>0&&item%2==0}
将把性能提高到大约120ms
在其他情况下,
@CompileStatic
或使用invokedynamic运行(invokedynamic版本的性能完全取决于JVM版本!)也可能有助于提高性能。我想他们在这个测试中不会做太多。一般来说,我有一些性能测试的指导原则:
- 确保测量时间超过1秒,以避免计算机时钟出现计时错误
- 始终提供Groovy和Java版本以及计算机规格,以便能够进行比较
- 始终有足够长的热身阶段
- 不要同时运行多个微基准测试
- 美苏
/speed-test.sh Java test Java testUsingInterface: 500000 elapsed: 44 Java testUsingInterface: 500000 elapsed: 43 Java testUsingInterface: 500000 elapsed: 28 Java testUsingInterface: 500000 elapsed: 11 Java testUsingInterface: 500000 elapsed: 31 Java testUsingInterface: 500000 elapsed: 10 Java testUsingInterface: 500000 elapsed: 9 Java testUsingInterface: 500000 elapsed: 11 Java testUsingInterface: 500000 elapsed: 19 Java testUsingInterface: 500000 elapsed: 19 JavaTest: for testSize=1000000 and repeat=10 total elapsed: 226 Groovy Test GroovyTest: 500000 elapsed: 199 GroovyTest: 500000 elapsed: 76 GroovyTest: 500000 elapsed: 91 GroovyTest: 500000 elapsed: 80 GroovyTest: 500000 elapsed: 58 GroovyTest: 500000 elapsed: 83 GroovyTest: 500000 elapsed: 91 GroovyTest: 500000 elapsed: 58 GroovyTest: 500000 elapsed: 58 GroovyTest: 500000 elapsed: 67 GroovyTest: for testSize=1000000 and repeat=10 total elapsed: 1073