为什么程序的执行时间会发生显着变化?

时间:2014-09-21 20:45:35

标签: java sorting time execution

我想测试不同排序算法的执行时间,我发现了一个有趣的问题。当我多次运行程序时,比如说插入排序,前一次或两次花费的时间比后面的花费多得多。这种情况发生在数组大小较大时,不同大小对执行时间的影响也不同。

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倍。

注意:

  1. 语言:Java JDK7; IDE:Eclipse;平台:Win8.1;
  2. 对于每种尺寸,测试了许多实验,结果非常相似。虽然执行时间有一些随机性,但它无法解释为什么前两次相似而且比后来的时间长30多倍。
  3. 可能的原因可能是阵列已经在数据缓存中,因此以后的执行会花费更少的时间。我不确定是否还有其他原因。
  4. 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以上时,它类似于插入排序中的情况,后续执行会花费更少的时间。

2 个答案:

答案 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是编译代码,意味着在构建应用程序时,编写的Java-Soure被编译为低级语言。
  • 编译语言时,可以有各种抽象步骤。每个角色都需要(最终)从人类可读的代码翻译成“零”和“1” - 其中包含与语言相关的层数。
  • 由于您在设计时不知道运行时数据,因此无法将其转换为1和0 - 因此代码保持介于两者之间。 (但是它可以在运行时进一步翻译,当你最终知道数据并且使用相同的数据重复访问相同的方法时!
  • 每种语言都有一个共同点:相同的输入等于相同的输出。
  • 因此,每个层都可能有自己的(内部)缓存,以加快速度并减少CPU /内存负载。

就像你可以重用 java中的对象以避免从数据库重新加载一样,中间的每一层都可以重用已经使用过的位和字节。

(从数据库的角度来看这个效果会引发同样的问题:为什么第一次显示字符串需要125ms,而每隔一次只需要5ms?)


想象一个10人的房间,你问一个人:这里的平均年龄是多少? - 这个人需要询问每个人的年龄,进行一些计算才能回答 25

如果你再问一次 - 没有任何改变 - 答案会立即出现。 (复制阵列将是一个房间开关,同时保持相同的人)

但是如果你要改变那些人(无论是保留还是改变房间) - 整个算法都需要再次执行。

这个例子中只有一层(被问及的人)可能记得已经问过的问题。

答案 1 :(得分:0)

可能有很多原因,但在你的情况下,我认为这是JIT(即时编译)的效果,它编译成本机代码最近使用的字节代码片段。这就是前两次执行速度较慢的原因。它们由解释java字节代码的JVM完成。然后JIT将您的排序算法编译为本机代码,JVM执行它以获得显着的性能优势。