2011-04-02 29 views
9

Tôi có chu kỳ sau:Gcc có tối ưu hóa chu kỳ của tôi với điều kiện không?

//condition will be set here to true or false 

for (int i = 0; i < LARGE_NUMBER; i++) { 
    if (condition) { 
     //do foo 
    } else { 
     //do bar 
    } 
} 

Hồn Xác Lên Trời: Một chu kỳ nếu nhanh hơn mà không cần một điều kiện so với một điều kiện. (Đây có phải là sự thật không?) Câu hỏi: Liệu gcc có vượt qua số if của tôi không, nếu condition đã được đặt ngoài chu kỳ và bản thân chu kỳ không chạm vào condition?

Nếu không, tôi nên chuyển sang các iffor, sao chép mã, vi phạm DRY vv

+0

nếu 'điều kiện' không thay đổi tại sao bạn đặt nó vào bên trong 'cho'? – nacho4d

+0

@ nacho4d: Để tránh trùng lặp mã (tiêu đề 'for' và các câu lệnh khác ngoài' if' nhưng bên trong 'for') – erenon

+0

Bạn có khai báo' điều kiện' là 'biến động 'không? – mvds

Trả lời

17

Đối với những người không muốn đọc một bài dài, tối ưu hóa này được gọi là (trong LLVM) Loop Unswitch.

Tại sao không yêu cầu trình biên dịch?

void foo(char* c); 

int main(int argc, char **argv) { 
    bool const condition = argc % 2; 

    for (int i = 0; i != argc; ++i) { 
    if (condition) { 
     foo(argv[1]); 
    } else { 
     foo(argv[0]); 
    } 
    } 
    return 0; 
} 

được chuyển thành dạng SSA (thông qua LLVM try out):

