2012-05-15 14 views
6

maggior parte del tempo un ramo è richiesto in un programma CUDA o OpenCL, come:CUDA/openCL; rami riscrittura come senza ramificazioni espressione

for (int i=0; i<width; i++) 
{ 
    if(i % threadIdx.x == 0) 
    quantity += i*i; 
} 

il codice può sempre (o almeno la maggior parte del tempo) essere riscritta in stile non ramificati:

for (int i=0; i<width; i++) 
{ 
    quantity += i*i* (i % threadIdx.x != 0); 
} 

il compromesso sembra essere sia in esecuzione in un singolo slot ordito contro facendo più calcoli su tutti i fili (nel secondo caso, la somma viene eseguita sempre, solo che a volte il valore è zero)

Supponendo che le operazioni di ramificazione occuperanno più intervalli di curvatura per ogni ramo possibile, ci si aspetterebbe che il secondo sia costantemente migliore del primo, Ora la mia domanda è; Posso fare affidamento sul compilatore per ottimizzare 1) in 2) ogni volta che ha senso, o non esiste un criterio ampiamente applicabile, il che implica che non si può decidere in generale quale sia il migliore senza provare e profilare?

+0

Che ordine è larghezza? Se sai che la larghezza è abbastanza grande, non dovresti iterare attraverso un ciclo for per farlo, poiché sai quali valori utilizzerai. 'Mentre (i 3Pi

risposta

0

Non ho molti ricordi su CUDA, ma perché non parallelizzi il tuo ciclo? Dovresti usare le operazioni atomiche [1] per aggiungere il tuo calcolo. Spero che questo ti possa aiutare! Scusa se non è il caso.

  1. Atomic Operazioni: http://supercomputingblog.com/cuda/cuda-tutorial-4-atomic-operations/
+0

Questo è all'interno di un kernel - ogni thread sta eseguendo il ciclo completo. Il commento non ha senso. –

1

Nella mia esperienza - è totalmente fino al compilatore-scrittori di ottimizzare questo tipo di casi limite.

Quindi, posso pensare a casi in cui 1) non può essere impostato su 2)? Eccone uno: ho scritto kernel dove è stato più efficiente eseguire certi pezzi dei calcoli ogni 10 thread o qualcosa del genere, nel qual caso tale ottimizzazione non può essere dedotta, anche se esiste un'operazione matematica (una divisione e una sottrazione) che può produrre lo stesso risultato indipendentemente dal condizionale rispetto a "correre su tutti, ma produrre risultati zero".

Tuttavia, anche se il controllo di threadId == 0 è uno scenario abbastanza comune, non ho idea se sia effettivamente ottimizzato per. Scommetto che dipende dall'implementazione E anche dal dispositivo stesso (CPU vs GPU).

Dovrete provarlo per scoprire veramente cosa funziona meglio, non solo per la ragione sopra, ma anche perché lo scheduler di lavoro potrebbe comportarsi in modo diverso in base a quanto è costoso pianificare/avviare/interrompere un set di thread al contrario di averli tutti eseguiti (e la maggior parte fornisce un risultato di zero/identità).

Spero che questo aiuti!

+0

quindi, in base alla tua esperienza, puoi formulare alcune raccomandazioni riguardo se dovrei sempre provare a scrivere codice nello stile 2, assumendo lo scenario peggiore? o può avere conseguenze non intenzionali? – lurscher

+0

Non posso giustificare l'uno sull'altro in tutti i casi - che è il mio punto. Probabilmente userò 1) se stavo facendo qualcosa come una riduzione di qualcosa gestibile su un dispositivo CPU ma 2) se fossi su una GPU a causa del costo della ramificazione su hardware precedente - se quello scenario era valido. I fattori da considerare sono: tipo di dispositivo, quanto il calcolo non parallelo è possibile, è possibile dividere il calcolo in più kernel (forse la riduzione) e infine se l'overhead di ramificazione su tutto l'hardware per il tipo di dispositivo selezionato è accettabile. Ma IMO, la sperimentazione sarebbe sempre raccomandata. – Ani

+0

per essere chiari, sto parlando nel caso specifico dei dispositivi GPU, ovviamente non c'è alcun guadagno con la CPU perché c'è un sacco di previsione delle derivazioni e pipeline che aiuta con la latenza che si nasconde – lurscher

3

Le operazioni di modulo sono ragionevolmente costose: sono ragionevolmente sicuro che l'aggiunta del modulo richiederebbe più tempo di una semplice istruzione che esegue solo 1 thread. La tua singola dichiarazione di diramazione, una if senza else, bloccherà solo gli altri thread mentre quella se la statistica è in esecuzione. Poiché gpus è ottimizzato per il cambio di contesto molto veloce, ci dovrebbe essere un costo molto basso per questo.

Si sconsiglia tuttavia l'uso di istruzioni di ramificazione lunghe: un eccessivo calcolo seriale sulla GPU (ovvero un thread che esegue tutto il lavoro) annulla il vantaggio del parallelismo.

+0

Inoltre, basta controllare la guida CUDA Best Programming, rendere il codice facile da usare per Branch Prediction è una priorità bassa. Ci sono cose più importanti da ottimizzare in generale. – 3Pi