Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/344.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 寻找如何在特定测试中提高groovy性能的想法_Java_Performance_Groovy - Fatal编程技术网

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版本以及计算机规格,以便能够进行比较
    • 始终有足够长的热身阶段
    • 不要同时运行多个微基准测试
    • 测量多个迭代以获得平均值要优于测量单个迭代
    因为性能测试是一个非常广泛的领域,尤其是微基准测试(因为你可能不会测试你认为你测试的东西)。我也为您的案例提供了一些提示,但对于这个平台来说,深入了解所有细节可能太多了

    首先,你应该考虑你想要测试什么。是峰值性能、平均性能还是初始性能?有无启动成本?正如您所知,JVM使用部分解释和部分运行时编译的代码。解释后的代码何时以及如何转换为编译后的代码,例如取决于包含代码的方法被调用的迭代次数(以及所使用的类型、代码大小和许多其他因素)

    如果您使用的是峰值性能,那么junit就不是合适的工具。例如,JMH在这里会更好,因为它不仅处理预热时间,而且在稳定阶段停止

    例如,在第一次使用groovy运行时,有很多类加载都完成了,其中加载了默认的groovy方法。仅此一项就可以轻松地占用您观察到的一半时间,而且实际上当时还没有执行任何代码

    @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