define i32 @main(i32 %argc, i8** nocapture %argv) { 
entry: 
    %0 = icmp eq i32 %argc, 0      ; <i1> [#uses=1] 
    br i1 %0, label %bb5, label %bb.nph 

bb.nph:           ; preds = %entry 
    %1 = and i32 %argc, 1       ; <i32> [#uses=1] 
    %toBool = icmp eq i32 %1, 0      ; <i1> [#uses=1] 
    %2 = getelementptr inbounds i8** %argv, i64 1 ; <i8**> [#uses=1] 
    br i1 %toBool, label %bb3.us, label %bb3 

bb3.us:           ; preds = %bb3.us, %bb.nph 
    %i.07.us = phi i32 [ %4, %bb3.us ], [ 0, %bb.nph ] ; <i32> [#uses=1] 
    %3 = load i8** %argv, align 8     ; <i8*> [#uses=1] 
    tail call void @_Z3fooPc(i8* %3) 
    %4 = add nsw i32 %i.07.us, 1     ; <i32> [#uses=2] 
    %exitcond = icmp eq i32 %4, %argc    ; <i1> [#uses=1] 
    br i1 %exitcond, label %bb5, label %bb3.us 

bb3:            ; preds = %bb3, %bb.nph 
    %i.07 = phi i32 [ %6, %bb3 ], [ 0, %bb.nph ] ; <i32> [#uses=1] 
    %5 = load i8** %2, align 8      ; <i8*> [#uses=1] 
    tail call void @_Z3fooPc(i8* %5) 
    %6 = add nsw i32 %i.07, 1      ; <i32> [#uses=2] 
    %exitcond8 = icmp eq i32 %6, %argc    ; <i1> [#uses=1] 
    br i1 %exitcond8, label %bb5, label %bb3 

bb5:            ; preds = %bb3, %bb3.us, %entry 
    ret i32 0 
} 

Không quá dễ đọc có lẽ, vì vậy hãy để tôi chỉ ra những gì ở đây:

  • entry: kiểm tra nếu argc bằng thành 0, nếu có, hãy truy cập bb5 (thoát) khác đi đến bb.nph
  • bb.nph: tính giá trị của condition, nếu đó là sự thật, đi đến bb3.us khác đi đến bb3
  • bb3.usbb3: vòng cho các điều kiện đúng và sai tương ứng
  • bb5: exit

Một trình biên dịch có thể khá nhiều biến đổi mã của bạn như thế nào nó muốn, miễn là hiệu quả là tương tự như những gì bạn yêu cầu. Trong trường hợp này, nó đã có hiệu quả viết lại các mã như:

int main(int argc, char**argv) { 
    if (argc != 0) 
    { 
    int i = 0; 
    if (argc % 2) { 
     do { 
     foo(argv[1]); 
     ++i; 
     } while (i != argc); 
    } else { 
     do { 
     foo(argv[0]); 
     ++i; 
     } while (i != argc); 
    } 
    } 
    return 0; 
} 

Đó là một hình thức của Vòng Invariant Tối ưu hóa, kết hợp ở đây với một tấm séc đầu tiên để tránh tình trạng máy tính nếu vòng lặp sẽ không được thực thi.

Đối với những người trong chúng ta, những người sẽ nghĩ giải pháp đầu tiên là rõ ràng hơn, chúng tôi rất vui khi có trình biên dịch thực hiện tối ưu hóa nitty gritty cho chúng tôi!

+1

* Lưu ý nhanh * Nó thậm chí có thể trở nên xấu hơn, trên thực tế, nếu trình biên dịch đang đi vào vòng lặp unrolling (để tránh kiểm tra xem nó có phải giữ vòng lặp tại mỗi lần lặp) hay không. Nếu bạn muốn xem xét việc unrolling thủ công, hãy tra cứu thiết bị của Duff, và có thể bạn sẽ đồng ý rằng nó tốt hơn nếu trình biên dịch thực hiện nó. –

+0

+1 để phân tích chi tiết và tuyệt vời. – Jon

+0

+1 & Chấp nhận cho việc tháo rời, cảm ơn. – erenon

8

Bất kỳ trình biên dịch tối ưu hóa phong nha sẽ làm điều này nếu condition thể được chứng minh để không thay đổi trong suốt phiên. Hơn nữa, ngay cả khi trình biên dịch không thực tế làm điều này, bạn sẽ làm tốt để hỗ trợ quyết định của bạn để viết lại mã vào một dạng ít người có thể đọc được với dữ liệu cứng từ lược tả. Không tối ưu hóa sớm. Nó không phải là giá trị nó để cung cấp cho độc giả của mã một "huh?" thời điểm để cạo râu một vài phần nghìn giây (và "độc giả" chắc chắn bao gồm bản thân bạn tại một thời điểm trong tương lai).

5

Tôi sẽ không chủ trương thực hiện bất kỳ hành động nào ở đây, bằng các đối số "tối ưu hóa sớm" thông thường. Việc giữ mã rõ ràng là quan trọng nhất, và nếu toàn bộ chương trình quá chậm, bạn có thể muốn lập hồ sơ và tìm các nút cổ chai thực tế (mà bạn thường không thể đoán được) sau khi chương trình đã được gỡ rối triệt để. Ngay cả khi trình biên dịch không tối ưu hóa trường hợp cụ thể này cho bạn, bạn có thể muốn biết rằng CPU thực hiện một số hình thức branch prediction sẽ giảm đáng kể thời gian cần thiết để xử lý điều kiện trong trường hợp điều kiện có thể dự đoán được.

Thật vậy, hầu hết các hướng dẫn xử lý CPU trong một pipeline và do thời gian địa chỉ bước nhảy phải được xác định, biến điều kiện có thể không xác định. Điều này sẽ gây ra một gian hàng đường ống và đây là nơi hầu hết các bộ vi xử lý hiện đại cố gắng đoán (thông minh trên thực tế), nơi chương trình sẽ nhảy. Nếu biến điều kiện thực sự được biết (như trong trường hợp của bạn), đoán sẽ là hoàn hảo. Vì vậy, tôi nghi ngờ rằng ngay cả với một trình biên dịch "câm", bạn sẽ thực sự thấy sự khác biệt giữa hai tùy chọn, ít nhất là trên các máy hiện đại.

+0

thực sự hầu hết các trình biên dịch nên viết hai vòng ở đây, tùy thuộc vào giá trị của điều kiện, nếu nó có thể được chứng minh rằng điều kiện không thay đổi trong vòng thực hiện vòng lặp. Nó không làm mất hiệu lực những gì bạn đang nói. –

+0

Hoàn toàn đồng ý với Matthieu. 1 cho dự đoán chi nhánh mà trong trường hợp này thực sự làm cho cuộc thảo luận tranh luận. – Jon

+0

+1 để đề cập đến dự đoán nhánh. Tôi quên điều đó. – erenon

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