2010-08-20 20 views
11

Mã thử nghiệm sau đây thực hiện chính xác trong VS hoặc với gỡ lỗi hoặc phát hành, và cũng trong GCC. Nó cũng làm đúng cho ICC với gỡ lỗi, nhưng không phải khi tối ưu hóa được kích hoạt (-O2).Trình biên dịch có được phép loại bỏ các vòng vô hạn như trình biên dịch C++ của Intel với -O2 không?

#include <cstdio> 

class tClassA{ 
public: 
    int m_first, m_last; 

    tClassA() : m_first(0), m_last(0) {} 
    ~tClassA() {} 

    bool isEmpty() const {return (m_first == m_last);} 
    void updateFirst() {m_first = m_first + 1;} 
    void updateLast() {m_last = m_last + 1;} 
    void doSomething() {printf("should not reach here\r\n");} 
}; 

int main() { 
    tClassA q; 
    while(true) { 
    while(q.isEmpty()) ; 
    q.doSomething(); 
    } 
    return 1; 
} 

Có nghĩa vụ dừng tại while(q.isEmpty()). Khi -O2 được bật theo ICC (bản phát hành), tuy nhiên, nó bắt đầu "doSomething" vô hạn.

Vì đây là chương trình đơn luồng isEmpty() nên được đánh giá là true, tôi không thể tìm thấy lý do nào để ICC hoạt động theo cách này? Tôi có nhớ gì không?

+0

nó giúp nếu m_first và m_last được khai báo là 'bay hơi'? Tôi không có quyền truy cập vào ICC. – Chubsdad

+0

Một ý nghĩ hoang dã khác: nó có liên quan đến thực tế là% s không được chỉ định với printf. printf ("% s", "không nên đến đây \ r \ n") ;? – Chubsdad

+5

Liên quan: [Trình biên dịch có được phép loại bỏ vòng lặp vô hạn không?] (Http://stackoverflow.com/questions/2178115/are-compilers-allowed-to-eliminate-infinite-loops) –

Trả lời

0

Đặt cược tốt nhất của bạn là thực hiện bước nhị phân kết quả vào nó và bỏ lắp ráp chức năng chính và xem lắp ráp nào được tạo ra. Không nói rằng bạn sẽ có thể nhìn thấy một lỗi, nhưng bạn có thể thấy nếu một cái gì đó đã được tối ưu hóa.

+0

Bạn không thể. Mã asm được tối ưu hóa mọi thứ và bạn không thể thấy gì cả. – Samuel

+0

Tôi không chắc điều đó có nghĩa là gì. Bạn đang nói rằng bạn dis-lắp ráp các kết quả nhị phân và while() vòng lặp đã biến mất? – linuxuser27

2

Nó chắc chắn giống như một lỗi. Dưới đây là một (khá hoang dã) đoán về những gì có thể có lý do dẫn đến nó ...

Sau khi nội tuyến, nó thấy:

while (q.m_first == q.m_last) /* do nothing */ ; 
do_something(); 

và bất kỳ chuỗi do nothing repeatedly ; do something có thể được dịch chỉ đơn giản là "làm điều gì đó". Điều này rơi xuống nếu phần lặp lại là vô tận (như trong trường hợp này). Nhưng có lẽ họ không kiểm tra việc biên dịch của họ trên các ví dụ cố tình có vòng lặp vô tận ;-).

+0

Phát hiện vòng lặp vô tận cố ý về cơ bản là vấn đề dừng, vì vậy thay vào đó họ giả định rằng, vì nó không có tác dụng phụ rõ ràng, nó không làm gì và do đó là vô ích đối với chương trình và có thể bị xóa. Rõ ràng, có hay không nó hoàn thành IS có thể nhìn thấy hiệu ứng cho chúng ta, nhưng không phải cho C/C++ trừu tượng máy được xác định bởi tiêu chuẩn. Nếu bạn muốn vòng lặp, thì bạn phải nói với trình biên dịch rằng nó thực sự ảnh hưởng đến chương trình bằng, cho exmaple, làm cho một biến đọc dễ bay hơi – Dan

