2016-07-13 30 views
11

Questo è un semplice programma in C++ utilizzando valarray:Perché l'ottimizzazione GCC non funziona con 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; 
} 

Se compilo e correre in questo modo:

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

L'uscita è come previsto:

512 256 128 

Tuttavia, se Compilare ed eseguire in questo modo:

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

L'output è:

0 0 0 

Lo stesso accade se uso -O1 parametri di ottimizzazione.

versione GCC è (ultimo di Archlinux):

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

Tuttavia, se provo con clangore, sia

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

e

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

producono lo stesso risultato corretto:

512 256 128 

versione Clang è:

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

Ho anche provato con GCC 4.9.2 su Debian, dove eseguibile produce il risultato corretto.

Si tratta di un possibile errore in GCC o sto facendo qualcosa di sbagliato? Qualcuno può riprodurlo?

EDIT: Sono riuscito a riprodurre il problema anche sulla versione Homebrew di GCC 6 su Mac OS.

+0

Utilizzando http://melpon.org/wandbox sembra che i cambiamenti di comportamento da 4.9.3 a 5.1. – NathanOliver

+0

Sfortunatamente, nella mia base di codice sono anche riuscito a riprodurre un problema simile (ma con uint32_t) anche su GCC 4.9.3, tuttavia funziona quando si inserisce un esempio minimo. Sto indagando ... – DoDo

risposta

6

valarray e auto non mescolare bene.

Questo crea un oggetto temporaneo, poi applica operator/ ad esso:

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

Il libstdC++ valarray utilizza modelli di espressione in modo che operator/ restituisce un oggetto leggero che si riferisce agli argomenti originali e li valuta pigramente. Si utilizza const auto& che fa sì che il modello di espressione sia associato al riferimento, ma non estende la durata del temporaneo a cui fa riferimento il modello di espressione, quindi quando la valutazione avviene il temporaneo è uscito dall'ambito e la sua memoria è stata riutilizzato.

E funziona bene se lo fai:

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

Grazie per la risposta dettagliata. Lo accetterò. Vorrei solo notare che anche se scrivo 'auto res (ratios_t :: value_type (256)/a)', ottengo lo stesso risultato con le ottimizzazioni attivate. Tuttavia, il tuo suggerimento funziona (cioè non usando 'auto'). Con entrambi clang e il codice originale di msvc ha funzionato correttamente. – DoDo

+0

Sì, perché 'auto' deduce il tipo del modello di espressione che contiene un riferimento al temporaneo scaduto. È necessario creare in modo esplicito un 'valarray', per forzare la valutazione a verificarsi prima che il temporaneo scompaia. Come ho detto, 'auto' e' valarray' non si combinano bene. –

+0

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57997 è un problema molto simile. –

3

E 'il risultato di una negligente attuazione operator/ (const T& val, const std::valarray<T>& rhs) (e molto probabilmente altri operatori oltre valarray) utilizzando la valutazione pigra:

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

Con il "x = 512" linea commentata, l'uscita è

512 256 128 

Decommentare quella linea e l'output cambia in

1024 512 256 

Dato che nell'esempio l'argomento sul lato sinistro dell'operatore di divisione è temporaneo, il risultato non è definito.

UPDATE

Come Jonathan Wakely correttamente pointed out, l'implementazione basata pigro valutazione diventa un problema in questo esempio per l'utilizzo di auto.

+0

Grazie per la risposta. Jonathan è stato il primo, quindi ho accettato la sua risposta, ma la tua è anche molto istruttiva. Peccato che SO non ti permetta di accettare più risposte :-). – DoDo

Problemi correlati