Ign*_*tyo 1 java arrays sorting cpu benchmarking
我有一个ArrayList,其中的数字从 10 MM 到 20 MM
List<Long> l = new ArrayList<>();
for (long i = 10_000_000; i < 20_000_000; i++) {
l.add(i);
}
Run Code Online (Sandbox Code Playgroud)
为什么sum()函数在排序数组上比在未排序数组上更快?
public class Main {
public static void main(String[] args) {
List<Long> l = new ArrayList<>();
for (long i = 10_000_000; i < 20_000_000; i++) {
l.add(i);
}
System.out.println(sum(l));
Collections.shuffle(l);
System.out.println(sum(l));
Collections.sort(l);
System.out.println(sum(l));
}
static long sum(List<Long> l) {
long started = System.nanoTime();
long result = 0;
for (long v : l) {
result += v;
}
System.out.println(" duration: " + (System.nanoTime() - started) / 1_000_000 + "ms");
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
我执行了sum()函数三次
在我的电脑上,执行时间是
85毫秒
250毫秒
97毫秒
为什么sum()函数在排序数组上比在未排序数组上更快?
这与排序本身无关,而是与访问内存的顺序有关。
我将简要描述一些实验来证实这一点:
尝试使用long[]而不是ArrayList<Long>. 然后就没有单独分配的Long对象,只有一个直接包含数据的大数组。这使得内存访问顺序sum与数组的内容无关。我没有进行这个实验,因为编写代码需要更多的工作。
尝试使用随机排序的值初始化列表。我尝试了(我还把列表缩短了一点以节省时间)并得到了这个结果:
duration: 31ms
-6262510617230624864
duration: 77ms
-6262510617230624864
duration: 160ms
-6262510617230624864
Run Code Online (Sandbox Code Playgroud)
即最快的顺序与创建对象的顺序相同Long,与它们的值无关。
打乱后按顺序重新创建Long对象,例如:
for (int i = 0; i < l.size(); i++)
l.set(i, new Long(l.get(i)));
Run Code Online (Sandbox Code Playgroud)
这使得sum打乱列表的 再次变快(随后对列表进行排序会取消内存访问模式的顺序并sum再次变慢)。
实际上并不能保证Long一个接一个地分配的对象会在内存中以相同的顺序分配,但这是一种很常见的情况,当分配足够多的对象并且同时不会发生太多干扰时。