2012-05-05 40 views
29

PMD nói với tôiHiệu quả: chuyển đổi báo cáo trên nếu phát biểu

Một chuyển đổi với ít hơn 3 chi nhánh là không hiệu quả, sử dụng một câu lệnh if để thay thế.

Tại sao lại như vậy? Tại sao 3? Làm thế nào để họ xác định hiệu quả?

+2

PMD là gì? ? ? – jmort253

+3

PMD quét mã nguồn Java và tìm kiếm các vấn đề tiềm ẩn như lỗi có thể, mã chết, mã tối ưu, biểu thức quá phức tạp và mã trùng lặp. (Di chuột qua các thẻ) – Chords

+9

Nó cũng nên quét chính nó cho ngữ pháp. "Ít hơn" nên "ít hơn". :) – yshavit

Trả lời

36

Vì câu lệnh switch được biên dịch với hai hướng dẫn JVM đặc biệt là lookupswitchtableswitch. Chúng rất hữu ích khi làm việc với nhiều trường hợp nhưng chúng gây ra chi phí khi bạn chỉ có một vài nhánh.

Thay vào đó, một tuyên bố if/else được biên dịch thành các chuỗi jejne ... thường nhanh hơn nhưng yêu cầu nhiều so sánh hơn khi được sử dụng trong một chuỗi chi nhánh dài.

Bạn có thể thấy sự khác biệt bằng cách nhìn vào mã byte, trong mọi trường hợp, tôi sẽ không lo lắng về những vấn đề này, nếu bất cứ điều gì có thể trở thành một vấn đề thì JIT sẽ chăm sóc nó.

ví dụ thực tiễn:

switch (i) 
{ 
    case 1: return "Foo"; 
    case 2: return "Baz"; 
    case 3: return "Bar"; 
    default: return null; 
} 

được biên dịch vào:

L0 
LINENUMBER 21 L0 
ILOAD 1 
TABLESWITCH 
    1: L1 
    2: L2 
    3: L3 
    default: L4 
L1 
LINENUMBER 23 L1 
FRAME SAME 
LDC "Foo" 
ARETURN 
L2 
LINENUMBER 24 L2 
FRAME SAME 
LDC "Baz" 
ARETURN 
L3 
LINENUMBER 25 L3 
FRAME SAME 
LDC "Bar" 
ARETURN 
L4 
LINENUMBER 26 L4 
FRAME SAME 
ACONST_NULL 
ARETURN 

Trong khi

if (i == 1) 
    return "Foo"; 
else if (i == 2) 
    return "Baz"; 
else if (i == 3) 
    return "Bar"; 
else 
    return null; 

được biên dịch vào

L0 
LINENUMBER 21 L0 
ILOAD 1 
ICONST_1 
IF_ICMPNE L1 
L2 
LINENUMBER 22 L2 
LDC "Foo" 
ARETURN 
L1 
LINENUMBER 23 L1 
FRAME SAME 
ILOAD 1 
ICONST_2 
IF_ICMPNE L3 
L4 
LINENUMBER 24 L4 
LDC "Baz" 
ARETURN 
L3 
LINENUMBER 25 L3 
FRAME SAME 
ILOAD 1 
ICONST_3 
IF_ICMPNE L5 
L6 
LINENUMBER 26 L6 
LDC "Bar" 
ARETURN 
L5 
LINENUMBER 28 L5 
FRAME SAME 
ACONST_NULL 
ARETURN 
+0

Jack cảm ơn bạn. Đây là một câu trả lời tuyệt vời. Trên một mặt lưu ý, bạn đã sử dụng cái gì để xem các tệp '.class'? – JAM

+0

Đó là một plugin cho Eclipse, nếu tôi nhớ chính xác nó phải là một: http://andrei.gmxhome.de/bytecode/index.html – Jack

+1

