2012-06-26 24 views
8

Sau một câu hỏi liên quan đến cách JVM thực hiện tạo chuỗi dựa trên char [], tôi đã đề cập rằng không lặp lại diễn ra khi char [] được sao chép vào bên trong của chuỗi mới , kể từ khi System.arraycopy được gọi là cuối cùng, trong đó sao chép bộ nhớ mong muốn bằng cách sử dụng một chức năng như memcpy ở mức độ bản địa, phụ thuộc thực hiện (the original question).OpenJDK thực hiện System.arraycopy

Tôi muốn tự kiểm tra, vì vậy tôi đã tải xuống mã nguồn Openjdk 7 và bắt đầu duyệt qua. tôi thấy việc thực hiện System.arraycopy trong mã nguồn OpenJDK C++, trong openjdx/hotspot/src/share/vm/oops/objArrayKlass.cpp:

if (stype == bound || Klass::cast(stype)->is_subtype_of(bound)) { 
    // elements are guaranteed to be subtypes, so no check necessary 
    bs->write_ref_array_pre(dst, length); 
    Copy::conjoint_oops_atomic(src, dst, length); 
} else { 
    // slow case: need individual subtype checks 

Nếu các yếu tố không cần kiểm tra kiểu (đó là trường hợp với, ví dụ, nguyên thủy kiểu dữ liệu mảng), Sao chép: : conjoin_oops_atomic được gọi.

Chức năng Copy::conjoint_oops_atomic nằm trong 'copy.hpp':

// overloaded for UseCompressedOops 
static void conjoint_oops_atomic(narrowOop* from, narrowOop* to, size_t count) { 
    assert(sizeof(narrowOop) == sizeof(jint), "this cast is wrong"); 
    assert_params_ok(from, to, LogBytesPerInt); 
    pd_conjoint_jints_atomic((jint*)from, (jint*)to, count); 
} 

Bây giờ chúng ta đang phụ thuộc nền tảng, như hoạt động sao chép có thực hiện khác nhau, dựa trên hệ điều hành/kiến ​​trúc. Tôi sẽ đi với Windows làm ví dụ. openjdk\hotspot\src\os_cpu\windows_x86\vm\copy_windows_x86.inline.hpp:

static void pd_conjoint_oops_atomic(oop* from, oop* to, size_t count) { 
// Do better than this: inline memmove body NEEDS CLEANUP 
if (from > to) { 
    while (count-- > 0) { 
    // Copy forwards 
    *to++ = *from++; 
    } 
} else { 
    from += count - 1; 
    to += count - 1; 
    while (count-- > 0) { 
    // Copy backwards 
    *to-- = *from--; 
    } 
} 
} 

Và ... trước sự ngạc nhiên của tôi, nó lặp thông qua các yếu tố (các giá trị oop), sao chép chúng từng cái một (dường như). Ai đó có thể giải thích tại sao bản sao được thực hiện, ngay cả ở cấp bản địa, bằng cách lặp qua các phần tử trong mảng?

Trả lời

4

Bởi vì jint bản đồ gần nhất với int, bản đồ gần nhất với kiến ​​trúc phần cứng cũ WORD, về cơ bản có cùng kích thước với chiều rộng của bus dữ liệu.

Kiến trúc bộ nhớ và xử lý cpu của ngày hôm nay được thiết kế để thử xử lý ngay cả trong trường hợp thiếu bộ nhớ cache và vị trí bộ nhớ có xu hướng tìm nạp trước các khối. Mã mà bạn đang xem không phải là khá "xấu" trong hoạt động như bạn nghĩ. Phần cứng thông minh hơn và nếu bạn không thực sự tạo hồ sơ, các thói quen tìm nạp thông minh của bạn có thể thực sự không thêm gì (hoặc thậm chí là xử lý chậm).

Khi bạn được giới thiệu về kiến ​​trúc phần cứng, bạn phải được giới thiệu với những kiến ​​trúc đơn giản. Những cái hiện đại làm nhiều hơn, vì vậy bạn không thể giả định rằng mã có vẻ không hiệu quả thực sự không hiệu quả. Ví dụ, khi tra cứu bộ nhớ được thực hiện để đánh giá điều kiện trên câu lệnh if, thường cả hai nhánh của câu lệnh if được thực hiện trong khi tra cứu đang xảy ra và nhánh "xử lý" sai sẽ bị loại bỏ sau khi dữ liệu có sẵn để đánh giá điều kiện. Nếu bạn muốn có hiệu quả, bạn phải cấu hình và sau đó hành động trên dữ liệu lược tả.

Nhìn vào nhánh trên phần mã hóa JVM. Bạn sẽ thấy nó là (hoặc có lẽ, chỉ là) một macro kỳ quặc ifdef để hỗ trợ (cùng một lúc) ba cách khác nhau để nhảy vào mã xử lý mã opcode. Đó là vì ba cách khác nhau thực sự đã tạo nên sự khác biệt hiệu năng có ý nghĩa trên các kiến ​​trúc Windows, Linux và Solaris khác nhau.

Có lẽ họ có thể đã bao gồm các thói quen MMX, nhưng họ không nói với tôi rằng SUN không nghĩ rằng nó đủ hiệu suất trên phần cứng hiện đại để lo lắng về nó.

+0

Wow, cảm ơn! Đó là một chút bối rối tìm kiếm thông qua việc thực hiện OpenJDK lần đầu tiên, vì vậy tôi đã mong đợi đã nhận được một cái gì đó sai trái. : P Vậy bạn nghĩ việc tối ưu hóa này diễn ra như thế nào? Tôi đã làm một số bài kiểm tra và Hệ thống.arraycopy nhanh gấp hai lần khi sao chép 10000 ints so với cách Java thông thường. Trong C++ một nhiệm vụ tương tự vẫn còn đáng chú ý nhanh hơn, mặc dù kết quả có thể bị ảnh hưởng bởi các tối ưu hóa trình biên dịch khác nhau. –

+0

Bản sao C++ không có bộ thu gom rác chạy trên một chuỗi riêng biệt. Ngay cả khi bạn không tạo ra rác, người thu thập phải ăn cắp một vài chu kỳ để xác minh rằng nó không có việc gì để làm. Tôi không chắc chắn nếu trình biên dịch được unrolling loop arraycopy hoặc nếu phần cứng là prefetching toàn bộ khối của mảng vào bộ nhớ cache. Trong thực tế, với tối ưu hóa vi mã, nó vượt quá chiều sâu kiến ​​thức của tôi. Đó là lý do tại sao hồ sơ là rất quan trọng, đó là thử nghiệm chứng minh việc tối ưu hóa là đáng giá. –

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