In che modo i blocchi / gli orditi / i thread CUDA vengono mappati sui core CUDA?


143

Uso CUDA da alcune settimane, ma ho alcuni dubbi sull'assegnazione di blocchi / orditi / filo. Sto studiando l'architettura da un punto di vista didattico (progetto universitario), quindi raggiungere le massime prestazioni non è una mia preoccupazione.

Prima di tutto, vorrei capire se ho capito bene questi fatti:

  1. Il programmatore scrive un kernel e organizza la sua esecuzione in una griglia di blocchi di thread.

  2. Ogni blocco è assegnato a un multiprocessore di streaming (SM). Una volta assegnato, non può migrare verso un altro SM.

  3. Ogni SM suddivide i propri blocchi in Warps (attualmente con una dimensione massima di 32 thread). Tutti i thread in un ordito vengono eseguiti simultaneamente sulle risorse dell'SM.

  4. L'esecuzione effettiva di un thread viene eseguita dai core CUDA contenuti nell'SM. Non esiste un mapping specifico tra thread e core.

  5. Se un ordito contiene 20 thread, ma attualmente ci sono solo 16 core disponibili, il warp non verrà eseguito.

  6. D'altra parte, se un blocco contiene 48 thread, verrà suddiviso in 2 orditi e verranno eseguiti in parallelo a condizione che sia disponibile memoria sufficiente.

  7. Se un thread inizia su un core, viene bloccato per l'accesso alla memoria o per un'operazione in virgola mobile lunga, la sua esecuzione potrebbe riprendere su un core diverso.

Sono corretti?

Ora ho una GeForce 560 Ti, quindi secondo le specifiche è dotata di 8 SM, ciascuna contenente 48 core CUDA (384 core in totale).

Il mio obiettivo è assicurarsi che ogni nucleo dell'architettura esegua le stesse istruzioni. Supponendo che il mio codice non richiederà più registri di quelli disponibili in ogni SM, ho immaginato approcci diversi:

  1. Creo 8 blocchi di 48 thread ciascuno, in modo che ogni SM abbia 1 blocco da eseguire. In questo caso i 48 thread verranno eseguiti in parallelo nell'SM (sfruttando tutti i 48 core disponibili per loro)?

  2. C'è qualche differenza se lancio 64 blocchi di 6 thread? (Supponendo che saranno mappati uniformemente tra gli SM)

  3. Se "sommergo" la GPU nel lavoro pianificato (creando 1024 blocchi di 1024 thread ciascuno, ad esempio) è ragionevole supporre che tutti i core verranno utilizzati ad un certo punto e eseguirò gli stessi calcoli (supponendo che i thread mai in stallo)?

  4. Esiste un modo per verificare queste situazioni utilizzando il profiler?

  5. C'è qualche riferimento per questa roba? Ho letto la guida alla programmazione CUDA e i capitoli dedicati all'architettura hardware in "Programmazione di processori paralleli massicci" e "Progettazione e sviluppo di applicazioni CUDA"; ma non sono riuscito a ottenere una risposta precisa.


Vorrei aggiungere come commento ciò che è "core CUDA". "CUDA core" o "Execution unit" è un intero intero con pipeline ALU e FPU che esegue un'istruzione aritmetica per ciclo di clock in un thread cuda.
bruziuz,

Risposte:


123

Due dei migliori riferimenti sono

  1. White paper NVIDIA Fermi Compute Architecture
  2. Recensioni GF104

Proverò a rispondere a ciascuna delle tue domande.

Il programmatore divide il lavoro in thread, thread in blocchi di thread e blocchi di thread in griglie. Il distributore del lavoro di calcolo alloca blocchi di thread a Streaming Multiprocessors (SMs). Una volta che un blocco thread viene distribuito a un SM, le risorse per il blocco thread vengono allocate (warp e memoria condivisa) e i thread vengono divisi in gruppi di 32 thread chiamati warps. Una volta allocato un ordito, questo viene chiamato ordito attivo. I due programmatori di ordito selezionano due orditi attivi per ciclo e inviano gli orditi alle unità di esecuzione. Per maggiori dettagli sulle unità di esecuzione e l'invio delle istruzioni vedere 1 p.7-10 e 2 .

