我想测试不同排序算法的执行时间,我发现了一个有趣的问题。当我多次运行程序时,比如说插入排序,前一次或两次花费的时间比后面的花费多得多。这种情况发生在数组大小较大时,不同大小对执行时间的影响也不同。
public static void insertSort(int[] array){
for(int i = 1; i<array.length; i++){
int current = array[i];
int j = i-1;
while((j>=0)&&(array[j]>current)){
array[j+1] = array[j];
array[j] = current;
j--;
}
}
}
public static void multiTimes(int size){
Random r = new Random();
int a[] = new int[size];
int b[] = new int[size];
for(int j = 0; j<size; j++)
a[j] = r.nextInt(size);
long startTime, endTime = 0;
b = Arrays.copyOf(a, a.length);
startTime=System.nanoTime();
insertSort(b);
endTime=System.nanoTime();
System.out.println("Insert "+(endTime-startTime)+" ns");
b = Arrays.copyOf(a, a.length);
startTime=System.nanoTime();
insertSort(b);
endTime=System.nanoTime();
System.out.println("Insert "+(endTime-startTime)+" ns");
b = Arrays.copyOf(a, a.length);
startTime=System.nanoTime();
insertSort(b);
endTime=System.nanoTime();
System.out.println("Insert "+(endTime-startTime)+" ns");
b = Arrays.copyOf(a, a.length);
startTime=System.nanoTime();
insertSort(b);
endTime=System.nanoTime();
System.out.println("Insert "+(endTime-startTime)+" ns");
b = Arrays.copyOf(a, a.length);
startTime=System.nanoTime();
insertSort(b);
endTime=System.nanoTime();
System.out.println("Insert "+(endTime-startTime)+" ns");
b = Arrays.copyOf(a, a.length);
startTime=System.nanoTime();
insertSort(b);
endTime=System.nanoTime();
System.out.println("Insert "+(endTime-startTime)+" ns");
}
尺寸:100
插入77908 ns
插入82573 ns
插入75109 ns
插入76508 ns
插入91902 ns
插入78840 ns
每次执行时间都相似。
尺寸:1000:
插入6256400 ns
插入5674659 ns
插入188938 ns
插入188004 ns
插入187071 ns
插入186605 ns
尺寸:2000:
插入7961037 ns
插入6590889 ns
插入793538 ns
插入793072 ns
插入793072 ns
插入792138 ns
我们可以看到,对于1000,2000或更大的尺寸,结果非常有趣。前两次的执行时间比以后的执行时间(大小= 1000)大约多30倍。
注意:
PS: 在我测试了插入排序之后,我发现它在快速排序中甚至令人困惑。
public static void quickSort(int a[], int left, int right){
if(right<=left)
return;
int temp[] = new int[right-left+1];
for(int i = left; i<=right; i++)
temp[i-left] = a[i];
int pivot = a[left];
int subr = right, subl = left;
for(int i = left+1; i<=right;i++){
if(temp[i-left]>pivot)
a[subr--] = temp[i-left];
else
a[subl++] = temp[i-left];
}
a[subl] = pivot;
quickSort(a, left, subl-1);
quickSort(a, subr+1, right);
}
尺寸= 1000:
Qs 888240 ns
Qs 2218734 ns
Qs 2179547 ns
问题2132896 ns
Qs 2146890 ns
Qs 2212670 ns
尺寸= 500:
Qs 432924 ns
Qs 406799 ns
Qs 941889 ns
Qs 1103302 ns
问题1101436 ns
Qs 1086042 ns
当大小在[200,2000]左右时,前几次花费的时间比后来的花费少,这与插入排序相反。当大小增加到2000以上时,它类似于插入排序中的情况,后续执行会花费更少的时间。
答案 0 :(得分:2)
当您删除sort方法的完整方法体并使用当前代码调用它时, 你会发现同样的效果 - 规模较小:
Insert 1488 ns
Insert 353 ns
Insert 246 ns
Insert 240 ns
Insert 224 ns
Insert 212 ns
如果您现在要删除属性int[] array
,您仍会注意到同样的效果:
Insert 1452 ns
Insert 342 ns
Insert 232 ns
Insert 203 ns
Insert 228 ns
Insert 209 ns
因此,显然这种行为独立于数据(-sate),内存分配或内存中已存在的某些值的重复。
显然,只有方法存根
public static void insertSort(){
}
左,它需要与方法声明本身有一些东西。由于AlexR已经Stated,Java有一个JIT编译器。由于没有任何关于数据的信息,这种行为只有一个原因:运行时优化。
就像你可以重用 java中的对象以避免从数据库重新加载一样,中间的每一层都可以重用已经使用过的位和字节。
(从数据库的角度来看这个效果会引发同样的问题:为什么第一次显示字符串需要125ms,而每隔一次只需要5ms?)
想象一个10人的房间,你问一个人:这里的平均年龄是多少? - 这个人需要询问每个人的年龄,进行一些计算才能回答 25
如果你再问一次 - 没有任何改变 - 答案会立即出现。 (复制阵列将是一个房间开关,同时保持相同的人)
但是如果你要改变那些人(无论是保留还是改变房间) - 整个算法都需要再次执行。
这个例子中只有一层(被问及的人)可能记得已经问过的问题。
答案 1 :(得分:0)
可能有很多原因,但在你的情况下,我认为这是JIT(即时编译)的效果,它编译成本机代码最近使用的字节代码片段。这就是前两次执行速度较慢的原因。它们由解释java字节代码的JVM完成。然后JIT将您的排序算法编译为本机代码,JVM执行它以获得显着的性能优势。