2015-04-25 20 views
33

Kể từ JDK 7 Tôi đã vui vẻ sử dụng phương pháp này nó giới thiệu để từ chối null giá trị được truyền cho một phương pháp mà không thể chấp nhận chúng:Là Objects.requireNonNull kém hiệu quả hơn so với cách cũ?

private void someMethod(SomeType pointer, SomeType anotherPointer) { 
    Objects.requireNonNull(pointer, "pointer cannot be null!"); 
    Objects.requireNonNull(anotherPointer, "anotherPointer cannot be null!"); 
    // Rest of method 
} 

Tôi nghĩ rằng phương pháp này làm cho mã rất gọn gàng mà rất dễ dàng để đọc, và tôi đang cố khuyến khích các đồng nghiệp sử dụng nó. Nhưng một (đặc biệt là kiến ​​thức) đồng nghiệp là kháng, và nói rằng theo cách cũ là hiệu quả hơn:

private void someMethod(SomeType pointer, SomeType anotherPointer) { 
    if (pointer == null) { 
     throw new NullPointerException("pointer cannot be null!"); 
    } 
    if (anotherPointer == null) { 
     throw new NullPointerException("anotherPointer cannot be null!"); 
    } 
    // Rest of method 
} 

Ông nói rằng gọi requireNonNull bao gồm việc đặt một phương pháp khác trên cuộc gọi JVM chồng và sẽ dẫn đến hiệu suất tồi tệ hơn một đơn giản == null kiểm tra.

Vì vậy, câu hỏi của tôi: có bất kỳ bằng chứng của một hình phạt hiệu suất đang phát sinh bằng cách sử dụng các phương pháp Objects.requireNonNull?

+13

No. Không. Đồng nghiệp "hiểu biết" của bạn rõ ràng không phải là kiến ​​thức như (s) ông nghĩ. Bất cứ tác động nào về hiệu suất nó có thể có là không đáng kể và rất có thể sẽ được JIT gạch chân nếu nó phát hiện ra rằng phương pháp này được sử dụng nhiều. –

+13

JIT sẽ vui vẻ nội tuyến các cuộc gọi phương thức đó. Bạn nên nhớ quy tắc tối ưu đầu tiên cho padawan cũ của bạn: * không *. Anh ta nên là người chứng minh rằng lời gọi phương thức này là nguyên nhân của một vấn đề quan trọng trước khi buộc mọi người phải tạo ra mã ít đọc được hơn. –

+4

