私はいくつかのコードをベンチマークしていました、そして、私は Java.math.BigInteger
と同じくらい速く走らせることができませんでした、たとえまったく同じアルゴリズムを使ったとしても。そこで、私は Java.math.BigInteger
sourceを自分のパッケージにコピーして試してみました。
//import Java.math.BigInteger;
public class MultiplyTest {
public static void main(String[] args) {
Random r = new Random(1);
long tm = 0, count = 0,result=0;
for (int i = 0; i < 400000; i++) {
int s1 = 400, s2 = 400;
BigInteger a = new BigInteger(s1 * 8, r), b = new BigInteger(s2 * 8, r);
long tm1 = System.nanoTime();
BigInteger c = a.multiply(b);
if (i > 100000) {
tm += System.nanoTime() - tm1;
count++;
}
result+=c.bitLength();
}
System.out.println((tm / count) + "nsec/mul");
System.out.println(result);
}
}
これを実行すると(MacOSではjdk 1.8.0_144-b01)、次のように出力されます。
12089nsec/mul
2559044166
インポート行のコメントを外して実行した場合
4098nsec/mul
2559044166
たとえ まったく同じコード を使っていても、私のバージョンと比べてBigIntegerのJDKバージョンを使うときのほうが約3倍速いです。
私はjavapでバイトコードを調べ、オプション付きで実行したときのコンパイラ出力を比較しました。
-Xbatch -XX:-TieredCompilation -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions
-XX:+PrintInlining -XX:CICompilerCount=1
そして両方のバージョンは同じコードを生成するようです。それでは、ホットスポットは、コードで使用できない、事前計算済みの最適化を使用していますか?そうではないことを私はいつも理解していました。この違いは何を説明しているのでしょうか。
はい、HotSpot JVMは一種の「不正」です。Javaコードには見られない特別なバージョンのBigInteger
メソッドがあるからです。これらのメソッドは JVM組み込み関数 と呼ばれます。
特に、BigInteger.multiplyToLen
はHotSpotの本質的な方法です。 JVMソースベースには特別な ハンドコーディングされたアセンブリ実装 がありますが、これはx86-64アーキテクチャー専用です。
JVMに純粋なJava実装を使用させるには、-XX:-UseMultiplyToLenIntrinsic
オプションを使用してこの組み込み関数を無効にします。この場合、パフォーマンスはコピーしたコードのパフォーマンスと同じになります。
P.S これは他のHotSpot組み込みメソッドの list です。
Java 8 これは確かに組み込みメソッドです。このメソッドを少し修正したもの
private static BigInteger test() {
Random r = new Random(1);
BigInteger c = null;
for (int i = 0; i < 400000; i++) {
int s1 = 400, s2 = 400;
BigInteger a = new BigInteger(s1 * 8, r), b = new BigInteger(s2 * 8, r);
c = a.multiply(b);
}
return c;
}
これを実行します。
Java -XX:+UnlockDiagnosticVMOptions
-XX:+PrintInlining
-XX:+PrintIntrinsics
-XX:CICompilerCount=2
-XX:+PrintCompilation
<YourClassName>
これはたくさんの行を印刷するでしょう、そしてそれらのうちの1つは次のようになるでしょう:
Java.math.BigInteger::multiplyToLen (216 bytes) (intrinsic)
一方、 Java 9 では、そのメソッドはもう組み込み関数ではないように見えますが、その代わりにそれは組み込み関数であるメソッドを呼び出します。
@HotSpotIntrinsicCandidate
private static int[] implMultiplyToLen
そのため、同じコードをJava 9で(同じパラメータで)実行すると、次のことがわかります。
Java.math.BigInteger::implMultiplyToLen (216 bytes) (intrinsic)
その下には、このメソッドの同じコードがあります。名前が少し異なるだけです。