2013-03-23 40 views
14

Tôi đã cố gắng tạo phiên bản nhanh hơn của phương pháp String.equals() và bắt đầu bằng cách sao chép nó. Kết quả tôi thấy khá khó hiểu. Khi tôi chạy phiên bản đã dán bản sao, đã hẹn giờ và so sánh nó với phiên bản JVM, phiên bản JVM nhanh hơn. Sự khác biệt dao động từ 6x đến 34x nhanh hơn! Đơn giản chỉ cần đặt, chuỗi dài hơn, lớn hơn là sự khác biệt.Tại sao String.equals() nhanh hơn chính nó?

boolean equals(final char a[], final char b[]) { 
    int n = a.length; 
    int i = 0; 

    while (n-- != 0) { 
     if (a[i] != b[i]) return false; 
     i++; 
    } 
    return true; 
} 

public static void main() throws Exception { 
    String a = "blah balh balh"; 
    String b = "blah balh balb"; 

    long me = 0, jvm = 0; 

    Field value = String.class.getDeclaredField("value"); 
    value.setAccessible(true); 

    final char lhs[] = (char[]) value.get(a); 
    final char rhs[] = (char[]) value.get(b); 
    for (int i = 0; i < 100; i++) { 
     long t = System.nanoTime(); 
     equals(lhs, rhs); 
     t = System.nanoTime() - t; 
     me += t; 
    } 

    for (int i = 0; i < 100; i++) { 
     long t = System.nanoTime(); 
     a.equals(b); 
     t = System.nanoTime() - t; 
     jvm += t; 
    } 

    System.out.println("me = " + me); 
    System.out.println("jvm = " + jvm); 
} 

Output:

me = 258931 
jvm = 14991 

Phương pháp equals() tôi đã viết là một phiên bản sao chép dán của một tìm thấy trong String.equals() phương pháp. Tại sao phiên bản JVM nhanh hơn phiên bản được sao chép. Nó có hiệu quả không?

Ai đó có thể giải thích lý do tại sao tôi thấy sự khác biệt rõ ràng như vậy?

PS: Nếu bạn muốn thấy sự khác biệt lớn, bạn có thể tạo chuỗi dài (thực sự, thực sự dài) chỉ với một ký tự khác nhau ở cuối.

+1

Chỉ cần đoán, nhưng tôi nghĩ nó có thể liên quan đến tối ưu hóa thời gian chạy của JVM. Phiên bản gốc có thể được sử dụng rất nhiều trong nội bộ. Các phương thức được sử dụng thường xuyên có nhiều khả năng được tối ưu hóa bởi JVM. – Philipp

+1

Tôi nghĩ 'jvm' tối ưu hóa' String.equals' thành chỉ lệnh lắp ráp tương đương dựa trên tên của nó chứ không phải là mã. Và có lẽ cũng inline nó. Khi bạn sao chép mã, tối ưu hóa sẽ bị mất. – doublep

+1

@Phillip: Tôi cũng đoán vậy. Nếu đó là trường hợp thì JVM đang xử lý các lớp riêng của nó một cách đặc biệt! –

Trả lời

13

Tại sao phiên bản JVM nhanh hơn phiên bản được sao chép. Nó có hiệu quả không?

Đáng ngạc nhiên là không.

So sánh chuỗi là một hoạt động phổ biến như vậy gần như chắc chắn là trường hợp của bạn JIT compiler có nội tại cho String.equals(). Điều này có nghĩa là trình biên dịch biết cách tạo mã máy được chế tạo đặc biệt để so sánh các chuỗi. Điều này được thực hiện một cách minh bạch đối với bạn, lập trình viên, khi bạn sử dụng String.equals().

Điều này sẽ giải thích tại sao String.equals() nhanh hơn rất nhiều so với phương pháp của bạn, ngay cả khi bề ngoài chúng xuất hiện giống hệt nhau.

Tìm kiếm nhanh tìm thấy một số báo cáo lỗi đề cập đến nội tại trong HotSpot. Ví dụ: 7041100 : The load in String.equals intrinsic executed before null check.

Nguồn HotSpot có liên quan có thể được tìm thấy here. Các chức năng trong câu hỏi là:

848 Node* LibraryCallKit::make_string_method_node(int opcode, Node* str1, Node* cnt1, Node* str2, Node* cnt2) { 

943 bool LibraryCallKit::inline_string_equals() { 
+0

Ngay cả khi JIT không có nội tại, JVM có thể. Có thể thay thế một phương thức JDK bằng một phương thức nguyên gốc "dưới bìa", và tôi sẽ không đặt cược rằng nó không được thực hiện. –

+0

@HotLicks: Điều đó có nghĩa là tôi có thể viết một số JNI dưới mui xe để nó sử dụng SIMD như bộ hướng dẫn để thực hiện các so sánh có thể đánh bại nội tại hoặc người bản địa JVM/JDK? –

+0

@Eshan: Bạn có thể thử. Tất nhiên, không có gì đảm bảo rằng bạn sẽ thành công. – NPE

1

Hotspot cho phép các nhà phát triển để cung cấp một thực hiện nguồn gốc của một phương pháp bổ sung tình hình thực hiện Java. Mã Java được hoán đổi vào thời gian chạy và được thay thế bằng phiên bản được tối ưu hóa. Nó được gọi là bản chất. Vài trăm phương pháp từ các lớp cơ sở được tối ưu hóa bởi nội tại.

Bằng cách xem mã nguồn OpenJDK, bạn có thể see the x86_64 implementation of String.equals. Bạn cũng có thể xem xét vmSymbols để có danh sách tất cả các công cụ (tìm kiếm do_intrinsic)

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