Nvidia CUDA
NVIDIA CUDA (Compute Unified Device Architecture) is een parallelle computerplatform en programmeermodel ontwikkeld door NVIDIA. CUDA stelt ontwikkelaars in staat om hoogpresterende NVIDIA GPU's (Graphics Processing Units) te gebruiken voor algemene computertaken. Met CUDA kunnen ontwikkelaars de krachtige rekenmogelijkheden van GPU's benutten voor grootschalige parallelle berekeningen, waardoor de applicatieprestaties aanzienlijk verbeteren, vooral in gebieden zoals wetenschappelijke computing, machine learning, beeldverwerking en fysieke simulatie. CUDA ondersteunt meerdere programmeertalen, zoals C, C++ en Python, en biedt een rijke set bibliotheken en tools om ontwikkelaars te helpen gemakkelijk GPU-versnelde computing te bereiken.
Basisprincipes van CUDA-programmering
Hoe kunnen we GPU-resources in een programma aanroepen om code-uitvoering te versnellen? Door CUDA te gebruiken, kunnen we gemakkelijk duizenden rekenkernen binnen de GPU beheren. Het CUDA-programmeermodel is een heterogeen model dat vereist dat de CPU en GPU samenwerken. In CUDA-programmering worden de termen "host" en "device" gebruikt om de apparaten te onderscheiden waar de code wordt uitgevoerd.
- Host: CPU en host-geheugen
- Device: GPU en videogeheugen
Over het algemeen laadt het hoofdprogramma op de CPU, kopieert vervolgens de initialisatiegegevens naar de GPU. De GPU voert de berekeningen uit en kopieert de resultaten terug naar de CPU.
We kunnen een kernel in een CUDA-programma gebruiken om het bovenstaande proces te implementeren. In CUDA verwijst een kernel naar een functie die, wanneer aangeroepen, de GPU veroorzaakt om veel threads tegelijkertijd te starten om deze kernel uit te voeren, waardoor parallellisme wordt bereikt. Elke thread voert de kernel uit met behulp van zijn thread-ID om overeen te komen met de index van de invoergegevens, waardoor wordt gegarandeerd dat elke thread dezelfde kernel uitvoert maar verschillende gegevens verwerkt.
De GPU heeft een enorm aantal rekenkernen, en CUDA gebruikt een tweeledige organisatiestructuur om deze kernen te beheren. Hier zijn drie concepten in CUDA-programmering die moeten worden geïntroduceerd: Thread, Block en Grid.
- Thread: Een parallel CUDA-programma wordt uitgevoerd door veel threads.
- Block: Verschillende threads worden gegroepeerd om een blok te vormen.
- Grid: Meerdere blokken worden vervolgens georganiseerd in een grid.
Opmerking: Threads binnen hetzelfde blok kunnen synchroniseren en communiceren via gedeeld geheugen.
In CUDA heeft elke thread een unieke identificatie genaamd ThreadIdx, die afhankelijk is van de manier waarop het Grid en Block zijn verdeeld.
Bovendien volgt hier een korte introductie van het CUDA-geheugenmodel, zoals weergegeven in het diagram hieronder. Elke thread heeft zijn eigen privé lokale geheugen, terwijl elk threadblok gedeeld geheugen bevat dat toegankelijk is voor alle threads binnen het blok, met een levensduur gelijk aan die van het threadblok. Verder kunnen alle threads toegang krijgen tot globaal geheugen. Er zijn ook enkele alleen-lezen geheugenruimtes: constant geheugen en textuurgeheugen. De geheugenstructuur heeft betrekking op programma-optimalisatie, wat hier niet in detail zal worden besproken.
CUDA demonstratievoorbeeld
Laten we nu GPU-acceleratie ervaren door een eenvoudig voorbeeld. In dit voorbeeld berekenen we de som van twee vectoren op zowel de CPU als de GPU, en vergelijken we de computatiesnelheden tussen CPU en GPU.
De runtime-omgeving voorbereiden
Het JetPack-besturingssysteem bevat al de CUDA-runtime-omgeving. We kunnen de CUDA-versie controleren met het jtop-hulpmiddel. Open de terminal en voer jtop uit.
jtop
Opmerking: Als jtop niet op uw systeem is geïnstalleerd, raadpleeg dan deze handleiding.
Hieronder zullen we Python gebruiken om CUDA-code te schrijven. We moeten dus de volgende opdracht in de terminal uitvoeren om Numba te installeren.
pip install numbaHet voorbeeldcode uitvoeren
Stap 1: Maak een Python-script voor deze cursus in de hoofdmap:
mkdir -p ~/reComputer_J/3.4_CUDA
touch ~/reComputer_J/3.4_CUDA/add_compare.pyStap 2: Open VSCode in de map waar het script zich bevindt:
cd ~/reComputer_J/3.4_CUDA
code .Stap 3: In de linkernavigatiebalk kunnen we hello_world.py zien. Voer de volgende code in het python-bestand in:
Klik om code uit te breiden
from numba import cuda, float32
import numpy as np
import time
@cuda.jit
def add_gpu(a, b, out):
tx = cuda.threadIdx.x
ty = cuda.blockIdx.x
block_size = cuda.blockDim.x
grid_size = cuda.gridDim.x
start = tx + ty * block_size
stride = block_size * grid_size
for i in range(start, a.shape[0], stride):
out[i] = a[i] + b[i]
def add_cpu(a, b, out):
for i in range(a.shape[0]):
out[i] = a[i] + b[i]
def main():
# Prepare test data
n = 10000000
a = np.arange(n).astype(np.float32)
b = np.arange(n).astype(np.float32)
out = np.empty_like(a)
# 1. Test CPU computation time
start = time.time()
add_cpu(a, b, out)
print("CPU Time Taken:", time.time()-start)
# 2. Test GPU computation time
# Copy data from host memory to device memory
d_a = cuda.to_device(a)
d_b = cuda.to_device(b)
d_out = cuda.device_array_like(out)
# Define the number of threads per block and the number of blocks
threads_per_block = 256
blocks_per_grid = (n + threads_per_block - 1) // threads_per_block
# Compute the result
start_gpu = time.time()
add_gpu[blocks_per_grid, threads_per_block](d_a, d_b, d_out)
end_gpu = time.time()
# Copy the result from device memory back to host memory
d_out.copy_to_host(out)
print("GPU Time Taken:", end_gpu-start_gpu)
if __name__ == "__main__":
main()
Stap 4: Klik op de uitvoerknop om het resultaat van het Python-script in de terminal te zien.

Uit de resultaten in de terminal kan worden waargenomen dat de GPU-computatiesnelheid sneller is.
Meer CUDA-leertutorials
| Tutorial | Type | Beschrijving |
|---|---|---|
| Getting Started with CUDA Programming(zh) | Document | Een minimalistische tutorial om aan de slag te gaan met CUDA-programmering in het Chinees |
| CUDA C++ Programming Guide | Document | De programmeerhandleiding voor het CUDA-model en de interface. |