2009-07-13 27 views
7

Tôi đã thấy các chủ đề khác nói hiệu suất phản chiếu của java chậm hơn 10-100x so với khi sử dụng các cuộc gọi không phản chiếu.Diễn giải hiệu suất phản chiếu Java: Tại sao nó lại rất nhanh?

Bài kiểm tra của tôi ở 1.6 đã chỉ ra rằng đây không phải là trường hợp nhưng tôi đã tìm thấy một số điều thú vị khác mà tôi cần một người nào đó giải thích cho tôi.

Tôi có các đối tượng triển khai giao diện của mình. Tôi đã thực hiện ba điều 1) bằng cách sử dụng một tham chiếu đến một đối tượng mà tôi đưa đối tượng đó vào giao diện và gọi phương thức thông qua giao diện 2) bằng cách sử dụng tham chiếu đến đối tượng thực sự gọi phương thức trực tiếp và 3). Tôi thấy rằng cuộc gọi giao diện # 1 nhanh nhất được theo sau bởi phản xạ # 3 nhưng tôi nhận thấy rằng cuộc gọi phương thức trực tiếp là chậm nhất bởi một biên độ tốt.

Tôi không hiểu điều đó, tôi đã mong đợi cuộc gọi trực tiếp nhanh nhất, sau đó giao diện, sau đó phản ánh sẽ chậm hơn nhiều.

Blah và ComplexClass nằm trong gói khác với lớp chính và cả hai đều có phương thức doSomething (int x) triển khai giao diện và chỉ in số nguyên x.

Dưới đây là kết quả của tôi (lần trong ms, kết quả w nhiều thử nghiệm/rất giống nhau): gọi một phương pháp trực tiếp: 107.194 gọi một phương thức trực tiếp từ một dàn diễn viên đối tượng cho một giao diện: 89.594 gọi một phương thức thông qua phản ánh: 90453

đây là mã của tôi:

public class Main 
{ 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) 
    { 
     Blah x = new Blah(); 
     ComplexClass cc = new ComplexClass(); 
     test((Object) x, cc); 
    } 

    public static void test(Object x, ComplexClass cc) 
    { 
     long start, end; 
     long time1, time2, time3 = 0; 
     int numToDo = 1000000; 
     MyInterface interfaceClass = (MyInterface) x; 

     //warming up the cache 
     for (int i = 0; i < numToDo; i++) 
     { 
      cc.doSomething(i); //calls a method directly 
     } 

     start = System.currentTimeMillis(); 
     for (int i = 0; i < numToDo; i++) 
     { 
      cc.doSomething(i); //calls a method directly 
     } 
     end = System.currentTimeMillis(); 
     time1 = end - start; 

     start = System.currentTimeMillis(); 
     for (int i = 0; i < numToDo; i++) 
     { 
      interfaceClass.doSomething(i); //casts an object to an interface then calls the method 
     } 
     end = System.currentTimeMillis(); 
     time2 = end - start; 


     try 
     { 
      Class xClass = x.getClass(); 
      Class[] argTypes = 
      { 
       int.class 
      }; 
      Method m = xClass.getMethod("doSomething", argTypes); 
      Object[] paramList = new Object[1]; 
      start = System.currentTimeMillis(); 
      for (int i = 0; i < numToDo; i++) 
      { 
       paramList[0] = i; 
       m.invoke(x, paramList); //calls via reflection 
      } 
      end = System.currentTimeMillis(); 
      time3 = end - start; 

     } catch (Exception ex) 
     { 
     } 

     System.out.println("calling a method directly: " + time1); 
     System.out.println("calling a method directly from an object cast to an interface: " + time2); 
     System.out.println("calling a method through reflection: " + time3); 
    } 
+0

WHY CW? nó là một câu hỏi liên quan đến lập trình không chủ quan hợp lệ? Và tại sao các lá phiếu đóng lại ?! – hhafez

+0

Tôi đã đặt tiêu đề thành câu hỏi cho những người không thể xem câu hỏi trừ khi nó có dấu. Nhưng, cộng đồng wiki ?? –

Trả lời

