2016-08-06 30 views
5

Tôi đã kiểm tra các câu hỏi tương tự trên trang web, nhưng tôi không thể tìm thấy bất kỳ điều gì phù hợp với kịch bản của tôi ở đây. Đây là mã tôi đang cố gắng chạy (yêu cầu C++ 14):Tại sao tối ưu hóa -O2 hoặc cao hơn trong clang phá vỡ mã này?

#include <iostream> 
#include <chrono> 
#include <thread> 
using namespace std; 

class countdownTimer { 
public: 
    using duration_t = chrono::high_resolution_clock::duration; 

    countdownTimer(duration_t duration) : duration{ duration }, paused{ true } {} 

    countdownTimer(const countdownTimer&)    = default; 
    countdownTimer(countdownTimer&&)     = default; 
    countdownTimer& operator=(countdownTimer&&)   = default; 
    countdownTimer& operator=(const countdownTimer&) = default; 

    void start() noexcept { 
     if (started) return; 
     startTime = chrono::high_resolution_clock::now(); 
     endTime = startTime + duration; 
     started = true; 
     paused = false; 
    } 

    void pause() noexcept { 
     if (paused || !started) return; 
     pauseBegin = chrono::high_resolution_clock::now(); 
     paused = true; 
    } 

    void resume() noexcept { 
     if (!paused || !started) return; 
     auto pauseDuration = chrono::high_resolution_clock::now() - pauseBegin; 
     startTime += pauseDuration; 
     endTime += pauseDuration; 
     paused = false; 
    } 

    double remainingSeconds() const noexcept { 
     auto ret = double{ 0.0 }; 
     if (!started) ret = chrono::duration_cast<chrono::duration<double>>(duration).count(); 
     else if (paused) ret = chrono::duration_cast<chrono::duration<double>>(duration - (pauseBegin - startTime)).count(); 
     else ret = chrono::duration_cast<chrono::duration<double>>(duration - (chrono::high_resolution_clock::now() - startTime)).count(); 
     return (ret < 0.0) ? 0.0 : ret; 
    } 

    duration_t remainingTime() const noexcept { 
     auto ret = duration_t{ 0ms }; 
     if (!started) ret = chrono::duration_cast<duration_t>(duration); 
     else if (paused) ret = chrono::duration_cast<duration_t>(duration - (pauseBegin - startTime)); 
     else ret = chrono::duration_cast<duration_t>(duration - (chrono::high_resolution_clock::now() - startTime)); 
     return (ret < 0ms) ? 0ms : ret; 
    } 

    bool isPaused() const noexcept { return paused; } 

    bool hasFinished() const noexcept { return remainingTime() == 0s; } 

    void reset() noexcept { 
     started = false; 
     paused = true; 
    } 

private: 
    chrono::high_resolution_clock::time_point startTime; 
    chrono::high_resolution_clock::time_point endTime; 
    chrono::high_resolution_clock::time_point pauseBegin; 
    duration_t duration; 
    bool paused; 
    bool started; 
}; 

int main() { 
    countdownTimer timer(10s); 
    timer.start(); 

    while (!timer.hasFinished()) { 
     cout << timer.remainingSeconds() << endl; 
     this_thread::sleep_for(1s); 
    } 
} 

Đây là lớp đếm ngược đơn giản mà tôi đã viết cho một trong các dự án của mình. Mã khách hàng trong main() là khá tự giải thích, nó sẽ xuất ra một đếm ngược từ 10 đến 0, và sau đó thoát khỏi chương trình. Với không tối ưu hóa hoặc -O/-O1, nó không chính xác rằng:

10 
8.99495 
7.98992 
6.9849 
5.97981 
4.9748 
3.96973 
2.9687 
1.9677 
0.966752 
Program ended with exit code: 0 

Nhưng nếu tôi đẩy mạnh công tác tối ưu hóa để> = -O2, chương trình chỉ giữ xuất ra 10, và chạy mãi mãi. Đếm ngược chỉ đơn giản là không hoạt động, nó bị mắc kẹt ở giá trị bắt đầu.

