2017-08-28 25 views
363

Tôi đã đánh giá một số mã và tôi không thể chạy nó nhanh như với java.math.BigInteger, ngay cả khi sử dụng cùng một thuật toán chính xác. Vì vậy, tôi sao chép java.math.BigInteger nguồn vào gói của riêng tôi và cố gắng này:Java JIT có lừa dối khi chạy mã JDK không?

//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); 
    } 
} 

Khi tôi chạy này (jdk 1.8.0_144-b01 trên hệ điều hành MacOS) nó ra:

12089nsec/mul 
2559044166 

Khi tôi chạy nó với dòng nhập không được chú ý:

4098nsec/mul 
2559044166 

Nhanh gấp ba lần khi sử dụng phiên bản JDK của BigInteger so với phiên bản của tôi, ngay cả khi nó sử dụng chính xác mã.

Tôi đã kiểm tra bytecode với javap, và đầu ra trình biên dịch so với khi chạy với các tùy chọn:

-Xbatch -XX:-TieredCompilation -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions 
-XX:+PrintInlining -XX:CICompilerCount=1 

và cả hai phiên bản dường như để tạo mã tương tự. Vì vậy, là điểm phát sóng bằng cách sử dụng một số tối ưu hóa được chuẩn đoán trước mà tôi không thể sử dụng trong mã của mình? Tôi luôn hiểu rằng họ không biết. Điều gì giải thích sự khác biệt này?

+25

Thú vị. 1. Kết quả có nhất quán (hoặc ngẫu nhiên ngẫu nhiên) không? 2. Bạn có thể thử sau khi làm ấm JVM không? 3. Bạn có thể loại bỏ yếu tố ngẫu nhiên và cung cấp cùng một tập dữ liệu làm đầu vào cho cả thử nghiệm không? –

+7

Bạn đã thử chạy điểm chuẩn của mình với JMH http://openjdk.java.net/projects/code-tools/jmh/? Nó không phải là dễ dàng để thực hiện các phép đo một cách chính xác bằng tay (ấm lên và tất cả những thứ đó). –

+2

Vâng, nó rất nhất quán. Nếu tôi để nó chạy trong 10 phút tôi vẫn nhận được sự khác biệt tương tự. Hạt giống ngẫu nhiên cố định đảm bảo rằng cả hai chạy đều nhận được cùng một tập dữ liệu. –

Trả lời

490

Có, HotSpot JVM là loại "gian lận", bởi vì nó có một phiên bản đặc biệt của một số phương pháp BigInteger mà bạn sẽ không tìm thấy trong mã Java. Những phương pháp này được gọi là JVM intrinsics.

Cụ thể, BigInteger.multiplyToLen là phương pháp instrinsic trong HotSpot. Có một hand-coded assembly implementation đặc biệt trong cơ sở nguồn JVM, nhưng chỉ dành cho kiến ​​trúc x86-64.

Bạn có thể tắt công cụ này với tùy chọn -XX:-UseMultiplyToLenIntrinsic để buộc JVM sử dụng triển khai Java thuần túy. Trong trường hợp này, hiệu suất sẽ tương tự như hiệu suất của mã được sao chép của bạn.

P.S. Đây là list của các phương pháp nội tại HotSpot khác.

121

Trong Java 8 này thực sự là một nội tại, một phiên bản sửa đổi nhẹ của phương pháp này:

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; 
} 

Chạy này với:

java -XX:+UnlockDiagnosticVMOptions 
     -XX:+PrintInlining 
     -XX:+PrintIntrinsics 
     -XX:CICompilerCount=2 
     -XX:+PrintCompilation 
     <YourClassName> 

này sẽ in nhiều dòng và là một trong chúng sẽ là:

java.math.BigInteger::multiplyToLen (216 bytes) (intrinsic) 

Trong Java 9 mặt khác phương pháp mà dường như không phải là một bên trong nữa, nhưng lần lượt nó gọi một phương pháp mà là một nội tại:

@HotSpotIntrinsicCandidate 
private static int[] implMultiplyToLen 

Vì vậy, chạy cùng mã dưới Java 9 (với các thông số tương tự) sẽ tiết lộ:

java.math.BigInteger::implMultiplyToLen (216 bytes) (intrinsic) 

Bên dưới cùng một mã cho phương thức - chỉ cần đặt tên hơi khác.

Các vấn đề liên quan