4 ' . Esiste una mappatura tra laneid (indice dei thread in un warp) e un core.

5 ' . Se un warp contiene meno di 32 thread, nella maggior parte dei casi verrà eseguito come se avesse 32 thread. Gli orditi possono avere meno di 32 thread attivi per diversi motivi: il numero di thread per blocco non è divisibile per 32, il programma esegue un blocco divergente in modo che i thread che non hanno preso il percorso corrente siano contrassegnati come inattivi o che sia uscito un thread nell'ordito.

6 ' . Un blocco thread verrà diviso in WarpsPerBlock = (ThreadsPerBlock + WarpSize - 1) / WarpSize Non è necessario che gli scheduler di warp selezionino due orditi dallo stesso blocco thread.

7 ' . Un'unità di esecuzione non si fermerà su un'operazione di memoria. Se una risorsa non è disponibile quando un'istruzione è pronta per essere spedita, l'istruzione verrà nuovamente spedita in futuro quando la risorsa sarà disponibile. Gli orditi possono bloccarsi su barriere, operazioni di memoria, operazioni di trama, dipendenze di dati, ... Un ordito bloccato non è ammissibile per essere selezionato dal programmatore di ordito. Su Fermi è utile avere almeno 2 orditi idonei per ciclo in modo che il programmatore degli orditi possa emettere un'istruzione.

Vedi riferimento 2 per le differenze tra GTX480 e GTX560.

Se leggi il materiale di riferimento (pochi minuti) penso che scoprirai che il tuo obiettivo non ha senso. Proverò a rispondere ai tuoi punti.

1 ' . Se avvii il kernel <<< 8, 48 >>> otterrai 8 blocchi ciascuno con 2 orditi da 32 e 16 fili. Non vi è alcuna garanzia che questi 8 blocchi saranno assegnati a SM diversi. Se 2 blocchi sono assegnati a un SM, è possibile che ogni programmatore di warp possa selezionare un warp ed eseguire il warp. Utilizzerai solo 32 dei 48 core.

2 ' . C'è una grande differenza tra 8 blocchi di 48 thread e 64 blocchi di 6 thread. Supponiamo che il tuo kernel non abbia divergenze e che ogni thread esegua 10 istruzioni.

  • 8 blocchi con 48 fili = 16 orditi * 10 istruzioni = 160 istruzioni
  • 64 blocchi con 6 fili = 64 orditi * 10 istruzioni = 640 istruzioni

Per ottenere l'efficienza ottimale, la divisione del lavoro dovrebbe essere in multipli di 32 thread. L'hardware non unirà i thread da diversi orditi.

3 ' . Un GTX560 può avere 8 SM * 8 blocchi = 64 blocchi alla volta o 8 SM * 48 orditi = 512 orditi se il kernel non esaurisce i registri o la memoria condivisa. In qualsiasi momento una parte del lavoro sarà attiva su SM. Ogni SM ha più unità di esecuzione (più dei core CUDA). Quali risorse vengono utilizzate in qualsiasi momento dipende dai programmatori di warp e dal mix di istruzioni dell'applicazione. Se non si eseguono operazioni TEX, le unità TEX saranno inattive. Se non si esegue un'operazione speciale in virgola mobile, le unità SUFU resteranno inattive.

4 ' . Parallel Nsight e lo spettacolo Visual Profiler

un. IPC eseguito

b. IPC rilasciato

c. orditi attivi per ciclo attivo

d. deformazioni ammissibili per ciclo attivo (solo Nsight)

e. motivi di stallo warp (solo Nsight)

f. thread attivi per istruzione eseguita

