从jdk1.7.0_25迁移到jdk1.7.0_40时的性能下降

2022-09-03 02:56:52

我正在将Spring 3.1.2批处理应用程序从x64和Oracle迁移到x64。jdk1.7.0_25jdk1.7.0_40

使用Sun作为性能指标,结果显示性能下降了2.5倍(即,在u25上运行的应用程序要快得多)。OperatingSystemMXBean.getProcessCpuTime()

  • 据我所知,这不是由于和变化,因为使用u25的HashMap和ArrayList类引导u40时结果相同,这些更改对于这种差异来说太小了。java.util.HashMapjava.util.ArrayList
  • 这与HashMap并发回归无关,因为应用程序是单线程的,并且回归已在u40中修复
  • 热交换优化似乎也不是问题,因为运行并产生相同的结果(假设这些JDK之间的服务器编译是相同的)。-Xbatch-Xcomp
  • 有一个性能倒退,但这似乎无关紧要。除非Spring 3.1.2使用它们 - 我找不到证据。java.lang.invoke.MethodHandles
  • javac编译似乎也没有改变。

一些一般注意事项:

  • 此问题出现在每个JDK 7版本>= u40(以及最新的JDK 8)中,而<u40的版本似乎很好(包括各种版本的JDK 6)。jdk1.8.0
  • 普通的旧java代码(例如,运行1000 * 1000 * 1000 * some_calc)没有此性能问题,这意味着我的代码或使用的库以某种方式执行了奇怪和意外的事情?
  • 测试是使用相同的数据库实例(MSSQL 2008 R2)完成的,这并不重要。
  • 即使OperationSystemMXBean不可靠,测试的上墙时间也同样不同。
  • 在这两种情况下,GC似乎在同一时间和相同的持续时间内启动(我一直在用于测试)。+UseSerialGC
  • 分析显示没有异常的新热点,尽管它通常显示应用程序范围的执行时间增加。
  • 测试这些 Sun JDK 或 OpenJDK 版本的 x86 版本(我用过这些版本)不会改变结果。
  • 所有测试的代码(在 JDK 6 上运行时除外)都是使用 编译的。jdk1.7.0_40
  • 同一方案已在两台不同的计算机上进行了测试:x64 和 x86。

任何提示或想法?

编辑以添加:应用程序的结构是运行金融蒙特卡罗模拟的外部循环:即许多日期,计算等。因此,它目前有点复杂,我同意,不适合找到问题。我必须尝试缩小它。


答案 1

看起来问题是由JDK-7133857中完成的工作引起的,其中和使用x87进行了内涵和计算。java.lang.Math.pow()java.lang.Math.exp()

这些方法在分析的应用程序中被广泛使用(!),因此它们具有相当大的效果。

JDK-8029302 描述并修复了 2 路输入的电源问题。并且对应用程序的测试(其中问题已修复)显示性能有所提高,尽管没有恢复到完成内嵌之前的更高级别。jdk1.8.0_25jdk1.7.0_25

以下是我在三个相关JDK版本的JMH基准测试及其结果:Math.pow()

package org.sample;

import org.openjdk.jmh.annotations.*;
import java.lang.*;

public class MyBenchmark {

    @State(Scope.Benchmark)
    public static class ThreadState {
        volatile double x = 0;
        volatile double y = 0;
    }

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    public double powx(ThreadState state) {
        state.x++;
        state.y += 0.5;
        return Math.pow(state.x, state.y);
    }

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    public double pow3(ThreadState state) {
        state.x++;
        return Math.pow(state.x, 3);
    }

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    public double pow2(ThreadState state) {
        state.x++;
        return Math.pow(state.x, 2);
    }
}

结果:

英特尔®酷睿™i5-2520M 处理器 @ 2.50GHz

jdk1.7.0_25 - 内嵌术前

# VM invoker: x:\@sdks\jdks\jdk1.7.0_25\jre\bin\java.exe
...
Result: 4877658.355 (99.9%) 330460.323 ops/s [Average]
  Statistics: (min, avg, max) = (1216417.493, 4877658.355, 6421780.276), stdev = 1399189.700
  Confidence interval (99.9%): [4547198.032, 5208118.678]


# Run complete. Total time: 00:24:48

Benchmark                Mode  Samples         Score  Score error  Units
o.s.MyBenchmark.pow2    thrpt      200  40160618.138  1561135.596  ops/s
o.s.MyBenchmark.pow3    thrpt      200   3678800.153    88678.269  ops/s
o.s.MyBenchmark.powx    thrpt      200   4877658.355   330460.323  ops/s

jdk1.7.0_40 - 内嵌

# VM invoker: x:\@sdks\jdks\jdk1.7.0_40\jre\bin\java.exe
...
Result: 1860849.245 (99.9%) 94303.387 ops/s [Average]
  Statistics: (min, avg, max) = (418909.582, 1860849.245, 2379936.035), stdev = 399286.444
  Confidence interval (99.9%): [1766545.859, 1955152.632]


# Run complete. Total time: 00:24:48

Benchmark                Mode  Samples        Score  Score error  Units
o.s.MyBenchmark.pow2    thrpt      200  9619333.987   230749.333  ops/s
o.s.MyBenchmark.pow3    thrpt      200  9240043.369   238456.949  ops/s
o.s.MyBenchmark.powx    thrpt      200  1860849.245    94303.387  ops/s

jdk1.8.0_25 - 固定内涵

# VM invoker: x:\@sdks\jdks\jdk1.8.0_25\jre\bin\java.exe
...
Result: 1898015.057 (99.9%) 92555.236 ops/s [Average]
  Statistics: (min, avg, max) = (649562.297, 1898015.057, 2359474.902), stdev = 391884.665
  Confidence interval (99.9%): [1805459.821, 1990570.293]


# Run complete. Total time: 00:24:37

Benchmark                Mode  Samples         Score  Score error  Units
o.s.MyBenchmark.pow2    thrpt      200  81840274.815  1979190.065  ops/s
o.s.MyBenchmark.pow3    thrpt      200   9441518.686   206612.404  ops/s
o.s.MyBenchmark.powx    thrpt      200   1898015.057    92555.236  ops/s

如果我没看错的话,JDK-8029302 中肯定修复了 2 的幂问题,并且 >2 ints 的幂(我刚刚测试过)性能在 中得到了提高。至于上面在基准测试中完成的奇怪的非int,在从 移动到 时似乎仍然存在相当大的性能下降(>3x)。Math.pow(x, 3)jdk1.7.0_40Math.pow()spowx()jdk1.7.0_25jdk1.7.0_40

替换和使用它们各自的方法完全解决了性能提高的问题 - 就我而言,这是正确的解决方案。Math.pow()Math.exp()org.apache.commons.math3.util.FastMath

注意:如果有一种简单的方法(即不需要构建JDK)来设置-XX:-InlineIntrinsics标志,这将会更简单一些。


答案 2

推荐