Impatto dei loop di lunghezza variabile sugli shader GPU


9

È popolare rendere il contenuto procedurale all'interno della GPU, ad esempio nel demoscene (disegnare un singolo quad per riempire lo schermo e consentire alla GPU di calcolare i pixel).

La marcia del raggio è popolare:

inserisci qui la descrizione dell'immagine

Ciò significa che la GPU sta eseguendo un numero sconosciuto di iterazioni di loop per pixel (anche se puoi avere un limite superiore come maxIterations).

In che modo avere un loop di lunghezza variabile influisce sulle prestazioni dello shader?

Immagina il semplice psuedocode che marcia i raggi:

t = 0.f;
while(t < maxDist) {
    p = rayStart + rayDir * t;
    d = DistanceFunc(p);
    t += d;
    if(d < epsilon) {
       ... emit p
       return;
    }
}

In che modo sono interessate le varie famiglie di GPU tradizionali (Nvidia, ATI, PowerVR, Mali, Intel, ecc.)? Shader di vertici, ma shader di frammenti in particolare?

Come può essere ottimizzato?


Sfortunatamente, questa domanda è troppo difficile per rispondere correttamente qui. Sebbene una risposta abbia già dato punti a una fonte del genere che vale la pena leggere (comporta la ramificazione dinamica). +1 per il "topic" ..
teodron,

1
@teodron non essere disfattista! Speravo che qualcuno dicesse che sulle schede NVidia i pixel dello schermo in blocchi 8x8 saranno tutti iterati più in profondità dei bisogni più profondi e che i blocchi di 8x8 pixel possano essere eseguiti in qualsiasi ordine o qualcosa del genere; non è vero, è solo il tipo di saggezza che spero che le persone possano condividere. I collegamenti su Larrabee, hmm, sono piuttosto indiretti.
Will

Non sembra che stia discutendo di Larrabee, ma il ragazzo di Stanford ha tenuto lo stesso discorso due anni dopo, nel 2010 ( puoi vederlo qui ). Dalle sue figure, considerando un ciclo while, non ho capito se i pixel che "terminano" prima i loro calcoli compensano qualsiasi prestazione. In CUDA, i thread attendono una barriera. In analogia, cosa succede con i thread shader?
teodron,

@teodron sì, ho preso la mia comprensione di CUDA e mi sono applicato alle GPU; Sono sicuro che sono a passo d'uomo, ma mi piacerebbe che qualcuno fosse ben informato; in ogni caso, di qui correlate qualcosa williamedwardscoder.tumblr.com/post/26628848007/rod-marching
Will

Risposte:


8

Al GDC 2012 si è tenuto un bel discorso sul ray-marking nel campo della distanza della GPU (e altri argomenti): http://directtovideo.wordpress.com/2012/03/15/get-my-slides-from-gdc2012/

Per quanto riguarda le prestazioni, le ultime schede grafiche (classe DX11) eseguono shader su unità SIMD che eseguono "thread" 32 (NVIDIA) o 64 (AMD) in modalità blocco. Questi gruppi sono variamente noti come orditi o fronti d'onda. Per i pixel shader, ogni thread equivale a un pixel, quindi mi aspetto che l'unità SIMD stia elaborando insieme un blocco di pixel 8x4 (NVIDIA) o 8x8 (AMD). La diramazione e il controllo del flusso vengono eseguiti per fronte d'onda, quindi tutti i thread in un fronte d'onda devono eseguire il ciclo tante volte quanto il singolo pixel più profondo all'interno di quel fronte d'onda. Le maschere di corsia SIMD disattivano l'esecuzione per i pixel che sono già terminati, ma devono comunque procedere silenziosamente con il controllo di flusso complessivo del fronte d'onda. Ciò significa, ovviamente, che il sistema è molto più efficiente quando la ramificazione è coerente,

Nella mia esperienza, l'overhead del ramo è ancora piuttosto elevato anche se tutti i thread nel ramo del fronte d'onda sono allo stesso modo. In alcuni casi ho visto miglioramenti delle prestazioni srotolando il loop per ammortizzare alcune delle spese generali del ramo. Tuttavia, dipende da quanto lavoro stai facendo in ogni iterazione del ciclo, ovviamente. Se il corpo del loop contiene abbastanza "roba", lo srotolamento non sarà una vittoria.



0

Per quanto riguarda il branching dinamico, una nota aggiuntiva (può essere ovvia, ma vale comunque la pena notare per alcune persone): può influire gravemente sulle prestazioni dei loop non srotolati (ovviamente non è possibile srotolare un loop se c'è un numero non costante di iterazioni) .


-4

int s = 0;

ora per (int k = 1; k <= n; k ++) {s + = k;} è uguale a s = n * (n + 1) / 2

quindi non è vero in generale: D


1
Potresti essere molto sottovalutato perché nessuno è abbastanza sicuro di ciò che stai cercando di trasmettere qui o di ciò che ha a che fare con la domanda.
doppelgreener,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.