Il profiler non mostra la percentuale di utilizzo di nessuna delle unità di esecuzione. Per GTX560 una stima approssimativa sarebbe IssuedIPC / MaxIPC. Per MaxIPC supponiamo che GF100 (GTX480) sia 2 GF10x (GTX560) sia 4 ma il target 3 sia un target migliore.


1
La ringrazio per la risposta. Ho letto i riferimenti, ma ci sono alcune cose che non capisco nella tua risposta. Nelle seguenti domande suppongo che stiamo usando un'architettura di Fermi con 48 core (16 core * 3 "gruppi di core"): 1. Hai menzionato una mappatura tra core e laneid. Che tipo di mappatura è? 2. Dai riferimenti ho ottenuto che ciascun "gruppo principale" esegue al massimo un mezzo ordito (16 thread) per ciclo di clock. Quindi, in teoria, se abbiamo 48 thread nello stesso blocco, saranno organizzati in 3 semi-orditi ed eseguiti in parallelo sui 48 core. Ho ragione?
Dedalo,

1
I core CUDA sono il numero di unità FP a precisione singola. Il pensiero dell'esecuzione in termini di core CUDA non è corretto. Ogni ordito ha 32 fili. Questi thread verranno inviati a un gruppo di unità di esecuzione (ad esempio 16 core cuda). Per emettere tutti i 48 core in un singolo clock, uno dei due programmatori di warp deve selezionare un warp che soddisfi il req di una coppia superscalare ed entrambe le istruzioni devono essere di un tipo eseguito dai core CUDA. Inoltre, l'altro programmatore di warp deve scegliere un warp le cui istruzioni successive saranno eseguite dai core CUDA.
Greg Smith,

1
Non è necessario che gli orditi si trovino nello stesso blocco o che gli orditi in un blocco abbiano lo stesso contatore di programmi.
Greg Smith,

2
Nel tuo esempio, ogni programmatore sta raccogliendo un ordito ed emettendo 1 istruzione. In questo caso verranno utilizzati solo 2 gruppi di unità di esecuzione. Per utilizzare più unità di esecuzione 1 degli scheduler deve eseguire il doppio problema. Come indicato nei riferimenti, ci sono diversi tipi di unità di esecuzione (non solo ciò che è cuda core coniato) e ci sono regole di accoppiamento delle istruzioni (non ben documentate) che devono essere soddisfatte per gli scheduler a doppia emissione.
Greg Smith,

1
@GregSmith sto cercando in tutto il Web per scoprire da dove provengono questi 8 blocchi attivi per SM nell'architettura di Fermi. Non è nemmeno menzionato nel white paper fermi. Hai altri riferimenti a riguardo?
Greg K.

8

"E. Se un ordito contiene 20 thread, ma attualmente ci sono solo 16 core disponibili, il warp non verrà eseguito."

non è corretto. Stai confondendo i core nel loro solito senso (utilizzato anche nelle CPU) - il numero di "multiprocessori" in una GPU, con i core nel marketing nVIDIA parlano ("la nostra carta ha migliaia di core CUDA").

Un warp stesso può essere programmato solo su un singolo core (= multiprocessore) e può eseguire fino a 32 thread contemporaneamente; non può usare più di un singolo core.

Il numero "48 orditi" è il numero massimo di orditi attivi (orditi che possono essere scelti per essere programmati per funzionare nel ciclo successivo, in ogni ciclo) per multiprocessore, su GPU nVIDIA con capacità di calcolo 2.x; e questo numero corrisponde a 1536 = 48 x 32 thread.

Risposta basata su questo webinar


@GregSmith: modificata la risposta per risolvere questo problema. Va bene che tu ne fossi paziente, ma - sono passati cinque anni ...
einpoklum,

single core (= multiprocessore)? Penso che la domanda presupponga una terminologia single core = processore e non multiprocessore. Con la tua terminologia la tua risposta è corretta.
Adarsh
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.