2010-05-05 24 views
74

Tôi đã rất ngạc nhiên khi thấy trong nguồn Java rằng System.arraycopy là một phương thức gốc.Tại sao System.arraycopy có nguồn gốc trong Java?

Tất nhiên lý do là vì nó nhanh hơn. Nhưng những gì thủ đoạn bản địa là mã có thể sử dụng mà làm cho nó nhanh hơn?

Tại sao không chỉ lặp qua mảng ban đầu và sao chép từng con trỏ vào mảng mới - chắc chắn điều này không phải là chậm và cồng kềnh?

Trả lời

70

Trong mã gốc, nó có thể được thực hiện với một đơn memcpy/memmove, trái ngược với n hoạt động sao chép riêng biệt. Sự khác biệt về hiệu suất là đáng kể.

+0

@ Peter, vì vậy trong vòng mã nguồn gốc bạn có thể thích ứng với mô hình bộ nhớ Java? (Tôi chưa bao giờ có bất kỳ nguyên nhân nào để làm bất kỳ malarkey nào) –

+0

@ James B, tôi không phải là một chuyên gia về điều này, nhưng trong mã gốc bạn chắc chắn không bị ràng buộc với mô hình bộ nhớ Java hoặc bất cứ điều gì - bạn có thể fiddle với nguyên bit bất kỳ cách nào bạn thích (nguy cơ của riêng bạn, tất nhiên). –

+7

Trên thực tế, chỉ có thể triển khai một số phụ của 'mảng bản sao' bằng cách sử dụng 'memcpy' /' memmove'. Những người khác yêu cầu kiểm tra kiểu thời gian chạy cho mỗi phần tử được sao chép. –

14

Nó không thể được viết bằng Java. Mã nguồn gốc có thể bỏ qua hoặc bỏ qua sự khác biệt giữa các mảng của đối tượng và mảng nguyên thủy. Java không thể làm điều đó, ít nhất là không hiệu quả.

Và nó không thể được viết với một đơn memcpy(), vì ngữ nghĩa được yêu cầu bởi mảng chồng chéo.

+2

Tốt, do đó, 'memmove' sau đó. Mặc dù tôi không nghĩ rằng nó tạo ra sự khác biệt nhiều trong ngữ cảnh của câu hỏi này. –

+0

Không phải memmove(), hoặc, xem nhận xét của @Stephen C về câu trả lời khác. – EJP

+0

Thấy rằng đã, kể từ đó đã xảy ra được câu trả lời của riêng tôi ;-) Nhưng nhờ anyway. –

3

Có một vài lý do:

  1. Các JIT là khó có khả năng tạo ra như hiệu quả mã cấp thấp như một mã số C bằng tay bằng văn bản. Sử dụng mức thấp C có thể cho phép rất nhiều tối ưu hóa gần như không thể làm cho một trình biên dịch JIT chung.

    Xem liên kết này đối với một số thủ thuật và so sánh tốc độ của bàn tay bằng văn bản triển khai C (memcpy, nhưng nguyên tắc là như nhau): Kiểm tra Optimizing Memcpy improves speed

  2. Phiên bản C này là khá nhiều độc lập của các loại và kích thước của các thành viên mảng. Không thể làm điều tương tự trong java vì không có cách nào để lấy nội dung mảng như một khối dữ liệu thô (ví dụ: con trỏ).

+1

Mã Java có thể được tối ưu hóa. Trong thực tế những gì thực sự xảy ra là mã máy được tạo ra đó là hiệu quả hơn so với C. –

+0

Tôi đồng ý rằng đôi khi mã JITed sẽ được tốt hơn localy tối ưu hóa vì nó biết được bộ xử lý đó là runnimg trên. Tuy nhiên, vì nó là "chỉ trong thời gian", nó sẽ không bao giờ có thể sử dụng tất cả những sự tối ưu hóa phi địa phương mất nhiều thời gian hơn để thực thi. Ngoài ra, nó sẽ không bao giờ có thể khớp với mã C được tạo thủ công (cũng có thể lấy bộ xử lý trong tài khoản và loại bỏ một phần lợi thế của JIT, hoặc bằng cách biên dịch cho một bộ xử lý cụ thể hoặc bằng một số kiểm tra thời gian chạy). –

+1

Tôi nghĩ rằng nhóm biên dịch Sun JIT sẽ tranh luận nhiều trong số những điểm đó. Ví dụ, tôi tin rằng HotSpot thực hiện tối ưu hóa toàn cầu để loại bỏ việc gửi đi phương thức không cần thiết, và không có lý do tại sao một JIT không thể tạo ra mã bộ xử lý cụ thể. Sau đó, có một điểm mà trình biên dịch JIT có thể thực hiện tối ưu hóa chi nhánh dựa trên hành vi thực hiện của ứng dụng hiện tại đang chạy. –

9

Tất nhiên, phụ thuộc triển khai.

HotSpot sẽ coi đó là mã "nội tại" và chèn vào trang web cuộc gọi. Đó là mã máy, không làm chậm mã C cũ. Điều này cũng có nghĩa là các vấn đề với chữ ký của phương pháp phần lớn biến mất.

Một vòng lặp sao chép đơn giản đủ đơn giản để tối ưu hóa rõ ràng có thể được áp dụng cho nó. Ví dụ vòng lặp unrolling. Chính xác những gì xảy ra là một lần nữa thực hiện phụ thuộc.

+1

đây là một câu trả lời rất phong nha :), đặc biệt. việc đề cập đến bản chất. w/o chúng lặp đi lặp lại đơn giản có thể nhanh hơn vì nó thường được unrolled anyways bởi JIT – bestsss

4

Trong các thử nghiệm của riêng tôi System.arraycopy() để sao chép nhiều mảng kích thước là 10 đến 20 lần nhanh hơn so với đan xen cho vòng:

float[][] foo = mLoadMillionsOfPoints(); // result is a float[1200000][9] 
float[][] fooCpy = new float[foo.length][foo[0].length]; 
long lTime = System.currentTimeMillis(); 
System.arraycopy(foo, 0, fooCpy, 0, foo.length); 
System.out.println("native duration: " + (System.currentTimeMillis() - lTime) + " ms"); 
lTime = System.currentTimeMillis(); 

for (int i = 0; i < foo.length; i++) 
{ 
    for (int j = 0; j < foo[0].length; j++) 
    { 
     fooCpy[i][j] = foo[i][j]; 
    } 
} 
System.out.println("System.arraycopy() duration: " + (System.currentTimeMillis() - lTime) + " ms"); 
for (int i = 0; i < foo.length; i++) 
{ 
    for (int j = 0; j < foo[0].length; j++) 
    { 
     if (fooCpy[i][j] != foo[i][j]) 
     { 
      System.err.println("ERROR at " + i + ", " + j); 
     } 
    } 
} 

này in:

System.arraycopy() duration: 1 ms 
loop duration: 16 ms 
+8

Mặc dù câu hỏi này là cũ, chỉ cho hồ sơ: Đây không phải là một điểm chuẩn công bằng (hãy để một mình câu hỏi, nếu một điểm chuẩn sẽ có ý nghĩa trong địa điểm đầu tiên). 'System.arraycopy' thực hiện một bản sao nông (chỉ các tham chiếu * vào bên trong' float [] 's được sao chép), trong khi các vòng lặp' for' của bạn thực hiện một bản sao sâu ('float' bởi' float'). Một thay đổi đối với 'fooCpy [i] [j]' sẽ được phản ánh trong 'foo' bằng cách sử dụng' System.arraycopy', nhưng sẽ không sử dụng 'for'-loop lồng nhau. – misberner

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