[Lỗi OpenJDK 8073479] (https://bugs.openjdk.java.net/browse/JDK-8073479) đã chuyển đổi phương thức 'foo.getClass()' của JDK để sử dụng 'Objects.requireNonNull' thay vào đó, tuyên bố không có hiệu suất suy thoái và đôi khi một sự cải thiện. –

Trả lời

38

Hãy nhìn vào việc thực hiện các requireNonNull trong JDK của Oracle:

public static <T> T requireNonNull(T obj) { 
    if (obj == null) 
     throw new NullPointerException(); 
    return obj; 
} 

Vì vậy, đó là rất đơn giản. JVM (Oracle, anyway) bao gồm một trình biên dịch hai giai đoạn tối ưu hóa để chuyển đổi bytecode thành mã máy. Nó sẽ nội tuyến phương pháp tầm thường như thế này nếu nó có thể nhận được hiệu suất tốt hơn theo cách đó.

Vì vậy, không, không có khả năng chậm hơn, không theo bất kỳ cách nào có ý nghĩa, không phải bất kỳ nơi nào quan trọng.

Vì vậy, câu hỏi của tôi: là có bất kỳ bằng chứng của một hình phạt hiệu quả được phát sinh bằng cách sử dụng các phương pháp Objects.requireNonNull?

Bằng chứng duy nhất quan trọng sẽ là phép đo hiệu suất của mã số hoặc mã được thiết kế để đại diện cho nó. Bạn có thể kiểm tra điều này với bất kỳ công cụ kiểm tra hiệu suất nào, nhưng trừ khi đồng nghiệp của bạn có thể trỏ đến một ví dụ thực tế về vấn đề hiệu suất trong codebase của bạn liên quan đến phương pháp này (chứ không phải là điểm chuẩn tổng hợp) bạn và anh/cô ấy có cá lớn hơn để chiên.


Một chút, tôi nhận thấy phương pháp mẫu của bạn là phương pháp private. Vì vậy, chỉ mã nhóm của bạn đang viết cuộc gọi trực tiếp. Trong những trường hợp đó, bạn có thể xem liệu bạn có sử dụng trường hợp cho assertions thay vì kiểm tra thời gian chạy hay không. Các xác nhận có lợi thế là không thực thi trong mã "được giải phóng" ở tất cả, và do đó nhanh hơn hoặc là thay thế trong câu hỏi của bạn. Rõ ràng có những nơi bạn cần kiểm tra thời gian chạy, nhưng những người thường ở các điểm gatekeeping, phương pháp công cộng và như vậy. Chỉ cần FWIW.

+0

Một cái gì đó khác là gãi trong tâm trí của tôi: là có bất kỳ chi phí trong việc tạo ra một 'String' dụ khi gọi phiên bản của' requireNonNull' mà có một thông báo lỗi như một tham số thứ hai? Nếu vậy, ẩn thông báo lỗi bên trong khối 'if' có tránh được chi phí này khi thử nghiệm' == null' là 'false' không? – Bobulous

+5

@Bobulous: Chuỗi đã tồn tại, vì chuỗi ký tự được nạp sẵn khi tệp lớp được tải và được đặt trong nhóm chuỗi. Nếu bạn đang làm 'Objects.requireIfNull (blah," literal "+ runtimeString);', thì bạn sẽ tạo một 'StringBuilder' tại thời gian chạy (dưới bìa) để làm phép nối, ngay cả khi chuỗi kết quả không đã sử dụng. Đó có thể là một số chi phí. Nhưng không phải nếu bạn đang sử dụng chữ hoặc tham chiếu đến các chuỗi tĩnh hiện có. ** Và ** không bao giờ đánh giá thấp JIT! Nó hoàn toàn được phép không chỉ nội tuyến, nhưng * sắp xếp lại *, mã để tránh các cửa hàng chết như thế. –

+0

@Bobulous Câu trả lời này hoàn toàn đúng, chỉ cần để tôi thêm vào, trong trường hợp bạn cần một thông điệp động, hãy cân nhắc sử dụng [checkNotNull của Guava với một errorMessageTemplate] (http://docs.guava-libraries.googlecode.com/git/javadoc /com/google/common/base/Preconditions.html#checkNotNull(T,%20java.lang.String,%20java.lang.Object ...)). Điều này giúp loại bỏ chuỗi nối nếu nó không cần thiết (một mảng varargs được tạo ra thay vì đó là rẻ hơn và có thể dễ dàng hơn để loại bỏ bởi JIT). – maaartinus

1

Đồng nghiệp của bạn có nhiều khả năng sai.

JVM rất thông minh và rất có thể sẽ tuân theo phương pháp Objects.requireNonNull(...). Hiệu suất là vấn đề nhưng sẽ có tối ưu hóa nghiêm túc hơn nhiều so với điều này.

Bạn nên sử dụng phương thức tiện ích từ JDK.

5

Nếu bạn muốn bằng chứng ... thì cách để có được nó là viết một điểm chuẩn vi mô.

(Tôi khuyên bạn nên xem xét dự án Calliper trước! Hoặc JMH ... theo đề xuất của Boris. Dù bằng cách nào, đừng cố gắng viết tiêu chuẩn vi mô từ đầu. Có quá nhiều cách để làm sai.)

Tuy nhiên, bạn có thể nói với bạn đồng nghiệp hai điều:

  • Trình biên dịch JIT làm một công việc tốt của nội tuyến các cuộc gọi phương pháp nhỏ, và nó có khả năng rằng điều này sẽ xảy ra trong trường hợp này.

  • Nếu không trực tiếp cuộc gọi, có khả năng sự khác biệt về hiệu suất sẽ chỉ là từ 3 đến 5 hướng dẫn và rất khó có thể tạo ra sự khác biệt đáng kể.

+1

Điều này [JMH so sánh các phương pháp kiểm tra null] (http://cr.openjdk.java.net/~shade/scratch/NullChecks.java) cho thấy rằng ngay cả trong trường hợp inlining thất bại phương pháp 'Objects.requireNonNull' mất ít hơn dài hơn 2ns so với mã if-null. Và nơi mã đã được inlined, có vẻ như không có bất kỳ sự khác biệt đáng kể giữa hai cách. – Bobulous

13

nói Chính thức, đồng nghiệp của bạn là đúng:

  • Nếu someMethod() hoặc tương ứng dấu vết là không đủ nóng, các mã byte được hiểu, và stack frame thêm được tạo ra

  • Nếu someMethod() được gọi ở cấp độ sâu 9th từ điểm nóng, các cuộc gọi requireNonNull() không được gạch chân vì MaxInlineLevel JVM Option

  • Nếu phương pháp không được gạch chân vì bất kỳ lý do nào ở trên, thì đối số của T.J. Crowder đi vào chơi, nếu bạn sử dụng nối để sản xuất thông báo lỗi

  • Ngay cả khi requireNonNull() được gạch chân, JVM lãng phí thời gian và không gian để thực hiện điều này.

Mặt khác, có tùy chọn JVM cấm inlining quá lớn (intecode). Các bytecode của phương thức được tính một mình, không có kích thước kế toán của các phương thức, được gọi trong phương thức này. Do đó, việc trích xuất các đoạn mã thành các phương thức độc lập có thể hữu ích đôi khi, trong ví dụ với requireNonNull(), việc trích xuất này được thực hiện cho bạn.

-3

Objects.requireNonNull được tối ưu hóa nhiều hơn như thể bạn là bạn đang sử dụng lại mã. Cũng trong oracle requireNonNull si được định nghĩa là

public static <T> T requireNonNull(T obj) { 
    if (obj == null) 
     throw new NullPointerException(); 
    return obj; 
} 

vì vậy đã có sẵn trong bytecode.

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