2013-01-25 37 views
1

我做一些簡單的測試與雙,這樣做簡單的計算:Java的:int和double採取0 millisecons至100萬次

startTime = System.currentTimeMillis(); 

for (int i = 0; i < 100_000_000; i++) 
{ 
    doubleCalcTest(); 
} 

endTime = System.currentTimeMillis();  
System.out.println("That took " + (endTime - startTime) + " nanoseconds"); 

public static double doubleCalcTest() 
{ 
    double x = 987.654321; 
    double y = 123.456789; 

    x = x + y; 
    x = x - y; 
    x = x * y; 
    return x/y; 
} 

事實證明,輸出是0毫秒。這是沒有道理給我,因爲如果我設置爲循環僅運行10萬次,輸出爲3毫秒。 我發現int另行動以同樣的方式。

誰能給我一隻手搭在這一個?謝謝。

我改變了我的代碼來調用「System.nanoTime的時間和傳遞由循環的指數增加的雙重價值的建議。

double x = 123.456789 
startTime = System.nanoTime(); 

for (int i = 0; i < 100_000_000; i++) 
{ 
    x = x + i; 
    doubleCalcTest(x); 
} 

endTime = System.nanoTime();  
System.out.println("That took " + (endTime - startTime) + " nanoseconds"); 

public static double doubleCalcTest(double x) 
{ 
    double y = 123.456789; 

    x = x + y; 
    x = x - y; 
    x = x * y; 
    return x/y; 
} 

運行10,000次花了503200納秒

運行1000萬次花3421納秒

+4

優化了...? –

+0

我一開始也這麼認爲,但它並不能解釋爲什麼需要3毫秒來運行100,000次。 – Xin

+0

我測試了你的代碼,大約80毫秒。 - 我安裝了最新的java環境。 – shuangwhywhy

回答

7

JIT放棄執行DoubleCalcTest,因爲它沒有任何副作用(純計算)並且不使用結果。循環本身也可以被優化,因爲沒有效果。

與JIT去嘗試這一點,它需要大約8000毫秒:

java -Xint snippet.Snippet 

在byteocde水平,沒有什麼是優化的。

javap -c snippet.Snippet 

結果:

public class snippet.Snippet { 
    public snippet.Snippet(); 
    Code: 
     0: aload_0  
     1: invokespecial #8     // Method java/lang/Object."<init>":()V 
     4: return   

    public static void main(java.lang.String[]); 
    Code: 
     0: invokestatic #16     // Method java/lang/System.currentTimeMillis:()J 
     3: lstore_1  
     4: iconst_0  
     5: istore_3  
     6: goto   16 
     9: invokestatic #22     // Method DoubleCalcTest:()D 
     12: pop2   
     13: iinc   3, 1 
     16: iload_3  
     17: ldc   #26     // int 100000000 
     19: if_icmplt  9 
     22: invokestatic #16     // Method java/lang/System.currentTimeMillis:()J 
     25: lstore_3  
     26: getstatic  #27     // Field java/lang/System.out:Ljava/io/PrintStream; 
     29: new   #31     // class java/lang/StringBuilder 
     32: dup   
     33: ldc   #33     // String That took 
     35: invokespecial #35     // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V 
     38: lload_3  
     39: lload_1  
     40: lsub   
     41: invokevirtual #38     // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder; 
     44: ldc   #42     // String milliseconds 
     46: invokevirtual #44     // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
     49: invokevirtual #47     // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
     52: invokevirtual #51     // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     55: return   

    public static double DoubleCalcTest(); 
    Code: 
     0: ldc2_w  #64     // double 987.654321d 
     3: dstore_0  
     4: ldc2_w  #66     // double 123.456789d 
     7: dstore_2  
     8: dload_0  
     9: dload_2  
     10: dadd   
     11: dstore_0  
     12: dload_0  
     13: dload_2  
     14: dsub   
     15: dstore_0  
     16: dload_0  
     17: dload_2  
     18: dmul   
     19: dstore_0  
     20: dload_0  
     21: dload_2  
     22: ddiv   
     23: dreturn  
} 

如果您嘗試使用導致DoubleCalc的()通過將其分配給一個變量,然後打印出來。

public static void main(String[] args) { 
    long startTime = System.currentTimeMillis(); 

    double res = 0; 
    for (int i = 0; i < 100000000; i++) { 
     res = DoubleCalcTest(); 
    } 

    System.out.println(res); 
    long endTime = System.currentTimeMillis(); 
    System.out.println("That took " + (endTime - startTime) + " milliseconds"); 
} 

這將需要相同的時間。爲什麼? JIT似乎足夠聰明地理解結果並不取決於迭代完成的時間。