Tôi đang sử dụng Xcode mới nhất trên OS X. clang --version nói Apple LLVM version 7.3.0 (clang-703.0.31).

Phần lạ là mã của tôi không chứa bất kỳ vòng lặp tự viết nào, hành vi không xác định hoặc bất kỳ thứ gì giống như vậy, nó chỉ là các cuộc gọi thư viện chuẩn, vì vậy rất kỳ lạ là tối ưu hóa nó.

Bất kỳ ý tưởng nào?

PS: Tôi chưa thử trên các trình biên dịch khác, nhưng tôi sắp sửa. Tôi sẽ cập nhật câu hỏi với những kết quả đó.

+1

Mức tối ưu hóa không được thực sự phá vỡ hành vi của mã của bạn trừ khi bạn giới thiệu hành vi không xác định ở đâu đó. –

+0

Có, nhưng tôi không thực sự thấy nó có thể ở đâu. Nếu đó là, nó phải là một cái gì đó rất tinh tế mà tôi không nhận thấy. – adam10603

+2

Lưu ý đối với những người cố gắng tạo lại vấn đề: mã này cần phải được biên dịch bằng '-std = C++ 14' hoặc cao hơn. – zwol

Trả lời

6

Lỗi là trong constructor của bạn:

countdownTimer(duration_t duration) 
: duration{ duration }, paused{ true } {} 

Bạn f orgot để khởi tạo started. Điều này kích hoạt hành vi không xác định khi bạn gọi start().

Không có phiên bản của vang mà tôi có quyền truy cập thuận tiện để sẽ chẩn đoán lỗi này, nhưng phiên bản GCC 5 và 6 (trên Linux - Tôi không có GCC trên máy Mac của tôi nữa) sẽ:

$ g++ -O2 -Wall -Wextra -std=c++14 test.cc 
test.cc: In function ‘int main()’: 
test.cc:18:13: warning: ‘*((void*)& timer +33)’ is used uninitialized in this function [-Wuninitialized] 
     if (started) return; 
      ^~~~~~~ 
test.cc:74:20: note: ‘*((void*)& timer +33)’ was declared here 
    countdownTimer timer(10s); 
        ^~~~~ 

(bản sao của tôi của Xcode có vẻ là một chút trong ngày, với Apple LLVM version 7.0.2 (clang-700.1.81); nó không thay đổi hành vi của các chương trình tại -O2 có thể là kêu vang của bạn sẽ chẩn đoán lỗi này nếu bạn bật cảnh báo)

..

(Tôi đã nộp bug report with GCC về IR gobbledyg ook trong chẩn đoán.)

+0

Ah ... Tôi không bận tâm để khởi tạo nó, vì tôi gán một giá trị cho nó trong 'start()', nhưng tôi thực sự đã chèn kiểm tra lúc bắt đầu tại 'start()' sau đó, và tôi đã không nghĩ lại điều đó. – adam10603

+0

Tại sao điều này không gây ra bất kỳ sự cố nào ở mức tối ưu hóa thấp hơn? – adam10603

+0

Bất kỳ thông tin tối ưu hóa nào được hiển thị không được bật ở mức tối ưu hóa thấp hơn. Trả lời chi tiết hơn sẽ yêu cầu đào qua các bãi gỡ lỗi ở giai đoạn trung gian, và tôi không biết làm thế nào để làm điều đó với tiếng kêu (và tôi cũng không có phiên bản clang đúng để làm điều đó). – zwol

9

bool started không được khởi tạo. Nếu bạn khởi tạo nó để false, nó hoạt động với -O2:

live example

Bạn có thể tìm thấy các lỗi như thế này bằng cách sử dụng hành vi Undefined khử trùng:

$ g++ -std=c++14 -O2 -g -fsanitize=undefined -fno-omit-frame-pointer main.cpp && ./a.out 

main.cpp:18:9: runtime error: load of value 106, which is not a valid value for type 'bool' 
Các vấn đề liên quan