java - 最小化 Java 函数调用开销

标签 java performance optimization java-8 jmh

我有a piece of code在我运行的每个测试中,函数调用都会产生大量开销。代码是一个紧密的循环,对数组的每个元素执行一个非常简单的函数(包含 4-8 百万个 int)。

运行代码,主要包括

for (int y = 1; y < h; ++y) {
    for (int x = 1; x < w; ++x) {
        final int p = y * s + x;
        n[p] = f.apply(d, s, x, y);
    }
}

执行类似的东西

(final int[] d, final int s, final int x, final int y) -> {
    final int p = s * y + x;
    final int a = d[p] * 2
                + d[p - 1]
                + d[p + 1]
                + d[p - s]
                + d[p + s];
    return (1000 * (a + 500)) / 6000;
};

在各种机器上(我的工作笔记本电脑,一台配备 i7 3840QM 的 W530,一台配备一个 Xeon E5-1620 核心的服务器虚拟机,以及一个配备一个未知 CPU 核心的 Digital Ocean 节点),我反复获得具有统计显着性的数据调用方法与内联时的性能下降。所有测试均在 Java 1.8.0_11(Java HotSpot(TM) 64 位服务器虚拟机)上执行。

工作机:

Benchmark                               Mode   Samples        Score  Score error    Units
c.s.q.ShaderBench.testProcessInline    thrpt       200       40.860        0.184    ops/s
c.s.q.ShaderBench.testProcessLambda    thrpt       200       22.603        0.159    ops/s
c.s.q.ShaderBench.testProcessProc      thrpt       200       22.792        0.117    ops/s

专用服务器、VM:

Benchmark                               Mode   Samples        Score  Score error    Units
c.s.q.ShaderBench.testProcessInline    thrpt       200       40.685        0.224    ops/s
c.s.q.ShaderBench.testProcessLambda    thrpt       200       16.077        0.113    ops/s
c.s.q.ShaderBench.testProcessProc      thrpt       200       23.827        0.088    ops/s

做 VPS:

Benchmark                               Mode   Samples        Score  Score error    Units
c.s.q.ShaderBench.testProcessInline    thrpt       200       24.425        0.506    ops/s
c.s.q.ShaderBench.testProcessLambda    thrpt       200        9.643        0.140    ops/s
c.s.q.ShaderBench.testProcessProc      thrpt       200       13.733        0.134    ops/s

所有可接受的性能,但我有兴趣弄清楚为什么调用有如此大的开销以及可以做些什么来优化它。目前正在试验不同的参数集。

内联所有潜在的操作会很困难,但理论上是可行的。对于接近 2 倍的性能提升,这可能是值得的,但维护将是一场噩梦。

我不确定是否有合理的方法来批量处理一组重复;大多数操作采用多个输入(调用者不知道)并产生单个输出。

我还有哪些其他选择可以最大限度地减少开销和夜间性能?

最佳答案

方法调用不是问题,因为热方法通常是内联的。 虚拟通话是个问题。

在您的代码中,类型分析器被初始化方法 Image.random 所欺骗。当 Image.process 第一次进行 JIT 编译时,它针对调用 random.nextInt() 进行了优化。因此 Image.process 的下一次调用将导致内联缓存未命中,然后是对 Shader.apply 的未优化虚拟调用。

  1. 从初始化方法中删除 Image.process 调用,然后 JIT 会将有用的调用内联到 Shader.apply

  2. BlurShader.apply 内联后,您可以帮助 JIT 执行 Common subexpression elimination通过替换优化

    final int p = s * y + x;
    

    final int p = y * s + x;
    

    后面的表达式在Image.process中也会遇到,所以JIT不会对同一个表达式计算两次。

应用这两项更改后,我获得了理想的基准分数:

Benchmark                           Mode   Samples         Mean   Mean error    Units
s.ShaderBench.testProcessInline    thrpt         5       36,483        1,255    ops/s
s.ShaderBench.testProcessLambda    thrpt         5       36,323        0,936    ops/s
s.ShaderBench.testProcessProc      thrpt         5       36,163        1,421    ops/s

关于java - 最小化 Java 函数调用开销,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24873010/

相关文章:

java - 非数据库应用事务

c# - ToList() 与 EF4 的高锁争用和低性能

javascript - 我的 jQuery 运行有点慢。我怎样才能加快速度?

java - 如何使用 RemoteViews 更改小部件背景?

java - 我应该使用哪个 Java 集合来实现线程安全缓存?

java - 获取对象实例的原始类

sql - 哪个更快 - NOT IN 或 NOT EXISTS?

优化连续映射/过滤器/折叠调用

math - 方格内11个方格的最优排列

python - 求解给定变量和不确定性的线性方程 : scipy-optimize?