2012-01-12 24 views
13

Tôi đã viết một chút mã để tạo mảng đa chiều thay vì mảng mảng để tôi có thể lưu một số bộ nhớ. Sau đó, tôi chạy một số thử nghiệm để so sánh tốc độ của nó với mảng Java thông thường (int [] []) vì tôi không muốn chương trình của mình chạy chậm hơn ngay cả khi nó tiết kiệm bộ nhớ. Những gì tôi thấy trong các bài kiểm tra tính thời gian khiến tôi bối rối. Dưới đây là kết quả điển hình cho chạy thử nghiệm. Thời gian cho cùng một chút mã. Chú ý cách hai cái cuối cùng lớn hơn bốn cái đầu tiên.Tôi gặp vấn đề về hiệu năng Java mà tôi không hiểu

thời gian: 58.343.722 ns
thời gian: 59.451.156 ns
thời gian: 51.374.777 ns

thời gian: 61.777.424 ns
thời gian: 813.156.695 ns
thời gian: 782.140.511 ns

Bây giờ Điều đầu tiên tôi nghĩ là bộ thu gom rác đang nổ. Tôi nâng giới hạn bộ nhớ lên 5GB (-Xmx5g) sao cho bộ thu gom rác chắc chắn Tôi không bắt đầu. Không có gì thay đổi. Tôi di chuyển mọi thứ xung quanh, nhưng mô hình vẫn ở lại.

Vậy mô hình là gì? Trong ba lần đầu tiên, bit mã nằm trong một hàm và tôi gọi nó ba lần. Trong lần thứ ba ba lần bit mã được lặp lại trong một hàm duy nhất ba lần. Vì vậy, các mô hình là bất cứ khi nào bit mã được chạy nhiều lần trong cùng một chức năng thời gian cần để chạy bit mã sẽ bắt đầu với bit thứ hai của mã và ở lại đó sau đó.

Tôi đã tìm thấy một sự thay đổi đó sẽ tạo ra kết quả như thế này:

thời gian: 58.729.424 ns
thời gian: 59.965.426 ns
thời gian: 51.441.618 ns

thời gian: 57.359.741 ns
thời gian: 65362705 ns
thời gian: 857942387 ns

Những gì tôi đã làm là thêm một sự chậm trễ một phần nghìn giây giữa các bit mã của ba giây thứ hai. Làm điều đó chỉ tăng tốc độ thứ hai của các bit mã trong khối và không có lượng trễ sẽ tăng tốc bất kỳ bit mã nào sau đó.

Thẳng thắn, tôi bối rối. Tôi không thể giải thích hành vi này. Ai đó có thể làm sáng tỏ những gì đang xảy ra?

Đây là mã:

package multidimensionalarraytests; 

import java.lang.reflect.Array; 
import java.util.logging.Level; 
import java.util.logging.Logger; 

public class MultidimensionalArrayTests { 
    static ArrayInt2Dv1 array=new ArrayInt2Dv1(10000,10000); 

    public static void main(String[] args) { 
     System.out.println("ignore the warmup"); 
     test(); 
     test(); 
     combined(); 
     combined(); 

     System.out.println("running tests"); 
     test(); 
     test(); 
     test(); 
     System.out.println(); 
     combined(); 
    } 

    static long test(){ 
     int value=1; 
     long start,stop,time; 

     System.out.print("time: "); 
     start=System.nanoTime(); 
     for(int x=0;x<array.length1;x++){ 
      for(int y=0;y<array.length2;y++){ 
       array.set(x, y, value); 
       value=array.get(x, y); 
      } 
     } 
     stop=System.nanoTime(); 
     time=(stop-start); 
     System.out.println(time+" ns"); 
     return time; 
    } 