8

Đưa tất cả các bài kiểm tra vào cùng một chương trình là một sai lầm microbenchmarking - có một ấm áp nhất định lên kết hợp với hiệu suất Java. Đây là thất bại quan trọng nhất.

Đặt thử nghiệm của bạn trong các chương trình riêng biệt. Sau đó chạy các bài kiểm tra nhiều lần, vì vậy bạn sẽ có được cảm giác khi thời gian ấm lên đã kết thúc và có ý nghĩa thống kê.

Ngoài ra, bạn có một phương pháp rất lớn chứa vòng lặp bên trong của bạn. Hotspot có vẻ tốt hơn trong việc đối phó với điều này hơn trước đây, nhưng nó vẫn không tốt.

Bạn sẽ thấy rằng với -server gọi phương thức ảo (ngay cả khi được tải bởi trình nạp lớp khác) trong một vòng lặp chặt chẽ sẽ được tối ưu hoàn toàn. Do đó, không có ý nghĩa gì khi nói rằng cuộc gọi trực tiếp nhanh đến mức nào nhiều hơn là một cuộc gọi phản chiếu.

+0

ý của bạn là có một phương pháp khổng lồ có chứa vòng lặp bên trong? Bạn có nói rằng phương pháp thử nghiệm chính nó là quá lớn? Tại sao lại là một vấn đề? máy chủ là gì? – jbu

+0

và tôi thấy điểm của bạn với vấn đề là các bài kiểm tra cá nhân không có trong phương pháp riêng của họ. Mặc dù tôi có cảm giác tôi sẽ thấy kết quả tương tự. Sẽ tạo một bài đăng riêng biệt có thể một khi tôi nhận được những kết quả đó. – jbu

+1

Bạn đã có một phương pháp thử nghiệm lớn lặp lại vòng lặp hàng triệu lần. -server là công cụ chuyển đổi nhanh hơn (nhưng tăng thời gian khởi động và không nhất thiết phải có mặt, đặc biệt là trên Windows JRE). Các xét nghiệm nên được trong quá trình riêng của họ. Chạy chương trình mới cho mỗi bài kiểm tra. –

4

Đầu tiên, phản ánh nhanh hơn nhiều trong các JDK mới nhất. Thứ hai, tôi hy vọng rằng trình biên dịch Hot Spot sẽ tối ưu hóa tất cả các cuộc gọi này đến gần mã. Nó có thể làm phân tích thời gian chạy để nhận ra rằng bạn đang gọi cùng một hàm lặp đi lặp lại để nó có thể tối ưu hóa sự phản chiếu (và gọi hàm ảo). Điều tương tự với ví dụ về giao diện.

+0

+1 để cải thiện sự phản chiếu trong jdk sau này. –

0

Thử nghiệm của tôi cho thấy rằng nếu java có thể có chức năng nội tuyến, lệnh gọi trực tiếp có thể cực kỳ nhanh. Trong cuộc gọi trực tiếp xếp hàng nhanh hơn 200-300 lần so với cuộc gọi phản chiếu. Thử nghiệm trên ubuntu 12,10, Jdk 1,6,35, CPU Xeon E5-2620.

Java ngày càng thông minh hơn và thông minh hơn.

import java.lang.reflect.Method; 

public class Main 
{ 
    static class Test 
    { 
     int i=0; 
     public void set(int value){ 
      this.i = value; 
     } 
    } 

public static void main(String[] args) throws Exception 
{ 
    Test test = new Test(); 
    int max = 10000000; 

    long direct = System.currentTimeMillis(); 
    for(int i=0; i<max; i++){ 
     Integer io = new Integer(i*i); 
     test.set(io); 
    } 
    System.out.println("Direct : " + (System.currentTimeMillis() - direct)); 

    Method method = Test.class.getMethod("set", Integer.TYPE); 
    long reflection = System.currentTimeMillis();   
    for(int i=0; i<max; i++){ 
     Integer io = new Integer(i*i); 
     method.invoke(test, io); 
    } 
    System.out.println("Reflection : " + (System.currentTimeMillis() - reflection)); 

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