2008-11-05 34 views
10

Có sự khác biệt hiệu suất nào giữa các vòng lặp trên một mảng nguyên thủy không?hiện đại cho vòng lặp cho mảng nguyên thủy

Giả:

double[] doubleArray = new double[300000]; 


for (double var: doubleArray) 
    someComplexCalculation(var); 

hay:

for (int i = 0, y = doubleArray.length; i < y; i++) 
    someComplexCalculation(doubleArray[i]); 

kết quả thử nghiệm

Tôi thực sự cấu hình nó:

Total timeused for modern loop= 13269ms 
Total timeused for old loop = 15370ms 

Vì vậy, các vòng lặp hiện đại thực sự chạy nhanh hơn, ít nhất là trên Mac OSX JVM 1.5 của tôi.

+0

Mất bao nhiêu bộ nhớ. 80GB? – JesperE

+0

Tôi thực sự đã không suy nghĩ khi tôi viết rằng ... nhưng yeah. 80Gb – Dan

+0

Thực ra, nó sẽ không biên dịch vì 10000000000 không phải là một int. –

Trả lời

4

bạn viết tay, hình thức "cũ" thực hiện các lệnh ít hơn, và có thể được. nhanh hơn, mặc dù bạn phải cấu hình nó theo một trình biên dịch JIT cụ thể để biết chắc chắn. Biểu mẫu "mới" chắc chắn là không phải là nhanh hơn.

Nếu bạn nhìn vào mã tháo rời (biên soạn bởi của Sun JDK 1.5), bạn sẽ thấy rằng hình thức "mới" là tương đương với đoạn mã sau:

1: double[] tmp = doubleArray; 
2: for (int i = 0, y = tmp.length; i < y; i++) { 
3: double var = tmp[i]; 
4: someComplexCalculation(var); 
5: } 

Vì vậy, bạn có thể thấy rằng địa phương hơn biến được sử dụng.Việc gán doubleArray đến tmp ở dòng 1 là "phụ", nhưng nó không xảy ra trong vòng lặp và có thể không đo được. Việc gán cho số var ở dòng 3 cũng được bổ sung. Nếu có sự khác biệt về hiệu suất, điều này sẽ có trách nhiệm.

Dòng 1 có vẻ không cần thiết, nhưng đó là bản mẫu để lưu kết quả nếu mảng được tính bằng phương pháp trước khi vào vòng lặp.

Điều đó nói rằng, tôi sẽ sử dụng biểu mẫu mới, trừ khi bạn cần phải làm điều gì đó với biến chỉ mục. Bất kỳ sự khác biệt hiệu năng nào cũng có thể được tối ưu hóa bởi trình biên dịch JIT trong thời gian chạy và biểu mẫu mới rõ ràng hơn. Nếu bạn tiếp tục làm điều đó "bằng tay", bạn có thể bỏ lỡ các tối ưu hóa trong tương lai. Nói chung, một trình biên dịch tốt có thể tối ưu hóa mã "ngu ngốc" tốt, nhưng tình cờ gặp phải mã "thông minh".

2

Tại sao không tự đánh giá?

Điều này nghe có vẻ hơi khắc nghiệt, nhưng loại câu hỏi này rất dễ để tự xác minh.

Chỉ cần tạo mảng và thực hiện từng vòng lặp 1000 lần hoặc nhiều hơn và đo lượng thời gian. Lặp lại nhiều lần để loại bỏ các trục trặc.

1

Không có sự khác biệt. Java sẽ biến đổi tăng cường cho vào vòng lặp bình thường. Việc tăng cường cho chỉ là một "cú pháp đường". Bytecode được tạo giống nhau cho cả hai vòng lặp.

+1

Câu trả lời này không đúng. Biên dịch cả hai ví dụ và kiểm tra các mã byte kết quả. Bạn sẽ thấy rằng mã là khác nhau, như tôi giải thích trong câu trả lời của tôi. – erickson

5

Ý kiến ​​của tôi là bạn không biết và không nên đoán. Cố gắng để trình biên dịch outsmart những ngày này là không kết quả.

Đã có lần mọi người học "Mẫu" dường như tối ưu hóa một số hoạt động, nhưng trong phiên bản tiếp theo của Java, các mẫu đó thực sự chậm hơn.

Luôn viết rõ ràng như bạn có thể và đừng lo lắng về việc tối ưu hóa cho đến khi bạn thực sự có một số thông số người dùng trong tay và không đáp ứng được một số yêu cầu, và thậm chí rất cẩn thận để chạy trước và sau khi kiểm tra để đảm bảo rằng "sửa lỗi" của bạn thực sự cải thiện nó đủ để thực hiện yêu cầu đó. Trình biên dịch có thể thực hiện một số điều tuyệt vời mà thực sự thổi tất của bạn ra, và thậm chí nếu bạn thực hiện một số thử nghiệm lặp lại trên một phạm vi rộng lớn, nó có thể thực hiện hoàn toàn khác nhau nếu bạn có phạm vi nhỏ hơn hoặc thay đổi những gì xảy ra bên trong vòng lặp.

