我有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
的未优化虚拟调用。
从初始化方法中删除
Image.process
调用,然后 JIT 会将有用的调用内联到Shader.apply
。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/