2013-03-25 23 views
19

Tôi đã viết một điểm chuẩn ít kiểm tra hiệu suất của java.lang.invoke.MethodHandle, java.lang.reflect.Method và các cuộc gọi trực tiếp của phương pháp.MethodHandle hiệu suất

Tôi đọc rằng hiệu suất MethodHandle.invoke() gần như giống như cuộc gọi trực tiếp. Nhưng kết quả thử nghiệm của tôi cho thấy kết quả khác: MethodHandle gọi chậm hơn ba lần so với phản xạ. Vấn đề của tôi là gì? Có thể đây là kết quả của một số tối ưu hóa JIT?

public class Main { 
    public static final int COUNT = 100000000; 
    static TestInstance test = new TestInstance(); 

    static void testInvokeDynamic() throws NoSuchMethodException, IllegalAccessException { 
     int [] ar = new int[COUNT]; 

     MethodHandles.Lookup lookup = MethodHandles.lookup(); 
     MethodType mt = MethodType.methodType(int.class); 

     MethodHandle handle = lookup.findStatic(TestInstance.class, "publicStaticMethod", mt) ; 

     try { 
      long start = System.currentTimeMillis(); 

      for (int i=0; i<COUNT; i++) { 
       ar[i] = (int)handle.invokeExact(); 
      } 

      long stop = System.currentTimeMillis(); 

      System.out.println(ar); 

      System.out.println("InvokeDynamic time: " + (stop - start)); 
     } catch (Throwable throwable) { 
      throwable.printStackTrace(); 
     } 
    } 

    static void testDirect() { 
     int [] ar = new int[COUNT]; 

     try { 
      long start = System.currentTimeMillis(); 

      for (int i=0; i<COUNT; i++) { 
       ar[i] = TestInstance.publicStaticMethod(); 
      } 

      long stop = System.currentTimeMillis(); 

      System.out.println(ar); 

      System.out.println("Direct call time: " + (stop - start)); 
     } catch (Throwable throwable) { 
      throwable.printStackTrace(); 
     } 
    } 

    static void testReflection() throws NoSuchMethodException { 
     int [] ar = new int[COUNT]; 

     Method method = test.getClass().getMethod("publicStaticMethod"); 

     try { 
      long start = System.currentTimeMillis(); 

      for (int i=0; i<COUNT; i++) { 
       ar[i] = (int)method.invoke(test); 
      } 

      long stop = System.currentTimeMillis(); 

      System.out.println(ar); 

      System.out.println("Reflection time: " + (stop - start)); 
     } catch (Throwable throwable) { 
      throwable.printStackTrace(); 
     } 
    } 

    static void testReflectionAccessible() throws NoSuchMethodException { 
     int [] ar = new int[COUNT]; 

     Method method = test.getClass().getMethod("publicStaticMethod"); 
     method.setAccessible(true); 

     try { 
      long start = System.currentTimeMillis(); 

      for (int i=0; i<COUNT; i++) { 
       ar[i] = (int)method.invoke(test); 
      } 

      long stop = System.currentTimeMillis(); 

      System.out.println(ar); 

      System.out.println("Reflection accessible time: " + (stop - start)); 
     } catch (Throwable throwable) { 
      throwable.printStackTrace(); 
     } 
    } 

