2010-03-06 14 views
6

Relativo al mio other question, ora ho modificato il solutore matrice sparse per utilizzare il metodo SOR (Successive Over-Relaxation). Il codice è ora la seguente:Perché la velocità di questo risolutore SOR dipende dall'input?

void SORSolver::step() { 
    float const omega = 1.0f; 
    float const 
     *b = &d_b(1, 1), 
     *w = &d_w(1, 1), *e = &d_e(1, 1), *s = &d_s(1, 1), *n = &d_n(1, 1), 
     *xw = &d_x(0, 1), *xe = &d_x(2, 1), *xs = &d_x(1, 0), *xn = &d_x(1, 2); 
    float *xc = &d_x(1, 1); 
    for (size_t y = 1; y < d_ny - 1; ++y) { 
     for (size_t x = 1; x < d_nx - 1; ++x) { 
      float diff = *b 
       - *xc 
       - *e * *xe 
       - *s * *xs - *n * *xn 
       - *w * *xw; 
      *xc += omega * diff; 
      ++b; 
      ++w; ++e; ++s; ++n; 
      ++xw; ++xe; ++xs; ++xn; 
      ++xc; 
     } 
     b += 2; 
     w += 2; e += 2; s += 2; n += 2; 
     xw += 2; xe += 2; xs += 2; xn += 2; 
     xc += 2; 
    } 
} 

Ora la cosa strana è: se io aumento omega (il fattore di rilassamento), la velocità di esecuzione inizia a dipendere drammaticamente sui valori all'interno delle varie matrici!

Per omega = 1.0f, il tempo di esecuzione è più o meno costante. Per omega = 1.8, la prima volta, in genere occorrono, per esempio, 5 millisecondi per eseguire questo step() 10 volte, ma questo gradualmente aumenterà fino a circa 100 ms durante la simulazione. Se imposto lo omega = 1.0001f, vedo un leggero aumento dei tempi di esecuzione; più alto è il omega, più veloce sarà il tempo di esecuzione durante la simulazione.

Poiché tutto questo è incorporato nel solutore di fluidi, è difficile trovare un esempio indipendente. Ma ho salvato lo stato iniziale e rieseguito il risolutore in quello stato ogni volta che passo, oltre a risolvere il passo temporale attuale. Per lo stato iniziale era veloce, per le successive fasi temporali più lentamente. Poiché tutto il resto è uguale, ciò dimostra che la velocità di esecuzione di questo codice dipende dai valori in questi sei array.

Questo è riproducibile su Ubuntu con g ++, così come su Windows 7 a 64 bit durante la compilazione per 32-bit con VS2008.

Ho sentito che i valori NaN e Inf possono essere più lenti per i calcoli in virgola mobile, ma non ci sono né NaN né Infs. È possibile che la velocità dei calcoli mobili dipenda altrimenti dai valori dei numeri di input?

+0

Sei sicuro di non misurare altri codepath che dipendono dai valori dei calcoli? – ebo

+0

Se si continuano a riscontrare problemi di prestazioni, considerare l'utilizzo di una GPU per questi calcoli. Le GPU sono ottime per il lavoro a matrice sparsa. – Mark

+0

@ebo: ho usato l'orologio in tempo reale su Linux per misurare solo questa chiamata. Quindi sì, ne sono abbastanza sicuro. – Thomas

risposta

5

La risposta breve alla tua ultima domanda è "sì" - i numeri denormalizzati (molto vicini allo zero) richiedono una gestione speciale e possono essere molto più lenti. La mia ipotesi è che si stanno insinuando nella simulazione col passare del tempo. Vedere questo post SO correlato: Floating Point Math Execution Time

L'impostazione del controllo a virgola mobile per svuotare i denormali a zero dovrebbe occuparsi di cose con un'imitazione trascurabile sulla qualità della simulazione.

+0

I valori denormalizzati stanno davvero entrando in esso; Sto risolvendo la pressione del fluido per ottenere la divergenza ('d_b' sopra) il più vicino possibile allo zero. Quindi questo lo spiega, e tu ancora una volta mi hai insegnato qualcosa. Grazie molto!(La tua confusione riguardo a '1-omega' è probabilmente dovuta al fatto che le righe della matrice sono ridimensionate quindi ci sono' 1's sulla diagonale. '- * xc' non viene da lì, ma in realtà è il termine' -omega * * xc' nella tua riga di codice. Sto ottenendo risposte ragionevoli.) – Thomas

+0

Felice che abbia fatto il trucco. Ho sentito una storia dell'orrore da un collega di recente - poco prima di una grande fiera qualche anno fa, il programma dimostrativo stava lentamente rallentando dopo aver funzionato per qualche minuto. Si è scoperto che un errore nel driver grafico non stava ripristinando correttamente il flag di controllo a virgola mobile, e stavano ottenendo i denormali dalla moltiplicazione ripetuta di un fattore di smorzamento nella simulazione del corpo rigido. Abbastanza semplice da risolvere una volta che conoscevano il problema, ma ci sono voluti un giorno o due di debug per rintracciarlo. Ad ogni modo, ho pensato subito a questo quando ho letto la tua descrizione. – celion

+0

Ho anche rimosso le mie modifiche (errate) su 1-omega per rendere la risposta più chiara ... – celion

0

celion's answer risulta essere quello corretto. Il post si collega alla dice per accendere filo a zero dei valori denormalizzati:

#include <float.h> 
_controlfp(_MCW_DN, _DN_FLUSH); 

Tuttavia, questo è solo per Windows. Utilizzando gcc su Linux, ho fatto la stessa cosa con un soffio di assembly inline:

int mxcsr; 
__asm__("stmxcsr %0" : "=m"(mxcsr) : :); 
mxcsr |= (1 << 15); // set bit 15: flush-to-zero mode 
__asm__("ldmxcsr %0" : : "m"(mxcsr) :); 

Questo è il mio primo assembly x86 mai, in modo che possa probabilmente essere migliorato. Ma fa il trucco per me.

+0

È possibile impostare l'ambiente FP in modo portabile con fenv.h (C99). – Jed

+0

Questo è C++, quindi non funzionerà direttamente. Ma potrebbe essere possibile rubare questa implementazione. Grazie! – Thomas

Problemi correlati