2013-04-22 13 views
7

Prima di tutto: so che molti bug di ottimizzazione sono dovuti a errori di programmazione o dipendono da fatti che possono cambiare a seconda delle impostazioni di ottimizzazione (valori in virgola mobile, problemi di multithreading, ...).Errore di programmazione o errore di programmazione?

Tuttavia, ho riscontrato un errore molto difficile da trovare e sono un po 'insicuro se esiste un modo per evitare che questo tipo di errore si verifichi senza disattivare l'ottimizzazione. Mi sto perdendo qualcosa? Questo potrebbe davvero essere un bug di ottimizzazione? Ecco un esempio semplificato:

struct Data { 
    int a; 
    int b; 
    double c; 
}; 

struct Test { 
    void optimizeMe(); 

    Data m_data; 
}; 

void Test::optimizeMe() { 
    Data * pData; // Note that this pointer is not initialized! 

    bool first = true; 

    for (int i = 0; i < 3; ++i) { 
    if (first) { 
     first = false; 

     pData = &m_data; 

     pData->a = i * 10; 
     pData->b = i * pData->a; 
     pData->c = pData->b/2; 
    } else { 
     pData->a = ++i; 
    } // end if 
    } // end for 
}; 

int main(int argc, char *argv[]) { 
    Test test; 
    test.optimizeMe(); 
    return 0; 
} 

Il vero programma ovviamente ha molto altro da fare. Ma tutto si riduce al fatto che invece di accedere direttamente a m_data, viene utilizzato un puntatore (precedentemente unitializzato). Non appena aggiungo sufficienti dichiarazioni alla if (first) -parte, l'ottimizzatore sembra cambiare il codice per qualcosa in queste righe:

if (first) { 
    first = false; 

    // pData-assignment has been removed! 

    m_data.a = i * 10; 
    m_data.b = i * m_data.a; 
    m_data.c = m_data.b/m_data.a; 
} else { 
    pData->a = ++i; // This will crash - pData is not set yet. 
} // end if 

Come si può vedere, si sostituisce il superfluo puntatore dereference con una scrittura diretta al membro struct. Tuttavia non lo fa nell'else -branch. Rimuove anche l'assegnazione pData. Dato che il puntatore è ora ancora unitializzato, il programma si bloccherà nell'else -branch.

naturalmente ci sono diverse cose che potrebbero essere migliorati qui, così si potrebbe dare la colpa al programmatore:

  • dimenticare il puntatore e fare ciò che l'ottimizzatore fa - usare m_data direttamente.
  • Inizializza pData su nullptr - in questo modo l'ottimizzatore sa che il else -branch avrà esito negativo se il puntatore non viene mai assegnato. Almeno sembra risolvere il problema nel mio ambiente di test.
  • Sposta l'assegnazione del puntatore davanti al ciclo (inizializzando in modo efficace pData con &m_data, che potrebbe anche essere un riferimento anziché un puntatore (per buona misura). Ciò ha senso in quanto pData è necessario in tutti i casi quindi non c'è motivo per fare questo all'interno del ciclo

il codice è ovviamente puzzolente, a dir poco, e non sto cercando di "colpa" l'ottimizzatore per fare questo, ma mi sto chiedendo:.. cosa sono Il programma potrebbe essere brutto, ma è un codice valido ...

Devo aggiungere che sto usando VS2012 con C + +/CLI e v110_xp-Toolset. L'ottimizzazione è impostata su/O2. Tieni inoltre presente che se vuoi davvero riprodurre il problema (non è proprio questo il punto di questa domanda), devi giocare con la complessità del programma. Questo è un esempio molto semplificato e l'ottimizzatore a volte non rimuove l'assegnazione del puntatore. Nascondere &m_data dietro una funzione sembra "aiutare".

EDIT:

D: Come faccio a sapere che il compilatore è l'ottimizzazione a qualcosa come l'esempio fornito?

A: Io non sono molto bravo a leggere assembler, ho guardato comunque e ho fatto 3 osservazioni che mi fanno credere che si sta comportando in questo modo:

  1. Appena calci di ottimizzazione in (l'aggiunta di più assegnazioni di solito fa il trucco) l'assegnazione del puntatore non ha alcuna istruzione associata all'assemblatore. Inoltre non è stato spostato fino alla dichiarazione, quindi è davvero lasciato non inizializzato sembra (almeno per me).
  2. Nei casi in cui il programma si blocca, il debugger salta l'istruzione di assegnazione. Nei casi in cui il programma viene eseguito senza problemi, il debugger si ferma lì.
  3. Se guardo il contenuto del pData e il contenuto del m_data durante il debug, mostra chiaramente che tutte le assegnazioni del if -branch hanno effetto sui m_data e m_data riceve i valori corretti. Il puntatore stesso continua a puntare allo stesso valore non inizializzato che aveva fin dall'inizio. Pertanto, devo supporre che in effetti non stia usando il puntatore per effettuare i compiti.

Q: Devo fare qualcosa con i (Loop srotolamento)?

R: No, il vero programma utilizza effettivamente fare {...} while() per ciclare su uno SQL SELECT-gruppo di risultati in modo che il conteggio di iterazione è completamente runtime specifico e non può essere predeterminato dal compilatore.

+0

Ridurre questo ad un [SSCCE] (http://sscce.org). – djechlin

+7

@djechlin: i bug interessati dall'ottimizzazione potrebbero essere difficili da ridurre a campioni di codice ridotto. La domanda afferma esplicitamente che questo è già un esempio semplificato. –

+0

non è ancora in grado di spiegare chiaramente un'idea, ma funziona allo stesso modo se si crea una classe invece se struct? – evilruff

risposta

5

Sicuramente sembra un bug per me. Va bene che l'ottimizzatore elimini il reindirizzamento non necessario, ma non dovrebbe eliminare l'assegnazione a pData.

Ovviamente, è possibile aggirare il problema assegnando a pData prima del ciclo (almeno in questo semplice esempio). Considero che il problema nel tuo codice attuale non è così facilmente risolto.

+1

In realtà la rimozione del puntatore nel suo complesso è stata la soluzione nel codice reale. Il codice ha più di 15 anni e molti bug come questi sono facili da risolvere perché il codice ha semplicemente bisogno di un semplice refactoring. Trovare il bug è una storia completamente diversa. – Excelcius

+0

Dato che sono abbastanza sicuro ora che questo è stato un errore del compilatore, accetterò questa come risposta. Non appena avrò depositato un bug report con microsoft, aggiungerò il link alla domanda, quindi sarà disponibile un esempio riproducibile reale. Penso che sia più una cosa C++/CLI, però, il normale compilatore non si è sbagliato. – Excelcius

1

Anche io voto per un bug di ottimizzazione se è davvero riproducibile in questo esempio. Per annullare l'ottimizzazione, provare a dichiarare pData come volatile.

Problemi correlati