2016-07-13 11 views
11

Dies ist ein einfaches C++ Programm valarrays:Warum funktioniert die GCC-Optimierung nicht mit Valarrays?

#include <iostream> 
#include <valarray> 

int main() { 
    using ratios_t = std::valarray<float>; 

    ratios_t a{0.5, 1, 2}; 
    const auto& res (ratios_t::value_type(256)/a); 
    for(const auto& r : ratios_t{res}) 
     std::cout << r << " " << std::endl; 
    return 0; 
} 

Wenn ich es so kompilieren und ausführen:

g++ -O0 main.cpp && ./a.out 

Der Ausgang wird wie erwartet:

512 256 128 

Wenn jedoch Ich kompiliere und laufe es so:

g++ -O3 main.cpp && ./a.out 

Die Ausgabe lautet:

0 0 0 

Gleiche passiert, wenn ich -O1 Optimierungsparameter verwenden.

GCC-Version (neueste in Archlinux):

$ g++ --version 
g++ (GCC) 6.1.1 20160707 

Wenn ich jedoch mit Klirren versuchen, beide

clang++ -std=gnu++14 -O0 main.cpp && ./a.out 

und

clang++ -std=gnu++14 -O3 main.cpp && ./a.out 

die gleiche korrekte Ergebnis produzieren:

512 256 128 

Clang Version ist:

$ clang++ --version 
clang version 3.8.0 (tags/RELEASE_380/final) 

ich auch mit GCC 4.9.2 auf Debian versucht haben, in denen ausführbare Datei das richtige Ergebnis erzeugt.

Ist das ein möglicher Fehler in GCC oder mache ich etwas falsch? Kann das jemand reproduzieren?

EDIT: Ich habe es geschafft, das Problem auch auf Homebrew-Version von GCC 6 auf Mac OS zu reproduzieren.

+0

Mit http://melpon.org/wandbox es die Verhaltensänderungen von 4.9.3 auf 5.1 erscheint. – NathanOliver

+0

Leider habe ich es in meiner Codebase auch geschafft, ähnliches Problem (aber mit uint32_t) sogar auf GCC 4.9.3 zu reproduzieren, allerdings funktioniert es bei minimalem Beispiel. Ich untersuche ... – DoDo

Antwort

6

valarray und auto nicht gut mischen.

Dieses ein temporäres Objekt erstellt, gilt dann operator/ es:

const auto& res (ratios_t::value_type(256)/a); 

Die libstdC++ valarray verwendet Ausdrucksvorlagen, so dass operator/ ein leichtes Objekt zurückgibt, das auf die ursprünglichen Argumente verweist und wertet sie träge. Sie verwenden const auto&, was bewirkt, dass die Ausdrucksvorlage an die Referenz gebunden ist, verlängert jedoch nicht die Lebensdauer des temporären Objekts, auf das die Expressionsvorlage verweist. Wenn die Evaluierung erfolgt, ist das temporäre Objekt nicht mehr verfügbar wiederverwendet.

Es wird gut funktionieren, wenn Sie tun:

ratios_t res = ratios_t::value_type(256)/a; 
+0

Vielen Dank für die ausführliche Antwort. Ich werde es akzeptieren. Ich möchte nur anmerken, dass selbst wenn ich 'auto res (ratios_t :: value_type (256)/a)' schreibe, bekomme ich immer noch das gleiche Ergebnis mit aktivierten Optimierungen. Ihr Vorschlag funktioniert jedoch (d. H. Nicht mit "auto"). Mit clang und msvc funktionierte der Originalcode korrekt. – DoDo

+0

Ja, weil 'auto' den Typ der Ausdrucksvorlage ableitet, die einen Verweis auf das abgelaufene temporäre enthält. Sie müssen explizit ein 'valarray' erstellen, um die Auswertung zu erzwingen, bevor das temporäre Objekt verschwindet. Wie gesagt, 'auto' und' valarray' mischen sich nicht gut. –

+0

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57997 ist ein sehr ähnliches Problem. –

3

Es ist das Ergebnis unachtsamer Umsetzung operator/ (const T& val, const std::valarray<T>& rhs) (und vermutlich anderer Betreiber über valarrays) lazy evaluation mit:

#include <iostream> 
#include <valarray> 

int main() { 
    using ratios_t = std::valarray<float>; 

    ratios_t a{0.5, 1, 2}; 
    float x = 256; 
    const auto& res (x/a); 
    // x = 512; // <-- uncommenting this line affects the output 
    for(const auto& r : ratios_t{res}) 
     std::cout << r << " "; 
    return 0; 
} 

Mit der "x = 512" Zeile auf Kommentar, ist der Ausgang

512 256 128 

Kommentieren Sie diese Zeile und der Ausgang ändert sich in

1024 512 256 

Da in Ihrem Beispiel das linke Argument des Divisionsoperators ein temporäres ist, ist das Ergebnis undefiniert.

UPDATE

Als Jonathan Wakely richtig pointed out, die lazy-Auswertung basierte Implementierung wird ein Problem, in diesem Beispiel durch den Einsatz von auto.

+0

Vielen Dank für Ihre Antwort. Jonathan war der erste, also habe ich seine Antwort akzeptiert, aber Sie sind auch sehr informativ. Schade, SO lässt dich nicht mehrere Antworten akzeptieren :-). – DoDo

Verwandte Themen