基准测试错误。错误的非详尽清单:
- 没有预热 :单次测量几乎总是错误的;
- 在单个方法中混合几个代码路径 :我们可能开始使用仅适用于该方法中第一个循环的执行数据来编译该方法;
- 来源是可预测的 :如果循环编译,我们实际上可以预测结果;
- 结果消除了死代码 :如果循环编译,我们可以将循环丢掉
这可以说是用jmh正确地做到的:
@OutputTimeUnit(TimeUnit.NANOSECONDS)@BenchmarkMode(Mode.AverageTime)@Warmup(iterations = 3, time = 1)@Measurement(iterations = 3, time = 1)@Fork(10)@State(Scope.Thread)public class Longs { public static final int COUNT = 10; private Long[] refLongs; private long[] primLongs; @Setup public void setup() { primLongs = new long[COUNT]; for (int i = 0; i < COUNT; i++) { primLongs[i] = 12l; } refLongs = new Long[COUNT]; for (int i = 0; i < COUNT; i++) { refLongs[i] = 12l; } } @GenerateMicroBenchmark public long[] prim_baseline() { long[] d = new long[COUNT]; System.arraycopy(primLongs, 0, d, 0, COUNT); return d; } @GenerateMicroBenchmark public long[] prim_sort() { long[] d = new long[COUNT]; System.arraycopy(primLongs, 0, d, 0, COUNT); Arrays.sort(d); return d; } @GenerateMicroBenchmark public Long[] ref_baseline() { Long[] d = new Long[COUNT]; System.arraycopy(refLongs, 0, d, 0, COUNT); return d; } @GenerateMicroBenchmark public Long[] ref_sort() { Long[] d = new Long[COUNT]; System.arraycopy(refLongs, 0, d, 0, COUNT); Arrays.sort(d); return d; }}…产生:
Benchmark Mode Samples Mean Mean error Unitso.s.Longs.prim_baseline avgt 30 19.604 0.327 ns/opo.s.Longs.prim_sort avgt 30 51.217 1.873 ns/opo.s.Longs.ref_baseline avgt 30 16.935 0.087 ns/opo.s.Longs.ref_sort avgt 30 25.199 0.430 ns/op
在这一点上,您可能会开始怀疑为什么排序
Long[]和排序
long[]会花费不同的时间。答案在于
Array.sort()重载:OpenJDK通过不同的算法(使用TimSort的引用,使用双数据点快速排序的基元)对基元数组和引用数组进行排序。这是使用选择另一个算法的亮点
-Djava.util.Arrays.useLegacyMergeSort=true,这又落到了合并排序的参考上:
Benchmark Mode Samples Mean Mean error Unitso.s.Longs.prim_baseline avgt 30 19.675 0.291 ns/opo.s.Longs.prim_sort avgt 30 50.882 1.550 ns/opo.s.Longs.ref_baseline avgt 30 16.742 0.089 ns/opo.s.Longs.ref_sort avgt 30 64.207 1.047 ns/op
希望这有助于解释差异。
上面的解释几乎没有涉及排序的性能。当使用不同的源数据(包括可用的预排序子序列,它们的模式和游程长度,数据本身的大小)呈现时,性能会有很大不同。