    static void combined(){ 
     int value=1; 
     long start,stop,time; 

     System.out.print("time: "); 
     start=System.nanoTime(); 
     for(int x=0;x<array.length1;x++){ 
      for(int y=0;y<array.length2;y++){ 
       array.set(x, y, value); 
       value=array.get(x, y); 
      } 
     } 
     stop=System.nanoTime(); 
     time=(stop-start); 
     System.out.println(time+" ns"); 

     //try {Thread.sleep(1);} catch (InterruptedException ex) {} 

     System.out.print("time: "); 
     start=System.nanoTime(); 
     for(int x=0;x<array.length1;x++){ 
      for(int y=0;y<array.length2;y++){ 
       array.set(x, y, value); 
       value=array.get(x, y); 
      } 
     } 
     stop=System.nanoTime(); 
     time=(stop-start); 
     System.out.println(time+" ns"); 

     //try {Thread.sleep(60000);} catch (InterruptedException ex) {} 

     System.out.print("time: "); 
     start=System.nanoTime(); 
     for(int x=0;x<array.length1;x++){ 
      for(int y=0;y<array.length2;y++){ 
       array.set(x, y, value); 
       value=array.get(x, y); 
      } 
     } 
     stop=System.nanoTime(); 
     time=(stop-start); 
     System.out.println(time+" ns");  
    } 
} 

và:

package multidimensionalarraytests; 

public class ArrayInt2Dv1 { 
    int [] array; 

    public final int length1; 
    public final int length2; 

    public ArrayInt2Dv1(int length1, int length2){ 
     this.length1=length1; 
     this.length2=length2; 
     array=new int[length1*length2]; 
    } 

    public int get(int x,int y){ 
     return array[x*length2+y]; 
    } 

    public void set(int x,int y,int value){ 
     array[x*length2+y]=value; 
    } 
} 

--- Chỉnh sửa ---

Kết quả trên Windows 7 với các tùy chọn -Xms5g -Xmx5g -XX: + PrintCompilation -verbose: gc -XX: CICompilerCount = 1 -Xbatch

time:  299 1 b  multidimensionalarraytests.ArrayInt2Dv1::set (15 bytes) 
    302 2 b  multidimensionalarraytests.ArrayInt2Dv1::get (14 bytes) 
    303 1 % b  multidimensionalarraytests.MultidimensionalArrayTests::test @ 31 (114 bytes) 
    358 1 %   multidimensionalarraytests.MultidimensionalArrayTests::test @ -2 (114 bytes) made not entrant 
60671451 ns 
    359 3 b  multidimensionalarraytests.MultidimensionalArrayTests::test (114 bytes) 
time:  365 2 % b  multidimensionalarraytests.MultidimensionalArrayTests::test @ 31 (114 bytes) 
58104484 ns 
time:  425 3 % b  multidimensionalarraytests.MultidimensionalArrayTests::combined @ 31 (330 bytes) 
69008251 ns 
time: 806898159 ns 
time: 845447596 ns 
    2146 4 b  multidimensionalarraytests.MultidimensionalArrayTests::combined (330 bytes) 
time: 52493169 ns 
time: 804304528 ns 
time: 845500191 ns 
running tests 
time: 51290771 ns 
time: 51922285 ns 
time: 51264108 ns 

time: 52258679 ns 
time: 842229025 ns 
time: 871403625 ns 

Trên Linux (Ubuntu trên VirtualBox trên cùng một máy) với các tùy chọn tương tự:

283 1 b java.lang.String::hashCode (60 bytes) 
    285 2 b sun.nio.cs.UTF_8$Encoder::encodeArrayLoop (490 bytes) 
    287 3 b java.lang.String::charAt (33 bytes) 
    287 4 b java.lang.String::indexOf (151 bytes) 
    297 5 b java.io.UnixFileSystem::normalize (75 bytes) 
    2850 6 b java.lang.String::lastIndexOf (156 bytes) 
ignore the warmup 
time: 5885 7 b multidimensionalarraytests.ArrayInt2Dv1::set (15 bytes) 
    5948 8 b multidimensionalarraytests.ArrayInt2Dv1::get (14 bytes) 
    5949 1% b multidimensionalarraytests.MultidimensionalArrayTests::test @ 31 (114 bytes) 
