2016-04-11 14 views
11

Tôi có vòng lặp for chạy 4096 lần và nó sẽ càng nhanh càng tốt. Hiệu suất thực sự quan trọng ở đây. Hiện tại tôi sử dụng các phương thức getter bên trong vòng lặp mà chỉ trả về các giá trị hoặc các đối tượng từ các trường không thay đổi trong khi vòng lặp đang được tiến hành.Java sử dụng getter trong vòng lặp hoặc tạo biến cục bộ?

Ví dụ:

for (;;) { 
    doSomething(example.getValue()); 
} 

Có bất kỳ chi phí sử dụng thu khí? Nó có nhanh hơn không?

Ví dụ:

Object object = example.getValue(); 
for (;;) { 
    doSomething(object); 
} 

Nếu có, đó là cũng đúng đối với việc tiếp cận các lĩnh vực công cộng như example.value?

Chỉnh sửa: Tôi không sử dụng System.out.println() bên trong vòng lặp.

Chỉnh sửa: Một số trường không phải là final. Không có trường nào là volatile và không có phương thức (getter) là synchronized.

+0

Lưu đầu ra trong một chuỗi cục bộ sẽ nhanh hơn so với getter hoặc đối tượng dereference. Nhưng điều này được đo bằng "thiếu niên" – Jamie

Trả lời

7

Như Rogério answered, nhận được tài liệu tham khảo đối tượng bên ngoài vòng lặp (Object object = example.getValue();) nhiều khả năng sẽ nhanh hơn (hoặc ít nhất sẽ không bao giờ được chậm hơn) so với cách gọi getter bên trong vòng lặp vì

  • trong trường hợp "tồi tệ nhất" , example.getValue() thực sự có thể làm một số công cụ rất tốn kém trong nền mặc dù getter methods được cho là "tầm thường". Bằng cách gán một tham chiếu một lần và tái sử dụng nó, bạn chỉ thực hiện tính toán tốn kém này một lần.
  • trong trường hợp "tốt nhất", example.getValue() thực hiện điều gì đó tầm thường như return value; và do đó gán nó bên trong vòng lặp sẽ không đắt hơn ngoài vòng lặp sau khi trình biên dịch JIT inlines the code.

Tuy nhiên, quan trọng hơn là sự khác biệt về ngữ nghĩa giữa hai và các hiệu ứng có thể của nó trong một môi trường đa luồng: Nếu trạng thái của đối tượng example thay đổi theo một cách gây example.getValue() trở lại tham chiếu đến các đối tượng khác nhau, có thể, trong mỗi lần lặp lại, phương thức doSomething(Object object) sẽ thực sự hoạt động trên một phiên bản khác nhau của Object bằng cách gọi trực tiếp doSomething(example.getValue());. Mặt khác, bằng cách gọi cho người dẫn chương trình bên ngoài vòng lặp và đặt tham chiếu đến thể hiện được trả về (Object object = example.getValue();), doSomething(object); sẽ hoạt động trên objectn lần cho n lần lặp.

Sự khác biệt này trong ngữ nghĩa có thể gây ra hành vi trong môi trường đa luồng hoàn toàn khác với môi trường đơn luồng. Hơn nữa, điều này không phải là vấn đề đa luồng thực tế "trong bộ nhớ": Nếu example.getValue() phụ thuộc vào ví dụ: cơ sở dữ liệu/tài nguyên mạng/HDD, có thể là dữ liệu này thay đổi trong quá trình thực hiện vòng lặp, làm cho có thể một đối tượng khác được trả về ngay cả khi ứng dụng Java là một luồng đơn.Vì lý do này, tốt nhất là xem xét những gì bạn thực sự muốn thực hiện với vòng lặp của bạn và sau đó chọn tùy chọn phản ánh tốt nhất hành vi mong muốn.

0

Nếu bạn cần chạy nhanh nhất có thể, bạn không nên sử dụng System.out.println trong các phần quan trọng.

Liên quan đến getter: Có chi phí nhỏ cho việc sử dụng getter, nhưng bạn không nên bận tâm về nó. Java không có tối ưu hóa getter và setter trong trình biên dịch JIT. Vì vậy, cuối cùng chúng sẽ được thay thế bằng mã gốc.

+1

Tôi không sử dụng 'System.out.println()' trong mã của tôi. Đây chỉ là một ví dụ xấu. – stonar96

4

Nó phụ thuộc vào bộ thu thập.

