最小化Java函数调用开销
我有一段代码 ,在我运行的每个测试中,函数调用都会产生大量的开销。 代码是一个紧密循环,对数组的每个元素执行一个非常简单的函数(包含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核心的服务器VM,以及带有一个未知CPU核心的数字海洋节点),我反复获得统计上显着的性能调用方法vs内联。 所有测试均在Java 1.8.0_11(Java HotSpot(TM)64位服务器VM)上执行。
工作机器:
Benchmark Mode Samples Score Score error Units csqShaderBench.testProcessInline thrpt 200 40.860 0.184 ops/s csqShaderBench.testProcessLambda thrpt 200 22.603 0.159 ops/s csqShaderBench.testProcessProc thrpt 200 22.792 0.117 ops/s
专用服务器,VM:
Benchmark Mode Samples Score Score error Units csqShaderBench.testProcessInline thrpt 200 40.685 0.224 ops/s csqShaderBench.testProcessLambda thrpt 200 16.077 0.113 ops/s csqShaderBench.testProcessProc thrpt 200 23.827 0.088 ops/s
做VPS:
Benchmark Mode Samples Score Score error Units csqShaderBench.testProcessInline thrpt 200 24.425 0.506 ops/s csqShaderBench.testProcessLambda thrpt 200 9.643 0.140 ops/s csqShaderBench.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
联BlurShader.apply
之后,您可以帮助JIT通过替换来执行Common子表达式消除优化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