编辑:maaartinus给出了我一直在寻找的答案,而tmyklebu的关于该问题的数据帮助很大,所以都谢谢!:)
我已经阅读了一些有关HotSpot如何在代码中注入一些“本能”的信息,特别是针对Java标准Math库的(从此处开始)
因此,我决定尝试一下,看看HotSpot与直接进行比较有何不同(特别是因为我听说过min / max可以编译为无分支的asm)。
public static final int max ( final int a, final int b )
{
if ( a > b )
{
return a;
}
return b;
}
那是我的实现。从另一个SO问题中,我读到使用三元运算符会使用一个额外的寄存器,但在执行if块和使用三元运算符之间,我并没有发现明显的区别(即return(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] );
}
}
我在try-with-
resources块中使用Benchmark对象。完成后,它将在对象上调用close()并打印该块完成所需的时间。通过注释/隐藏上面代码中的max调用,分别进行测试。
“ 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()方法(即return(a>
= b)?a:b),我仍然可以获得更好的结果!这个不成立。
眼镜:
处理器: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 results = new ArrayList<>();
final ArrayList 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的差异是2倍。