-3

Tôi nghĩ rằng đó có thể là phiên bản gcc của bạn. Tôi biên soạn prog của bạn theo 4.4.2 và nó hoạt động chính xác như nó cần phải có.

+2

Đây là icc, chứ không phải gcc –

1

Bất kỳ cơ hội nào mã thực tế mà bạn đã xây dựng và chạy đều thiếu dấu chấm phẩy sau while(q.isEmpty())? Điều đó chắc chắn sẽ dẫn đến dòng tiếp theo được gọi là vô hạn.

+0

+1 để nhắc tôi về một cái gì đó tương tự – Chubsdad

+0

1. có dấu chấm phẩy 2. kết quả tương tự với while (q.isEmpty()) {}, hoặc trong khi (q.isEmpty()) {true;} – Samuel

+0

Đối với bất cứ ai: Có phải bỏ phiếu xuống thực sự cần thiết? Tôi đã nhìn thấy một số trường hợp trên SO, nơi mã được đăng không phải là mã chính xác đang được sử dụng và sự cố thực tế đã bị bỏ sót hoặc cố định trong bản dịch. Tôi chỉ muốn giúp đảm bảo rằng đây không phải là kết quả của một lỗi đơn giản nhưng dễ bỏ qua. (Sau khi tất cả, không phải tất cả chúng ta đôi khi lãng phí thời gian trên một lỗi do một cái gì đó giống như một dấu chấm phẩy nhầm lẫn?) – TheUndeadFish

10

Vì vòng lặp while (q.isEmpty()) ; không chứa các câu lệnh nào có thể gây ra tác dụng phụ bên ngoài, toàn bộ vòng lặp đang được tối ưu hóa. Đó là lý do tương tự rằng:

for (int i = 0; i < 10; i++) 
    ; 

có thể được tối ưu hóa ra khỏi sự tồn tại, chừng nào i không volatile (cửa hàng để volatile đối tượng là một phần của hiệu ứng "từ bên ngoài có thể nhìn thấy" của chương trình).

Trong ngôn ngữ C, nó thực sự là một sự tranh luận nổi bật về việc liệu một vòng lặp vô hạn có được phép tối ưu hóa theo cách này (tôi không biết tình hình là gì với C++). Theo như tôi biết, một sự đồng thuận chưa bao giờ đạt được về vấn đề này - những người thông minh và hiểu biết đã lấy cả hai mặt.

+0

Điều này có thể là lý do. Mã sau hoạt động chính xác: static int A = 0; while (q.isEmpty()) {A = A + 1;} Nhưng không phải như sau: static int A = 1; while (q.isEmpty()) {A;} – Samuel

+1

Thú vị. Một vòng lặp vô hạn không làm gì cả - cơ hội tối ưu hóa tốt đẹp (và có thể hiểu tại sao một người nào đó có thể không đồng ý). – Suma

+0

Một điều nữa: nếu trong khi (true) được sử dụng trong khi (q.isEmpty()), mã hoạt động chính xác. – Samuel

1

Một chút sang một bên, phiên bản icc này thực hiện những gì bạn muốn. Nghĩa là, nó không bao giờ gọi doSomething().

[9:41am][[email protected] /tmp] icc --version 
icc (ICC) 11.0 20081105 
1

C++ tiêu chuẩn cho phép vòng lặp mà không tác dụng phụ phải được loại bỏ, ngay cả khi họ không chấm dứt:

Nó thường được cảm thấy rằng nó là quan trọng để cho phép việc chuyển đổi của tiềm năng các vòng không kết thúc (ví dụ: bằng cách hợp nhất hai vòng lặp lại cùng một tập hợp vô hạn hoặc bằng cách loại bỏ vòng lặp không có tác dụng), ngay cả khi có thể không được biện minh trong trường hợp trong đó vòng đầu tiên không bao giờ chấm dứt. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2429.htm

Xem các cuộc thảo luận ở đây: http://blog.regehr.org/archives/161

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