11529998483 ns 
    17565 9 b multidimensionalarraytests.MultidimensionalArrayTests::test (114 bytes) 
time: 1619622928 ns 
time: 19718 2% b multidimensionalarraytests.MultidimensionalArrayTests::combined @ 31 (330 bytes) 
475786382 ns 
time: 288586857 ns 
time: 315560700 ns 
    20789 10 b multidimensionalarraytests.MultidimensionalArrayTests::combined (330 bytes) 
time: 460577230 ns 
time: 311525066 ns 
time: 312343429 ns 
running tests 
time: 310261854 ns 
time: 298826592 ns 
time: 304689920 ns 

time: 315416579 ns 
time: 299473245 ns 
time: 290211350 ns 
+4

Để bắt đầu, hãy xem http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java – NPE

+0

Lời khuyên tốt. Tôi đã thay đổi mã trên để phản ánh các khuyến nghị và chạy chương trình với các tùy chọn: -Xms5g -Xmx5g -XX: + PrintCompilation -verbose: gc -XX: CICompilerCount = 1 -Xbatch. Nó đã không thay đổi đáng kể kết quả mặc dù. – user1145922

+1

Tôi đã thử (với mã được cập nhật) và tôi không thể tạo lại hiệu ứng bằng cách sử dụng Java 6u26 trên Ubuntu 64 bit. Ở đây, tất cả sáu lần trôi qua đều nằm trong khoảng 10% của nhau (thời gian khởi động biến động do trình biên dịch vừa mới khởi động). – NPE

Trả lời

6

Hãy thử -XX:+PrintCompilation này sẽ hiển thị rằng toàn bộ phương pháp được tối ưu hóa sau vòng đầu tiên lặp 10000 lần. Vấn đề là vòng lặp thứ hai/thứ ba được tối ưu hóa mà không có thông tin thống kê/truy cập. Đôi khi điều này không quan trọng, đôi khi các vòng lặp sau chậm hơn nhiều và nếu bạn hoán đổi thứ tự của các vòng lặp, các vòng lặp sau được cải thiện và vòng lặp đầu tiên sẽ chậm hơn.

Cách đơn giản để khắc phục điều này là đặt từng vòng lặp theo phương pháp riêng của nó và mỗi vòng lặp sẽ được tối ưu hóa đúng cách.

+0

OK, tôi thấy những gì bạn nói. Trong hàm kết hợp() một phép toán biên dịch xảy ra trong tập các vòng lặp, nhưng không phải là các vòng lặp khác. Nếu tôi thêm một ms ngủ thì các phép toán biên dịch xảy ra trong hai tập đầu tiên của vòng lặp, nhưng không phải là thứ ba. Các vòng mà các hoạt động biên dịch xảy ra tương ứng với các thực thi nhanh hơn. Nó có vẻ khá lạ lùng với tôi rằng nó sẽ chỉ tối ưu hóa tập đầu tiên của các vòng chứ không phải những người khác. Vâng, ít nhất tôi biết những gì nó làm mặc dù không phải lý do tại sao. – user1145922

+0

Một điều kỳ lạ là mặc dù tối ưu hóa tương tự được thực hiện khi tôi sử dụng một int [] [] chứ không phải là ArrayInt2Dv1 của tôi, nhưng không có hình phạt thời gian cho việc không chạy tối ưu hóa trên hai vòng cuối cùng. Nó khá khó chịu vì tôi sẽ không thể đơn giản thay thế mảng Java của mảng bằng mảng đa chiều của tôi mà không giữ các hoạt động của trình tối ưu hóa trong tâm trí hoặc mọi thứ có thể nhận được khá chậm. – user1145922

+0

Tất cả các vòng được tối ưu hóa, tuy nhiên các vòng sau được tối ưu hóa dựa trên không có thông tin được thu thập động (vì phương pháp được biên dịch cùng một lúc cơ sở không có thông tin tại thời điểm đó) –

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