2010-05-26 16 views
5

Ho questo ciclo scritto in C++, che compilato con MSVC2010 richiede molto tempo per essere eseguito. (300ms)prestazioni strane in C++ (VC 2010)

for (int i=0; i<h; i++) { 
    for (int j=0; j<w; j++) { 
     if (buf[i*w+j] > 0) { 
      const int sy = max(0, i - hr); 
      const int ey = min(h, i + hr + 1); 
      const int sx = max(0, j - hr); 
      const int ex = min(w, j + hr + 1); 
      float val = 0; 
      for (int k=sy; k < ey; k++) { 
       for (int m=sx; m < ex; m++) { 
        val += original[k*w + m] * ds[k - i + hr][m - j + hr]; 
       } 
      } 
      heat_map[i*w + j] = val; 
     } 
    } 
} 

Sembrava un po 'strano per me, così ho fatto alcuni test poi cambiato pochi bit di inline di montaggio: (in particolare, il codice che riassume "val")

for (int i=0; i<h; i++) { 
    for (int j=0; j<w; j++) { 
     if (buf[i*w+j] > 0) { 
      const int sy = max(0, i - hr); 
      const int ey = min(h, i + hr + 1); 
      const int sx = max(0, j - hr); 
      const int ex = min(w, j + hr + 1); 
      __asm { 
       fldz 
      } 
      for (int k=sy; k < ey; k++) { 
       for (int m=sx; m < ex; m++) { 
        float val = original[k*w + m] * ds[k - i + hr][m - j + hr]; 
        __asm { 
         fld val 
         fadd 
        } 
       } 
      } 
      float val1; 
      __asm { 
       fstp val1 
      } 
      heat_map[i*w + j] = val1; 
     } 
    } 
} 

Ora funziona in metà tempo, 150ms. Fa esattamente la stessa cosa, ma perché è due volte più veloce? In entrambi i casi è stato eseguito in modalità Release con ottimizzazioni attive. Sto facendo qualcosa di sbagliato nel mio codice C++ originale?

+3

Hai provato a confrontare il codice assembly generato in entrambi i casi ... –

risposta

5

Vi suggerisco di provare diversi modelli di calcolo in virgola mobile supportati dal compilatore - precise, strict o fast (vedi /fp opzione) - con il vostro codice originale prima di effettuare qualsiasi conclusione. Sospetto che il tuo codice originale sia stato compilato con un modello a virgola mobile eccessivamente restrittivo (non seguito dal tuo assembly nella seconda versione del codice), motivo per cui l'originale è molto più lento.

In altre parole, se il modello originale fosse davvero troppo restrittivo, allora si stava semplicemente confrontando le mele con le arance. Le due versioni non hanno fatto la stessa cosa, anche se a prima vista potrebbe sembrare così.

Nota, ad esempio, che nella prima versione del codice la somma intermedia viene accumulata in un valore float. Se è stato compilato con il modello precise, i risultati intermedi dovrebbero essere arrotondati alla precisione del tipo float, anche se la variabile val è stata ottimizzata e al suo posto è stato utilizzato il registro FPU interno. Nel tuo codice assembly non ti preoccupare di arrotondare il risultato accumulato, che è ciò che avrebbe potuto contribuire alla sua migliore performance.

Suggerirei di compilare entrambe le versioni del codice nella modalità /fp:fast e vedere come le loro prestazioni si confrontano in quel caso.

+0

Grazie!Ho eseguito il mio codice originale in modalità veloce e ora gira in 80 ms, mentre la seconda versione gira ancora a 150 ms in modalità veloce, quindi suppongo che il compilatore sappia ancora meglio :) Ho trovato questi # pragma's per MSVC per alternare la precisione galleggiante per funzione (non funziona funzioni all'interno): #pragma float_control (preciso, spento, spinta) ... codice qui ... #pragma float_control (pop) Ma più precisamente: http://msdn.microsoft.com/en-us/library/45ec64h6(VS.80).aspx – raicuandi

3

Un paio di cose da verificare:

  • è necessario verificare che in realtà è è lo stesso codice. Come in, le istruzioni di assemblaggio in linea sono esattamente le stesse di quelle generate dal compilatore? Vedo tre potenziali potenziali (potenziale perché potrebbero essere ottimizzati). La prima è l'impostazione iniziale di val a zero, la seconda è la variabile extra val1 (improbabile dal momento che molto probabilmente cambierà solo la sottrazione costante del puntatore dello stack), la terza è che la versione dell'assieme non può mettere i risultati intermedi di nuovo in val.

  • È necessario assicurarsi che lo spazio del campione sia ampio. Non hai menzionato se avevi fatto una sola esecuzione di ogni versione o un centinaio di esecuzioni, ma più corre, meglio è, in modo da rimuovere l'effetto di "rumore" nelle tue statistiche.

  • Una misurazione ancora migliore sarebbe il tempo della CPU anziché il tempo trascorso. Il tempo trascorso è soggetto a cambiamenti ambientali (come il tuo antivirus o uno dei tuoi servizi che decide di fare qualcosa nel momento in cui stai effettuando il test). Il grande spazio campione allevierà, ma non necessariamente risolverà, questo.

Problemi correlati