@JAM: Tôi tin rằng bạn cũng có thể sử dụng [javap] (http: // docs.oracle.com/javase/1.5.0/docs/tooldocs/windows/javap.html). – RanRag

7

Mặc dù có hiệu quả nhỏ khi sử dụng công tắc so với sử dụng câu lệnh if, những lợi ích đó sẽ không đáng kể trong hầu hết các trường hợp. Và bất kỳ máy quét mã nguồn nào có giá trị muối của nó sẽ nhận ra rằng micro-optimizations là thứ cấp để làm rõ mã.

Họ đang nói rằng một câu lệnh if đơn giản hơn để đọc và nhận ít dòng mã hơn lệnh chuyển đổi nếu công tắc ngắn gọn đáng kể.

Từ PMD website:

TooFewBranchesForASwitchStatement: báo cáo Chuyển được indended được sử dụng để hỗ trợ hành vi phân nhánh phức tạp. Việc sử dụng một công tắc chỉ trong một vài trường hợp là không đúng, vì các công tắc không dễ hiểu như các câu lệnh if-then. Trong những trường hợp này, sử dụng câu lệnh if-then để tăng khả năng đọc mã.

+1

Tầm quan trọng của sự rõ ràng vượt xa bất kỳ tối ưu hóa vi mô nào. Không nghi ngờ gì về điều đó. – JAM

+5

Điều tốt đẹp là họ nói "không hiệu quả" trong cảnh báo nhưng họ nói rằng nó chỉ liên quan đến "dễ đọc" trong tài liệu. Tốt mạch lạc .. – Jack

1

Tôi tin rằng nó có liên quan đến cách chuyển đổi và nếu/người khác biên dịch xuống.

Giả sử có 5 phép tính để xử lý câu lệnh chuyển đổi. Nói một câu lệnh if có hai phép tính. Ít hơn 3 tùy chọn trong chuyển đổi của bạn sẽ bằng 4 tính toán trong ifs so với 5 trong công tắc. Tuy nhiên, chi phí vẫn không đổi trong một switch, vì vậy nếu nó có 3 lựa chọn, ifs sẽ là 3 * 2 xử lý, so với 5 vẫn còn cho switch.

Lợi ích khi xem hàng triệu phép tính là vô cùng đáng kể. Đó là một vấn đề của "đây là cách tốt hơn để làm điều đó" hơn là bất cứ điều gì có thể ảnh hưởng đến bạn. Nó sẽ chỉ làm như vậy trên một cái gì đó mà chu kỳ trên chức năng đó hàng triệu lần trong một sự lặp lại khá.

6

Tại sao lại như vậy?

Trình tự khác nhau của hướng dẫn được sử dụng khi mã được biên dịch JIT biên dịch sang mã gốc. Một chuyển đổi được thực hiện bởi một chuỗi các lệnh gốc thực hiện một nhánh gián tiếp. (Trình tự thường tải một địa chỉ từ một bảng và sau đó các nhánh tới địa chỉ đó.) Một if/else được thực hiện như các lệnh đánh giá điều kiện (có thể là một lệnh so sánh) theo sau là một lệnh nhánh có điều kiện.

Vì sao 3?

Đây là một quan sát thực nghiệm, tôi giả sử dựa trên việc phân tích các hướng dẫn mã nguồn gốc được tạo và/hoặc điểm chuẩn. (Hoặc có thể không. Để hoàn toàn chắc chắn, bạn sẽ cần phải hỏi (các) tác giả về quy tắc PMD đó cách chúng bắt nguồn số đó.)

Làm cách nào để xác định hiệu quả?

Thời gian thực hiện để thực hiện hướng dẫn.


Cá nhân tôi gặp sự cố với quy tắc này ... hoặc chính xác hơn với thông báo. Tôi nghĩ rằng nó nên nói rằng một tuyên bố if/else là đơn giản và dễ đọc hơn một switch với 2 trường hợp. Vấn đề hiệu quả là thứ yếu và có thể không liên quan.

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