Chỉ trong thời gian biên dịch có nghĩa là đôi khi nó có thể hoạt động tốt hơn C, và không có lý do gì không thể hoạt động tốt hơn ngôn ngữ lắp ráp tĩnh trong một số trường hợp. . làm việc đó

Nói tóm lại: giá trị nhất mà bạn có thể đưa vào mã của bạn là để viết nó để có thể đọc

+1

Tôi muốn thêm, nếu anh ấy không biết và không nên đoán, anh ấy vẫn có thể * hỏi *, hoặc xem.Điều này không làm mất hiệu lực điểm mà trình biên dịch outsmarting là một mong manh, do đó, để nói, điều cần làm, kể từ khi cập nhật tiếp theo có thể hành xử khác nhau. Nhưng điều này không có nghĩa là anh ta không nên liên quan đến bất cứ điều gì. Nếu anh ta không nên nhập vào những chiều sâu của java vì lợi ích của một tối ưu hóa ổn định, nó có thể là do những người tạo java có nghĩa là anh ta chỉ tham khảo * đặc tả ngôn ngữ java *, ví dụ, thay vào đó. – n611x007

1

Tôi rất tò mò về câu hỏi của bạn, ngay cả sau câu trả lời trước của tôi. Vì vậy, tôi quyết định kiểm tra nó bản thân mình quá. Tôi viết mảnh nhỏ mã này (xin vui lòng bỏ qua đúng đắn toán về việc kiểm tra nếu một số là số nguyên tố ;-)):

public class TestEnhancedFor { 

    public static void main(String args[]){ 
     new TestEnhancedFor(); 
    } 

    public TestEnhancedFor(){ 
     int numberOfItems = 100000; 
     double[] items = getArrayOfItems(numberOfItems); 
     int repetitions = 0; 
     long start, end; 

     do { 
      start = System.currentTimeMillis(); 
      doNormalFor(items); 
      end = System.currentTimeMillis(); 
      System.out.printf("Normal For. Repetition %d: %d\n", 
        repetitions, end-start); 

      start = System.currentTimeMillis(); 
      doEnhancedFor(items); 
      end = System.currentTimeMillis(); 
      System.out.printf("Enhanced For. Repetition %d: %d\n\n", 
        repetitions, end-start); 

     } while (++repetitions < 5); 
    } 

    private double[] getArrayOfItems(int numberOfItems){ 
     double[] items = new double[numberOfItems]; 
     for (int i=0; i < numberOfItems; i++) 
      items[i] = i; 
     return items; 
    } 

    private void doSomeComplexCalculation(double item){ 
     // check if item is prime number 
     for (int i = 3; i < item/2; i+=2){ 
      if ((item/i) == (int) (item/i)) break; 
     } 
    } 

    private void doNormalFor(double[] items){ 
     for (int i = 0; i < items.length; i++) 
      doSomeComplexCalculation(items[i]); 
    } 

    private void doEnhancedFor(double[] items){ 
     for (double item : items) 
      doSomeComplexCalculation(item); 
    } 

} 

Chạy ứng dụng cho kết quả như sau cho tôi:

Bình thường Đối với. Lặp lại 0: 5594 Tăng cường cho. Lặp lại 0: 5594

Bình thường cho. Lặp lại 1: 5531 Tăng cường cho. Lặp lại 1: 5547

Bình thường cho. Lặp lại 2: 5532 Tăng cường cho. Lặp lại 2: 5578

Bình thường cho. Lặp lại 3: 5531 Tăng cường cho. Lặp lại 3: 5531

Bình thường cho. Lặp lại 4: 5547 Tăng cường cho. Lặp lại 4: 5532

Như chúng ta có thể thấy, sự thay đổi giữa các kết quả rất nhỏ và đôi khi vòng lặp bình thường chạy nhanh hơn, đôi khi vòng lặp nâng cao nhanh hơn. Vì có các ứng dụng khác đang mở trong máy tính của tôi, tôi thấy nó bình thường. Ngoài ra, chỉ thực hiện đầu tiên là chậm hơn so với những người khác - tôi tin rằng điều này đã làm với tối ưu hóa JIT.

Thời gian trung bình (trừ lần lặp lại đầu tiên) là 5535,25ms cho vòng lặp bình thường và 5547ms cho vòng lặp nâng cao. Nhưng chúng ta có thể thấy rằng thời gian chạy tốt nhất cho cả hai vòng là như nhau (5531ms), vì vậy tôi nghĩ chúng ta có thể đi đến kết luận rằng cả hai vòng lặp có cùng hiệu suất - và các biến thể của thời gian trôi qua là do các ứng dụng khác (thậm chí hệ điều hành) của máy.

+0

Kết quả thú vị! Như tôi mong đợi, JIT dường như đã tối ưu hóa sự khác biệt tinh tế trong mã byte đã biên dịch. – erickson

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