[英]Java multiplication strange behaviour
看看下面的代碼:
class Test
{
public static void main(String abc[])
{
for( int N=1; N <= 1_000_000_000; N=N*10)
{
long t1 = System.nanoTime();
start(N);
long t2 = System.nanoTime() - t1;
System.out.println("Time taken for " + N + " : " + t2);
}
}
public static void start( int N )
{
int j=1;
for(int i=0; i<=N; i++)
j=j*i;
}
}
上述問題產生的結果是:
Time taken for 1 : 7267
Time taken for 10 : 3312
Time taken for 100 : 7908
Time taken for 1000 : 51181
Time taken for 10000 : 432124
Time taken for 100000 : 4313696
Time taken for 1000000 : 9347132
Time taken for 10000000 : 858
Time taken for 100000000 : 658
Time taken for 1000000000 : 750
問題:
1.)為什么N = 1的時間異常大於N = 10? (有時甚至超過N = 100)
2.)為什么N = 10M及以后的時間異常低?
上述問題中指出的模式是深刻的,甚至在經過多次迭代后仍然存在。 這里有記憶的連接嗎?
編輯:
謝謝您的回答。 我想用實際循環替換方法調用。 但現在,沒有JIT優化。 為什么不 ? 是否將語句放在促進優化過程的方法中? 修改后的代碼如下:
class test
{
public static void main(String abc[])
{
for( int k=1; k<=3; k++)
{
for( int N=1; N<=1_000_000_000; N=N*10)
{
long t1 = System.nanoTime();
int j=1;
for(int i=0; i<=N; i++)
j=j*i;
long t2 = System.nanoTime() - t1;
System.out.println("Time taken for "+ N + " : "+ t2);
}
}
}
}
編輯2:以上修改代碼的輸出:
Time taken for 1 : 2160
Time taken for 10 : 1142
Time taken for 100 : 2651
Time taken for 1000 : 19453
Time taken for 10000 : 407754
Time taken for 100000 : 4648124
Time taken for 1000000 : 12859417
Time taken for 10000000 : 13706643
Time taken for 100000000 : 136928177
Time taken for 1000000000 : 1368847843
Time taken for 1 : 264
Time taken for 10 : 233
Time taken for 100 : 332
Time taken for 1000 : 1562
Time taken for 10000 : 17341
Time taken for 100000 : 136869
Time taken for 1000000 : 1366934
Time taken for 10000000 : 13689017
Time taken for 100000000 : 136887869
Time taken for 1000000000 : 1368178175
Time taken for 1 : 231
Time taken for 10 : 242
Time taken for 100 : 328
Time taken for 1000 : 1551
Time taken for 10000 : 13854
Time taken for 100000 : 136850
Time taken for 1000000 : 1366919
Time taken for 10000000 : 13692465
Time taken for 100000000 : 136833634
Time taken for 1000000000 : 1368862705
1.)為什么N = 1的時間異常大於N = 10
因為這是VM首次看到該代碼 - 它可能決定只是解釋它,或者需要花一點時間將它JIT化為本機代碼,但可能沒有優化。 這是Java基准測試的“陷阱”之一。
2.)為什么N = 10M及以后的時間異常低?
那時,JIT更加努力地優化代碼 - 將其減少到幾乎為零。
特別是,如果多次運行此代碼(僅在循環中),您將看到JIT編譯器優化的效果:
Time taken for 1 : 3732
Time taken for 10 : 1399
Time taken for 100 : 3266
Time taken for 1000 : 26591
Time taken for 10000 : 278508
Time taken for 100000 : 2496773
Time taken for 1000000 : 4745361
Time taken for 10000000 : 933
Time taken for 100000000 : 466
Time taken for 1000000000 : 933
Time taken for 1 : 933
Time taken for 10 : 467
Time taken for 100 : 466
Time taken for 1000 : 466
Time taken for 10000 : 933
Time taken for 100000 : 466
Time taken for 1000000 : 933
Time taken for 10000000 : 467
Time taken for 100000000 : 467
Time taken for 1000000000 : 466
Time taken for 1 : 467
Time taken for 10 : 467
Time taken for 100 : 466
Time taken for 1000 : 466
Time taken for 10000 : 466
Time taken for 100000 : 467
Time taken for 1000000 : 466
Time taken for 10000000 : 466
Time taken for 100000000 : 466
Time taken for 1000000000 : 466
正如您所看到的,在第一個循環之后,無論輸入是什么時間(模塊噪聲 - 基本上它總是~460ns或~933ns,不可預測),這意味着JIT優化了循環。
如果您實際返回了j
, 並將 i
的初始值更改為1
而不是0
,您將看到所期望的結果類型。 將i
的初始值更改為1
是因為否則JIT可以發現您總是最終返回0。
你實際上是對Java的JIT進行基准測試。 如果我修改你的代碼:
class Test
{
public static void main(String abc[])
{
for( int N=1; N <= 1_000_000_000; N=N*10)
{
long t1 = System.nanoTime();
start(N);
long t2 = System.nanoTime() - t1;
System.out.println("Time taken for " + N + " : " + t2);
}
for( int N=1; N <= 1_000_000_000; N=N*10)
{
long t1 = System.nanoTime();
start(N);
long t2 = System.nanoTime() - t1;
System.out.println("Time taken for " + N + " : " + t2);
}
}
public static void start( int N )
{
int j=1;
for(int i=0; i<=N; i++)
j=j*i;
}
}
我明白了:
Time taken for 1 : 1811
Time taken for 10 : 604
Time taken for 100 : 1510
Time taken for 1000 : 10565
Time taken for 10000 : 104439
Time taken for 100000 : 829173
Time taken for 1000000 : 604
Time taken for 10000000 : 302
Time taken for 100000000 : 0
Time taken for 1000000000 : 0
Time taken for 1 : 0
Time taken for 10 : 302
Time taken for 100 : 0
Time taken for 1000 : 302
Time taken for 10000 : 301
Time taken for 100000 : 302
Time taken for 1000000 : 0
Time taken for 10000000 : 0
Time taken for 100000000 : 0
Time taken for 1000000000 : 302
從不對“冷”系統進行基准測試。 總是重復每次測量幾次並丟棄前幾次測量,因為優化還沒有開始
原因是1)你沒有返回值,2)計算結果總是0.最后JIT將簡單地編譯循環。
如果將循環更改為以下內容,則會得到預期的行為:
public static int start(int N) {
int j = 1;
for (int i = 1; i <= N; i++)
j = j * i;
return j;
}
請注意,我已將循環init更改為int i = 1
並添加了return j
。 如果我只做其中一個,那么循環(最終)仍會被編譯掉。
這將產生以下系列(如果執行兩次):
Time taken for 1 : 2934
Time taken for 10 : 1466
Time taken for 100 : 3422
Time taken for 1000 : 20534
Time taken for 10000 : 191644
Time taken for 100000 : 1898845
Time taken for 1000000 : 1210489
Time taken for 10000000 : 11884401
Time taken for 100000000 : 115257525
Time taken for 1000000000 : 1061254223
Time taken for 1 : 978
Time taken for 10 : 978
Time taken for 100 : 978
Time taken for 1000 : 2444
Time taken for 10000 : 11244
Time taken for 100000 : 103644
Time taken for 1000000 : 1030089
Time taken for 10000000 : 10448535
Time taken for 100000000 : 107299391
Time taken for 1000000000 : 1072580803
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.