Nếu đó là trình rút gọn đơn giản, JIT sẽ xếp hàng trực tiếp vào truy cập trường trực tiếp, vì vậy sẽ không có sự khác biệt có thể đo lường được. Từ một quan điểm phong cách, sử dụng getter - nó ít mã hơn.

Nếu trình thu thập đang truy cập trường volatile, có thêm truy cập bộ nhớ bổ sung vì không thể giữ giá trị trong sổ đăng ký, tuy nhiên lần truy cập rất nhỏ.

Nếu bộ thu là synchronized, thì sử dụng biến cục bộ sẽ nhanh hơn khi khóa không cần lấy và giải phóng mọi cuộc gọi, nhưng mã vòng lặp sẽ sử dụng giá trị tiềm năng cũ của trường tại thời điểm getter được gọi.

+0

Cảm ơn bạn đã trả lời. Toàn bộ mã là chuỗi đơn. Vì vậy, không có các trường dễ bay hơi hoặc các phương thức được đồng bộ hóa. Các getters là getters đơn giản. – stonar96

4

Bạn nên thích một biến địa phương bên ngoài vòng lặp, vì những lý do sau đây:

  1. Nó có xu hướng để làm cho mã dễ đọc/hiểu, bằng cách tránh phương pháp lồng nhau gọi như doSomething(example.getValue()) trong một dòng mã và bằng cách cho phép mã cung cấp tên tốt hơn, cụ thể hơn cho giá trị được trả về bằng phương thức getter.
  2. Không phải tất cả các phương thức getter đều tầm thường (đôi khi chúng làm một số công việc tốn kém), nhưng các nhà phát triển thường không chú ý đến nó, giả sử một phương pháp cho là tầm thường và không tốn kém khi nó thực sự không. Trong những trường hợp như vậy, mã có thể đạt được hiệu suất đáng kể mà không cần nhà phát triển thực hiện. Khai thác vào một biến địa phương có xu hướng tránh vấn đề này.
1

Rất dễ lo lắng về hiệu suất nhiều hơn mức cần thiết. Tôi biết cảm giác.Một số điều cần xem xét:

  1. 4096 không nhiều, vì vậy trừ khi điều này phải hoàn thành trong một thời gian rất ngắn, đừng lo lắng về hiệu suất quá nhiều.
  2. Nếu có bất kỳ điều gì khác từ xa đắt tiền xảy ra trong vòng lặp này, bộ thu sẽ không thành vấn đề.
  3. Tối ưu hóa sớm là gốc rễ của mọi điều ác. Tập trung vào việc làm cho mã của bạn chính xác và rõ ràng trước tiên. Sau đó đo lường và cấu hình nó và thu hẹp những thứ đắt nhất, và chăm sóc đó. Cải thiện thuật toán thực tế nếu có thể.

Về câu hỏi của bạn, tôi không biết chính xác những gì JIT có, nhưng trừ khi nó có thể chứng minh một cách chắc chắn rằng example.getValue() hoặc example.value không thay đổi trong vòng lặp (đó là khó có thể làm trừ khi lĩnh vực này là final và getter là tầm thường) sau đó có một cách hợp lý không có cách nào nó có thể tránh gọi getter nhiều lần trong mẫu cũ vì điều đó sẽ có nguy cơ thay đổi hành vi của chương trình. Các cuộc gọi lặp đi lặp lại chắc chắn là một số lượng không làm việc thêm.

Có nói tất cả điều đó, tạo biến cục bộ bên ngoài vòng lặp, cho dù có nhanh hơn hay không, vì nó rõ ràng hơn. Có lẽ điều đó làm bạn ngạc nhiên, nhưng mã tốt không phải lúc nào cũng ngắn nhất. Mục đích thể hiện và thông tin khác là cực kỳ quan trọng. Trong trường hợp này, biến cục bộ bên ngoài vòng lặp hiển thị cho bất kỳ ai đọc mã mà đối số đến doSomething không thay đổi (đặc biệt nếu bạn làm cho nó cuối cùng) rất hữu ích để biết. Nếu không, họ có thể phải làm một số đào thêm để đảm bảo rằng họ biết cách chương trình hoạt động.

+0

"* sau đó có một cách hợp lý không có cách nào nó có thể tránh gọi hàm getter nhiều lần *" => phương thức getter vẫn có thể được inline, bất kể nó đang làm gì ... – assylias

+0

@assylias yes, nhưng đó vẫn là một số công việc, thậm chí nếu nó chỉ kiểm tra giá trị của một trường. –

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