2016-08-06 51 views
5

Ich überprüfte ähnliche Fragen auf der Website, aber ich konnte nichts finden, das meinem Szenario hier entspricht. Dies ist der Code I (erfordert C++ 14) laufen bin versucht:Warum bricht die Optimierung von o2 oder höher beim clang diesen Code?

#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); 
    } 
} 

Es ist eine einfache Countdown-Timer-Klasse, die ich für eine meiner Projekte geschrieben. Der Client-Code in main() ist ziemlich selbsterklärend, es sollte einen Countdown von 10 bis 0 ausgeben und dann das Programm beenden. Ohne Optimierung oder -O/-O1, tut es genau das:

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 

Aber wenn ich die Optimierung> = -O2 intensivieren, das Programm hält nur 10 ausgegeben wird, und läuft für immer. Der Countdown funktioniert einfach nicht, er bleibt beim Startwert hängen.

Ich verwende den neuesten Xcode auf OS X. clang --version sagt Apple LLVM version 7.3.0 (clang-703.0.31). Der seltsame Teil ist, dass mein Code keine seltsamen selbstgeschriebenen Schleifen, undefiniertes Verhalten oder ähnliches enthält, es sind so ziemlich nur Standardbibliotheksaufrufe, es ist also sehr seltsam, dass die Optimierung es bricht.

Irgendwelche Ideen?

PS: Ich habe es nicht auf anderen Compilern versucht, aber ich bin dabei. Ich werde die Frage mit diesen Ergebnissen aktualisieren.

+1

Optimierungsstufen sollten das Verhalten des Codes nicht beeinträchtigen, es sei denn, Sie haben irgendwo undefiniertes Verhalten eingeführt. –

+0

Ja, aber ich sehe nicht wirklich, wo das sein könnte. Wenn das so ist, muss es etwas sehr Subtiles sein, das ich nicht wahrnehme. – adam10603

+2

Hinweis für Personen, die versuchen, das Problem zu reproduzieren: Dieser Code muss mit '-std = C++ 14' oder höher kompiliert werden. – zwol

Antwort

6

Der Fehler in Ihrem Konstruktor ist:

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

Sie f orgot zu initialisieren started. Dies löst ein undefiniertes Verhalten aus, wenn Sie start() aufrufen.

Keine Version von Klappern, die ich bequem Zugriff habe diese Fehler zu diagnostizieren, aber GCC-Versionen 5 und 6 (unter Linux - ich habe nicht GCC auf meinem Mac mehr) wird:

$ 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); 
        ^~~~~ 

(Mein Exemplar von Xcode scheint ein bisschen veraltet zu sein, mit Apple LLVM version 7.0.2 (clang-700.1.81), es ist nicht das Verhalten des Programms bei -O2 ändert es ist möglich, dass Ihre Klirren diesen Fehler diagnostizieren, wenn Sie auf die Warnungen eingeschaltet)

..

(Ich habe eine bug report with GCC über das IR-Gobblead eingereicht ook in der Diagnose.)

+0

Ah ... Ich habe mich nicht darum gekümmert, es zu initialisieren, da ich ihm einen Wert in 'start()' zugewiesen habe, aber ich habe den Check am Anfang bei 'start()' hinterher eingefügt, und das tat ich nicht denke das nochmal. – adam10603

+0

Warum verursacht das bei einem niedrigeren Optimierungslevel keine Probleme? – adam10603

+0

Unabhängig davon, welcher Optimierungsprozess ausgeführt wurde, das Problem wurde bei niedrigeren Optimierungsstufen nicht aktiviert. Beantworten Sie diese Frage genauer, müssten Sie Debugging-Dumps in der Zwischenstufe durchsuchen, und ich weiß nicht, wie ich das mit clang machen soll (und ich habe auch nicht die richtige Version von clang, um das zu tun). – zwol

9

bool started ist nicht initialisiert. Wenn Sie es false initialisieren, es funktioniert mit -O2:

live example

Sie Fehler wie diese finden können Sanitizer die undefinierten Verhalten mit:

$ 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'