    public static void main(String ... args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InterruptedException { 
     Thread.sleep(5000); 

     Main.testDirect(); 
     Main.testInvokeDynamic(); 
     Main.testReflection(); 
     Main.testReflectionAccessible(); 

     System.out.println("\n___\n"); 

     System.gc(); 
     System.gc(); 

     Main.testDirect(); 
     Main.testInvokeDynamic(); 
     Main.testReflection(); 
     Main.testReflectionAccessible(); 
    } 
} 

Môi trường: java phiên bản "1.7.0_11" Java (TM) SE Runtime Environment (xây dựng 1.7.0_11-B21) Java HotSpot (TM) 64-Bit Server VM (xây dựng 23,6-b04, hỗn hợp mode) OS - Windows 7 64

+1

trước tiên hãy đảm bảo bạn biết cách viết điểm chuẩn, xem: http://www.ibm.com/developerworks/java/library/j-jtp02225/index.html –

+0

@NathanHughes Có điểm gì sai với điểm chuẩn của mình? – Andremoniy

+0

@Andremoniy: vấn đề rõ ràng nhất là không có sự khởi động của jvm. –

Trả lời

1

Nó xuất hiện những người khác đã thấy kết quả tương tự: http://vanillajava.blogspot.com/2011/08/methodhandle-performance-in-java-7.html

Dưới đây là của người khác: http://andrewtill.blogspot.com/2011/08/using-method-handles.html

tôi chạy mà thứ hai và thấy chúng có cùng tốc độ thậm chí còn sửa chữa thử nghiệm đó để có sự ấm lên. Tuy nhiên, tôi đã sửa nó để nó không tạo ra một mảng args mỗi lần. Tại số đếm mặc định, nó dẫn đến kết quả tương tự: phương pháp xử lý nhanh hơn một chút. Nhưng tôi đã đếm được 10000000 (mặc định * 10) và sự phản chiếu nhanh hơn nhiều.

Vì vậy, tôi khuyên bạn nên thử nghiệm với các thông số. Tôi tự hỏi nếu MethodHandles hiệu quả hơn đối phó với các thông số? Ngoài ra, hãy kiểm tra việc thay đổi số lượng - số lần lặp lại.

@ bình luận Meriton của vào các liên kết câu hỏi để công việc của mình và trông rất hữu ích: Calling a getter in Java though reflection: What's the fastest way to repeatedly call it (performance and scalability wise)?

0

Nếu publicStaticMethod là một cài đặt đơn giản như trở về một hằng số, nó là rất nhiều khả năng là cuộc gọi trực tiếp là trong lót bằng JIT trình biên dịch. Điều này có thể không thực hiện được với methodHandles.

RE http://vanillajava.blogspot.com/2011/08/methodhandle-performance-in-java-7.html ví dụ, như đã đề cập, nhận xét việc triển khai không tuyệt vời của nó. nếu bạn thay đổi kiểu đúc thành int (thay vì số nguyên) trong vòng lặp tính toán, kết quả sẽ gần hơn với lệnh gọi phương thức trực tiếp.

Với việc triển khai phức tạp (tạo và gọi một tác vụ trong tương lai trả về một số ngẫu nhiên) cho điểm chuẩn với số gần hơn, nơi MethodStatic chậm hơn tối đa 10% so với phương pháp trực tiếp. Vì vậy, bạn có thể thấy hiệu suất chậm hơn 3 lần do tối ưu hóa JIT

2

Dường như điều này đã được trả lời gián tiếp bởi @AlekseyShipilev trong tham chiếu đến truy vấn khác. Trong liên kết sau How can I improve performance of Field.set (perhap using MethodHandles)?

Nếu bạn đọc qua, bạn sẽ thấy thêm điểm chuẩn hiển thị kết quả tương tự. Nó có khả năng là các cuộc gọi trực tiếp chỉ có thể được tối ưu hóa bởi JIT theo những cách mà Theo những phát hiện trên, sự khác biệt là: MethodHandle.invoke = ~ 195ns MethodHandle.invokeExact = ~ 10ns cuộc gọi trực tiếp = 1.266ns

Vì vậy, - các cuộc gọi trực tiếp sẽ vẫn nhanh hơn, nhưng MH rất nhanh. Đối với hầu hết các trường hợp sử dụng, điều này là đủ và chắc chắn nhanh hơn khung phản chiếu cũ (btw - theo các phát hiện ở trên, phản ánh cũng nhanh hơn đáng kể theo java8 vm)

Nếu sự khác biệt này có ý nghĩa trong hệ thống của bạn, tôi sẽ đề nghị tìm các mô hình khác nhau hơn là phản ánh trực tiếp sẽ hỗ trợ các cuộc gọi trực tiếp.

-1

chạy với tùy chọn VM: -Djava.compiler = NONE, có nghĩa là cấm JIT. Sau đó, bạn sẽ thấy sự khác biệt.