2012-02-19 5 views
0

私は最近、マルチスレッド演算のためのフレームワークを作成し、それをテストするために千の素数の最初の2つを計算しました。Java 32ビットと64ビットの間の空のループで大規模なパフォーマンスの違い

しかし、私はより多くの時間を取るためにそれを必要とするので、私は素数の計算にこのコードを挿入:長い間

for (int i = 0; i < 1000000; i++) 
{ 
    // Nothing. 
} 

、私が書くと64ビットマシン上でコードをコンパイルし、それをテストしました32ビットマシンの数。

次に、64ビットマシンで実行し、大きなパフォーマンスの違いに気付きました。

完全に似た64台のマシンは、同じホスト上で2台の仮想マシンを実行するために、32台のマシンで何を使用するかは、< 100msです。

私は別のコンピュータにWindowsとUbuntu上でテストしてみた、と同じの.classファイルを使用して、私はまだ64ビット差対この巨大な32bit版を入手。

ここでは、パフォーマンスの違いを複製するためのクイックコードを示します。

import java.util.ArrayList; 
import java.util.Collection; 
public class Test { 
public static void main(String[] args) 
{ 
    long start = System.currentTimeMillis(); 
    int j = 2; 
    ArrayList<Integer> res = new ArrayList<Integer>(); 
    for (int k = 0; k < 50000; k++) 
    { 
     Collection<Integer> partres = work(k); 
     if (partres != null) 
      res.addAll(work(k)); 
    } 
    long end = System.currentTimeMillis(); 
    System.out.println("Done in " + (end-start) + " ms."); 
} 
public static Collection<Integer> work(Integer j) { 
    for (int i = 0; i < 1000000; i++) 
    { 
     // Nothing. 
    } 
    if (isPrime(j)) 
    { 
     ArrayList<Integer> res = new ArrayList<Integer>(); 
     res.add(j); 
     return res; 
    } 
    else 
     return null; 
} 
static boolean isPrime(int n) { 
    if (n == 2) return true; 
    if (n%2==0) return false; 
    for(int i = 3; i * i <= n; i += 2) 
     if(n%i==0) 
      return false; 
    return true; 
} 
} 

、今

here is the .class file i compiled it to.私の質問。

私は、64ビットマシンを使用することでパフォーマンスが向上することは知っていますが、それはこの大きな違いを説明していません。なぜ誰が何が起こっているのか分かりませんか?

+0

コンパイラは、空のループを最適化する必要があります。 –

+0

仮想マシンで常にテストしたことがありますか、または64ビットの正常な(またはこれまで呼ばれていた)マシンでしたか? – MByD

+0

私は、空のループがそれをベンチマークするための貧弱な方法であることを知っています。しかし、ポイントは、64ビットマシンで実行するかどうかによって、同じコンパイルされたファイルに大きなパフォーマンスの差があることです。 そして私はそれを仮想マシンと "ネイティブ"の64と32マシンでテストしました。 – Webbies

答えて

3

ウィンドウでは、デフォルトで32ビットの場合は-client JVMが使用され、64ビットJVMの場合は-serverが使用されます。サーバーJVMは、何もしないコードを除去する際に、より積極的です。例えば空のループ。あなたはそれがループを検出して排除するのにかかる時間の量に依存しているため、このようなループは関係なく、カウント制限の同じ時間程度かかるでしょう。同じ方法に2つ目のタイミングループを追加してみてください、あなたはそれは関係なく、あなたがこの方法は、時間によって、第2ループにコンパイルされるためです(そのない無限ループを想定)に最大値を設定するもののほとんどの時間を要するでしょう開始する。

http://docs.oracle.com/javase/1.5.0/docs/guide/vm/server-class.html

ところで:私はnanoTimeを使用して、秒の少なくともカップルのためrepeatelyテストを実行します。

+1

+1はドキュメントにリンクしています。私は "-server"を使って32ビットマシン上でコードを実行しようとしましたが、64ビットマシンとほぼ同じ時間がかかりました。 – Webbies

+0

x86-64 Linuxでは、 'server' VMもOracle JDKとOpenJDKのデフォルトです。 32bit Linuxについては不明。なぜOracleはデフォルトで 'server'を持つ32ビットJVMを出荷しないのですか?数年前、32ビットマシンのRAMが低くなる可能性がありました。実際には、誰も32ビットWindowsを古代ではないもので実行していますか? –

4

64ビットJavaは常に-server JITコンパイラを使用し、32ビットJVMはおそらく-client JITコンパイラを使用していました。

C2は別名です。 -serverコンパイラは、このような何かを見ている:

for (int i = 0; i < 1000000; i++) 
{ 
    // Nothing. 
} 

これは、ループが何もしないことに気づくだろう、とは削除されます!あなたのループは何もしません、何も最適化されません。

ループを何かする必要があります。たとえば、iを一緒にXORして、の結果をにすることができます。その後、ループはコンパイラの実際の作業のように見え、コードは保持されます。

+0

コードが保存されている可能性があります。サーバーJVMは、比較的複雑な(無意味な場合)コードを排除できます。 @PeterLawrey、 –

+1

Brian Goetzは、彼のJCiPの本、IIRCで説明したXORメソッドを推奨しています。 iを一緒に追加するのと違って、単純な乗算演算に変換することはできません。これはループの結果を利用する必要がある理由でもあります。 println()を使用します。 –

関連する問題