Java Math.min/max performance
编辑:maaartinus给出了我正在寻找的答案,tmyklebu关于这个问题的数据帮助很大,所以谢谢两位!:)
我已经阅读了一些关于HotSpot如何注入代码的“内部函数”的信息,特别是对于Java标准Math库(从这里开始))
所以我决定试一试,看看HotSpot与直接进行比较会产生多大的差异(特别是因为我听说min/max可以编译成无分支asm)。
public class OpsMath {
public static final int max(final int a, final int b) {
if (a > b) {
return a;
}
return b;
}
}
这就是我的实现。从另一个SO问题中,我读到使用三元运算符使用额外的寄存器,我没有发现做if块和使用三元运算符之间的显着差异(即,返回(a > b)? a : b)。
分配一个8Mb的int数组(即200万个值),并对其进行随机化,我做以下测试:
try ( final Benchmark bench = new Benchmark( "millis to max" ) )
{
int max = Integer.MIN_VALUE;
for ( int i = 0; i < array.length; ++i )
{
max = OpsMath.max( max, array[i] );
// max = Math.max( max, array[i] );
}
}
我在资源试用块中使用 Benchmark 对象。当它完成时,它在对象上调用 close() 并打印块完成所花费的时间。测试是通过注释上述代码中的最大调用来单独完成的。
“max”被添加到基准测试块外的列表中,稍后再打印,以避免JVM优化整个块。
每次运行测试时,数组都是随机的。
运行测试 6 次,它给出以下结果:
Java 标准数学:
millis to max 9.242167
millis to max 2.1566199999999998
millis to max 2.046396
millis to max 2.048616
millis to max 2.035761
millis to max 2.001044
因此,在第一次运行后相当稳定,并且再次运行测试给出了类似的结果。
OpsMath:
millis to max 8.65418
millis to max 1.161559
millis to max 0.955851
millis to max 0.946642
millis to max 0.994543
millis to max 0.9469069999999999
同样,第一次运行后的结果非常稳定。
问题是:为什么?这是一个很大的区别。我不知道为什么。即使我完全像Math.max()一样实现我的max()方法(即,返回(a >= b)? a : b)我仍然会得到更好的结果!这是没有道理的。
规格:
CPU: Intel i5 2500, 3,3Ghz.Java 版本:JDK 8(3 月 18 日公开发布),x64。Debian Jessie (測試版) x64.
我还没有尝试过使用32位JVM。
编辑:根据要求进行自包含测试。添加了一行来强制 JVM 预加载 Math 和 OpsMath 类。这消除了OpsMath测试第一次迭代的18ms成本。
// Constant nano to millis.
final double TO_MILLIS = 1.0d / 1000000.0d;
// 8Mb alloc.
final int[] array = new int[(8*1024*1024)/4];
// Result and time array.
final ArrayList<Integer> results = new ArrayList<>();
final ArrayList<Double> times = new ArrayList<>();
// Number of tests.
final int itcount = 6;
// Call both Math and OpsMath method so JVM initializes the classes.
System.out.println("initialize classes " +
OpsMath.max( Math.max( 20.0f, array.length ), array.length / 2.0f ));
final Random r = new Random();
for ( int it = 0; it < itcount; ++it )
{
int max = Integer.MIN_VALUE;
// Randomize the array.
for ( int i = 0; i < array.length; ++i )
{
array[i] = r.nextInt();
}
final long start = System.nanoTime();
for ( int i = 0; i < array.length; ++i )
{
max = Math.max( array[i], max );
// OpsMath.max() method implemented as described.
// max = OpsMath.max( array[i], max );
}
// Calc time.
final double end = (System.nanoTime() - start);
// Store results.
times.add( Double.valueOf( end ) );
results.add( Integer.valueOf( max ) );
}
// Print everything.
for ( int i = 0; i < itcount; ++i )
{
System.out.println( "IT" + i + " result: " + results.get( i ) );
System.out.println( "IT" + i + " millis: " + times.get( i ) * TO_MILLIS );
}
Java Math.max 结果:
IT0 result: 2147477409
IT0 millis: 9.636998
IT1 result: 2147483098
IT1 millis: 1.901314
IT2 result: 2147482877
IT2 millis: 2.095551
IT3 result: 2147483286
IT3 millis: 1.9232859999999998
IT4 result: 2147482828
IT4 millis: 1.9455179999999999
IT5 result: 2147482475
IT5 millis: 1.882047
OpsMath.max结果:
IT0 result: 2147482689
IT0 millis: 9.003616
IT1 result: 2147483480
IT1 millis: 0.882421
IT2 result: 2147483186
IT2 millis: 1.079143
IT3 result: 2147478560
IT3 millis: 0.8861169999999999
IT4 result: 2147477851
IT4 millis: 0.916383
IT5 result: 2147481983
IT5 millis: 0.873984
总体结果仍然相同。我只尝试过一次随机化数组,并在同一数组上重复测试,我总体上得到了更快的结果,但Java Math.max和OpsMath之间的差异相同.max。