2016-03-27 12 views
7

Câu hỏi của tôi là như sau:Do mảng đã nhập giúp JIT tối ưu hóa tốt hơn?

thông thường của nó đối với mã Java đã sưu tập chung thực hiện như:

public class GenericCollection<T> { 
    private Object[] data; 

    public GenericCollection() { 
     // Backing array is a plain object array. 
     this.data = new Object[10]; 
    } 

    @SuppressWarnings("unchecked") 
    public T get(int index) { 
     // And we just cast to appropriate type when needed. 
     return (T) this.data[index]; 
    } 
} 

Và sử dụng như thế này ví dụ:

for (MyObject obj : genericCollection) { 
    obj.myObjectMethod(); 
} 

Kể từ khi loại chung của genericCollection bị xóa, JVM dường như không có cách nào để biết rằng thực sự bên trong mảng 'data' của genericCollection chỉ có MyObject, vì kiểu thực tế của mảng là Object, có thể có một String trong đó, và gọi 'myObjectMethod' trên nó sẽ làm tăng một ngoại lệ.

Vì vậy, tôi giả sử JVM phải thực hiện một số kiểm tra thời gian chạy thể dục để biết những gì thực sự là bên trong trường hợp GenericCollection đó.

Bây giờ nhìn vào thực hiện điều này:

public class GenericCollection<T> { 
    private T[] data; 

    @SuppressWarnings("unchecked") 
    public GenericCollection (Class<T> type) { 
     // Create a type specific array. 
     this.data = (T[]) Array.newInstance(type, 10); 
    } 

    public T get (int index) { 
     // No unsafe casts needed. 
     return this.data[index]; 
    } 
} 

Trong trường hợp này, chúng tôi tạo ra một mảng loại cụ thể thông qua phản ánh, vì vậy JVM có thể suy ra có thể có chỉ là đối tượng T bên trong đó mảng trong một bối cảnh nhất định, làm cho các phôi không an toàn và có thể kiểm tra loại đắt tiền thừa.

Câu hỏi của tôi là, vì những điều HotSpot có thể làm, nó có giúp ích gì, hiệu quả, để triển khai các bộ sưu tập chung với một mảng sao lưu cụ thể kiểu "thích hợp" không?

Ví dụ, nó có giúp HotSpot trong việc loại bỏ kiểm tra loại hoặc phôi không cần thiết? Có lẽ có thể cho phép nó dễ dàng hơn các phương thức nội tuyến vì nó biết mảng sao lưu là của một kiểu cụ thể?

+0

hotspot JIT chủ yếu dựa trên các loại quan sát bởi profiling và không loại thông tin java cấp. Vì vậy, tạo mảng của nhiều loại khác nhau thực sự có thể tạo mã đa hình trong các đường dẫn mã không được mã hóa – the8472

Trả lời

6

Không có trong trường hợp cụ thể này.

Mảng chung T[] bị xóa thành Object[] trong mã byte. Bộ tách mảng cho Object[] luôn trả về Object, vì vậy nó không cần kiểm tra loại mảng thực tế. Do đó không có lợi ích khi có T[] thay vì Object[] cho hoạt động lấy mảng. Trong cả hai trường hợp, có aaload hướng dẫn theo sau là checkcast và hoạt động theo cùng một cách.

Thiết lập mảng trong khi đó sẽ hoạt động kém hơn cho mảng đã nhập thay vì Object[], vì aastore phải kiểm tra xem giá trị có khớp với loại thành phần mảng thực tế hay không.

Đó là, sửa đổi đề xuất của bạn hoạt động bình đẳng cho get, nhưng thực hiện tồi tệ hơn cho set. Điều này có thể được xác nhận bằng điểm chuẩn JMH sau đây.

package bench; 

import org.openjdk.jmh.annotations.*; 

import java.lang.reflect.Array; 

@State(Scope.Benchmark) 
public class Generics { 
    private ObjectArray<String> objectArray; 
    private GenericArray<String> genericArray; 
    private StringArray stringArray; 
    private int index; 

    @Param("100000") 
    private int length; 

    @Setup 
    public void setup() { 
     genericArray = new GenericArray<>(String.class, length); 
     objectArray = new ObjectArray<>(length); 
     stringArray = new StringArray(length); 

     for (int i = 0; i < length; i++) { 
      String s = Integer.toString(i); 
      objectArray.set(i, s); 
      genericArray.set(i, s); 
      stringArray.set(i, s); 
     } 
    } 

    @Benchmark 
    public String getGenericArray() { 
     return genericArray.get(nextIndex()); 
    } 

    @Benchmark 
    public String getObjectArray() { 
     return objectArray.get(nextIndex()); 
    } 

    @Benchmark 
    public String getStringArray() { 
     return stringArray.get(nextIndex()); 
    } 

    @Benchmark 
    public void setGenericArray() { 
     genericArray.set(nextIndex(), "value"); 
    } 

    @Benchmark 
    public void setObjectArray() { 
     objectArray.set(nextIndex(), "value"); 
    } 