但是,如果將其更改爲:

public static void main(String[] args) { 
    long startTime = System.currentTimeMillis(); 

    double res = 0; 
    for (int i = 0; i < 100000000; i++) { 
     res += DoubleCalcTest(); 
    } 

    System.out.println(res); 
    long endTime = System.currentTimeMillis(); 
    System.out.println("That took " + (endTime - startTime) + " milliseconds"); 
} 

結果取決於迭代次數和JIT不進一步優化。在這種情況下,大約需要100毫秒。如果我爲2億更換1億,則需要兩倍的時間。

所以結論是,JIT在那裏停留。

注:

對於給定的C程序:

#include <stdio.h> 

int main(int argc, char** argv) { 

    long x = 0; 
    int i; 

    for(i=0; i<1000000; i++) { 
     x+=i; 
    } 
    printf("%ld", x); 
} 

GCC能夠在編譯時完全優化的循環並計算x的值:

gcc -O2 -S main.c 

主.s:

.file "main.c" 
    .section .rodata.str1.1,"aMS",@progbits,1 
.LC0: 
    .string "%ld" 
    .section .text.startup,"ax",@progbits 
    .p2align 4,,15 
    .globl main 
    .type main, @function 
main: 
.LFB11: 
    .cfi_startproc 
    movabsq $499999500000, %rsi <---- See, this is the pre-computed result 
    movl $.LC0, %edi 
    xorl %eax, %eax 
    jmp printf 
    .cfi_endproc 
.LFE11: 
    .size main, .-main 
    .ident "GCC: (GNU) 4.7.2 20121109 (Red Hat 4.7.2-8)" 
    .section .note.GNU-stack,"",@progbits 

很酷,嘿?

+0

WOW〜,謝謝你的努力。真的很好的答案。 – Xin

1

有可能已優化,但可以肯定,隨着System.nanoTime更換System.currentTimeMillis

System.nanoTime返回納秒的時間,這是更爲精確的。此外,the Javadocs建議使用它來測量經過時間。

+0

感謝您的回覆。我確實將代碼更改爲'System.nanoTime'時間,並發現了一些有趣的結果。我會更新我的問題。 – Xin

+0

還要注意'System.currentTimeMillis'的javadoc表示它可能是不準確的,這取決於底層系統。 –

6

這是因爲你寫的不是價值100000000,它是三個值100000000,由逗號運算符,它會評估它的操作數,並返回上一個值分隔對。您的循環從未被輸入,因爲100,000,000 == (100,0),0 == 0,0 == 0

+0

其實他的代碼甚至不會編譯。提問時可能是一個錯字。 – Doorknob

+0

40年的編程和一個放大的權利超過我! –

+3

對不起,我的壞,逗號在那裏,因爲它更容易讓人閱讀。我會修好它。 – Xin

0

戈登好點。你是否將逗號留在實際的代碼中?

如果沒有,那麼試圖傳遞一個參數值進入方法使JVM不能優化掉整個事情。

例如:

public static double DoubleCalcTest(double x) 
{ 

double y = 123.456789; 

    x = x + y; 
    x = x - y; 
    x = x * y; 
    return x/y; 
} 

再經過由你的循環指數增加的雙重價值。 JVM將不能進行優化,因爲它不再是靜態計算。

0

確定。我覺得編譯器發現,你從來沒有使用方法的返回值,所以根本沒有被執行的方法。我的結果似乎與你的不同,但它更有意義。

public static void main (String args[]) { 
    long startTime = System.currentTimeMillis(); 
    double d = 0; 

    for (int i = 0; i < 100000000; i++) { 
     //DoubleCalcTest(); 
    } 

    long endTime = System.currentTimeMillis(); 
    System.out.println("That took " + (endTime - startTime) + " milliseconds"); 
} 

取消註釋DoubleCalcTest();的循環,結果是約80毫秒,因爲我說。評論它,結果是一樣的。

現在將其更改爲d += DoubleCalcTest();結果是844毫秒。

我之前想過我應該在記錄endTime之後使用d,請致電System.out.println(d);,但它沒有區別,所以我刪除了它。

隨着d += DoubleCalcTest(),運行1,000,000次,大約10毫秒。這大約是運行100,000,000次的1/100,考慮到錯誤是正確的。

+0

被標記。沒有答案。 – Doorknob

+0

感謝您的努力。 – Xin

+0

@ user2009500任何時候,實際上我已經嘗試了一些事情後發現了一些其他有趣的事實。順便說一句,我仍然不知道爲什麼我會從你那裏得到不同的結果。 – shuangwhywhy