    @Benchmark 
    public void setStringArray() { 
     stringArray.set(nextIndex(), "value"); 
    } 

    private int nextIndex() { 
     if (++index == length) index = 0; 
     return index; 
    } 

    static class GenericArray<T> { 
     private T[] data; 

     @SuppressWarnings("unchecked") 
     public GenericArray(Class<T> type, int length) { 
      this.data = (T[]) Array.newInstance(type, length); 
     } 

     public T get(int index) { 
      return data[index]; 
     } 

     public void set(int index, T value) { 
      data[index] = value; 
     } 
    } 

    static class ObjectArray<T> { 
     private Object[] data; 

     public ObjectArray(int length) { 
      this.data = new Object[length]; 
     } 

     @SuppressWarnings("unchecked") 
     public T get(int index) { 
      return (T) data[index]; 
     } 

     public void set(int index, T value) { 
      data[index] = value; 
     } 
    } 

    static class StringArray { 
     private String[] data; 

     public StringArray(int length) { 
      this.data = new String[length]; 
     } 

     public String get(int index) { 
      return data[index]; 
     } 

     public void set(int index, String value) { 
      data[index] = value; 
     } 
    } 
} 

Và kết quả:

Benchmark     (length) Mode Cnt Score Error Units 
Generics.getGenericArray 100000 avgt 40 5,212 ± 0,038 ns/op <- equal 
Generics.getObjectArray  100000 avgt 40 5,224 ± 0,043 ns/op <- 
Generics.getStringArray  100000 avgt 40 4,557 ± 0,051 ns/op 
Generics.setGenericArray 100000 avgt 40 3,299 ± 0,032 ns/op <- worse 
Generics.setObjectArray  100000 avgt 40 2,456 ± 0,007 ns/op <- 
Generics.setStringArray  100000 avgt 40 2,138 ± 0,008 ns/op 
+0

Phân tích tuyệt vời. Thật thú vị vì sao 'get' luôn chậm hơn' set' ngay cả khi không cần checkcast. Trục trặc liên quan đến lỗ đen? –

+1

@TagirValeev Chính xác. Mỗi 'get' trong benchmark này được ngầm theo sau bởi' Blackhole.consume' (không phải là tự do), trong khi 'set' thì không. – apangin

+0

Xin lỗi vì trả lời muộn. Câu trả lời tuyệt vời mặc dù nó không giải quyết một điểm trong câu hỏi của tôi. "set" là chậm hơn vì kiểm tra kiểu. phương thức "get" giống nhau. Vấn đề là, với một chung chung "bình thường", bạn sẽ không phải trả chi phí kiểm tra loại khi ** sử dụng ** đối tượng trả về của bộ sưu tập? "nhận được" một mình sẽ không phải chịu một loại kiểm tra vì đối tượng không được sử dụng, nhưng tôi đoán rằng trong trường hợp ví dụ của tôi (obj.myObjectMethod(); trong khi lặp lại), kiểm tra kiểu sẽ phải được thực hiện trong trường hợp chung "bình thường" trên mỗi truy cập, nhưng sẽ tránh được trong trường hợp mảng đã nhập. – TheStack

2

số Các type erasure Java Tutorial giải thích

Generics đã được giới thiệu về ngôn ngữ Java để cung cấp kiểm tra kiểu chặt chẽ hơn tại thời gian biên dịch và hỗ trợ lập trình generic. Để thực hiện Generics, trình biên dịch Java áp dụng loại tẩy xoá để:

  • Thay thế tất cả các tham số kiểu trong loại chung với giới hạn hoặc đối tượng của chúng nếu tham số kiểu không bị chặn. Bytecode được sản xuất, do đó, chỉ chứa các lớp, giao diện và phương thức thông thường.
  • Chèn phôi loại nếu cần thiết để bảo vệ an toàn loại.
  • Tạo các phương thức cầu để bảo tồn đa hình trong các loại chung mở rộng.

Do đó, sau khi biên soạn, các loại chung là Object.

+0

Generics là một cơ chế an toàn kiểu thời gian biên dịch; đúng như vậy, vào thời gian chạy nó được coi là một 'Object []'. –

+0

Vâng, chung loại đối tượng của nó. Nhưng như nó nói ở đó, trình biên dịch chèn phôi kiểu cần thiết (ví dụ, lấy một đối tượng từ bộ sưu tập). Bây giờ, những gì tôi hỏi là những gì xảy ra tại * runtime *. Mảng được tạo ra với kiểu thích hợp thông qua Array.newInstance (bạn có thể thử nó), vì vậy những gì tôi hỏi là HotSpot có thể làm điều gì đó thêm với loại thông tin kiểu đó không, như xóa các phôi không cần thiết/kiểm tra kiểu. Thông số chỉ đề cập đến việc xóa kiểu tại thời gian biên dịch, nhưng nó không chỉ rõ loại HotSpot có thể làm gì với thông tin kiểu bổ sung khi chạy. – TheStack

+0

Generics không giúp đỡ, mặc dù sử dụng một mảng nguyên thủy thay vì một mảng Object có thể giúp đỡ. –

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