Εργαλεία ΤΝ 101

Μάστερ CUDA: Για Μηχανικούς Μηχανικής Μάθησης

mm
Προσθέστε το Unite.AI στις προτιμώμενες πηγές σας στο Google
Master CUDA: For Machine Learning Engineers
div]:bg-bg-300 [&_pre]:-mr-4 md:[&_pre]:-mr-9″>

Η υπολογιστική ισχύς έχει γίνει ένα κρίσιμο παράγοντα για την προώθηση των ορίων του τι είναι δυνατό στη μηχανική μάθηση. Όσο τα μοντέλα γίνονται πιο σύνθετα και τα σύνολα δεδομένων επεκτείνονται εκθετικά, η παραδοσιακή υπολογιστική με βάση τον CPU συχνά δεν είναι σε θέση να ανταποκριθεί στις απαιτήσεις των σύγχρονων εργασιών μηχανικής μάθησης. Αυτό είναι το σημείο όπου έρχεται το CUDA (Compute Unified Device Architecture), μια προσέγγιση για την επιτάχυνση των ροών εργασιών μηχανικής μάθησης.

CUDA, αναπτυγμένο από την NVIDIA (NVDA ), είναι μια πλατφόρμα παράλληλης υπολογίσεως και μοντέλο προγραμματισμού που αξιοποιεί την τεράστια υπολογιστική ισχύ των Graphics Processing Units (GPU). Ενώ τα GPU αρχικά σχεδιάστηκαν για την απόδοση γραφικών, η αρχιτεκτονική τους τα κάνει εξαιρετικά κατάλληλα για τις απαιτήσεις παράλληλης επεξεργασίας πολλών αλγορίθμων μηχανικής μάθησης.

Σε αυτό το άρθρο, θα εξερευνήσουμε πώς το CUDA μπορεί να επανακαθορίσει τα projέκτα μηχανικής μάθησης σας, εμβαθύνοντας στις βασικές έννοιες, αρχιτεκτονική και πρακτικές εφαρμογές. Αν είστε ένας έμπειρος μηχανικός μηχανικής μάθησης που επιθυμεί να βελτιώσει τις ροές εργασιών ή ένας νέος που είναι πρόθυμος να αξιοποιήσει την ισχύ της υπολογίσεως GPU, αυτό το οδηγό θα σας παρέχει τις γνώσεις για να πάρει τα projέκτα μηχανικής μάθησης σας στο επόμενο επίπεδο.

Κατανόηση της Παράλληλης Υπολογίσεως και του CUDA

Πριν μιλήσουμε για τις ιδιαιτερότητες του CUDA, είναι κρίσιμο να κατανοήσουμε την βασική έννοια της παράλληλης υπολογίσεως. Ουσιαστικά, η παράλληλη υπολογίση είναι μια μορφή υπολογίσεως όπου πολλές υπολογίσεις εκτελούνται ταυτόχρονα. Η αρχή είναι απλή αλλά ισχυρή: μεγάλα προβλήματα μπορούν συχνά να διαιρεθούν σε μικρότερα, τα οποία επιλύονται ταυτόχρονα.

Η παραδοσιακή σειριακή προγραμματισμός, όπου οι εργασίες εκτελούνται μια μετά την άλλη, μπορεί να比θεί σε μια μονή λωρίδα σε einen δρόμο. Η παράλληλη υπολογίση, από την άλλη πλευρά, είναι σαν να προσθέτουμε πολλές λωρίδες σε αυτόν τον δρόμο, επιτρέποντας περισσότερο трафик (ή σε αυτήν την περίπτωση, υπολογισμούς) να ρέει ταυτόχρονα.

Το CUDA παίρνει αυτήν την έννοια και την εφαρμόζει στην ιδιαίτερη αρχιτεκτονική των GPU. Αντιθέτως με τους CPU, οι οποίοι σχεδιάζονται για την αντιμετώπιση ενός ευρέος φάσματος εργασιών με σύνθετο έλεγχο, τα GPU είναι βελτιστοποιημένα για την εκτέλεση τεράστιου αριθμού απλών, παρόμοιων εργασιών παράλληλα. Αυτό τα κάνει ιδανικά για τους τύπους υπολογισμών που συναντώνται στη μηχανική μάθηση, όπως οι πολλαπλασιασμοί πινάκων και οι συνволυτικές επιχειρήσεις.

Ας διαλύσουμε κάποιες βασικές έννοιες:

  1. Νήματα και Ιεραρχία Νημάτων

Στο CUDA, ένα νήμα είναι η μικρότερη μονάδα εκτέλεσης. Αντιθέτως με τα νήματα CPU, τα οποία είναι σχετικά βαρέα, τα νήματα GPU είναι εξαιρετικά ελαφριά. Ένα τυπικό πρόγραμμα CUDA μπορεί να εκκινήσει χιλιάδες ή ακόμη και εκατομμύρια νήματα ταυτόχρονα.

Το CUDA οργανώνει τα νήματα σε μια ιεραρχία:

  • Τα νήματα ομαδοποιούνται σε μπλοκ
  • Τα μπλοκ οργανώνονται σε ένα πλέγμα

Αυτή η ιεραρχική δομή επιτρέπει την αποτελεσματική κλιμάκωση σε διαφορετικές αρχιτεκτονικές GPU. Εδώ είναι μια απλή οπτική αναπαράσταση:


<p>|-- Block (0,0)
| |-- Thread (0,0)
| |-- Thread (0,1)
| |-- ...
|-- Block (0,1)
| |-- Thread (0,0)
| |-- Thread (0,1)
| |-- ...
|-- ...
  1. Ιεραρχία Μνήμης

Το CUDA παρέχει διαφορετικούς τύπους μνήμης, κάθε ένας με τις δικές του ιδιότητες:

  • Γлобική Μνήμη: Προσβάσιμη από όλα τα νήματα, αλλά με υψηλότερη καθυστέρηση
  • Κοινή Μνήμη: Γρήγορη μνήμη κοινή εντός ενός μπλοκ νημάτων
  • Τοπική Μνήμη: Ιδιωτική σε κάθε νήμα
  • Σταθερή Μνήμη: Διαβάσιμη μόνο μνήμη για σταθερά δεδομένα

Η κατανόηση και η αποτελεσματική χρήση αυτής της ιεραρχίας μνήμης είναι κρίσιμη για την βελτίωση των προγραμμάτων CUDA.

  1. Πυρήνες

Στο CUDA, ένας πυρήνας είναι μια συνάρτηση που εκτελείται στην GPU. Εκτελείται από πολλά νήματα παράλληλα. Εδώ είναι ένα απλό παράδειγμα ενός πυρήνα CUDA:

__global__ void vectorAdd(float *a, float *b, float *c, int n)
{
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i &lt; n)
c[i] = a[i] + b[i];
}

Αυτός ο πυρήνας προσθέτει δύο διανύσματα στοιχείο προς στοιχείο. Το __global__ κλειδί υποδεικνύει ότι αυτή η συνάρτηση είναι ένας πυρήνας CUDA.

CUDA Μοντέλο Μνήμης

στιβαρή GPU υπολογιστικών εφαρμογών, βιβλιοθηκών, μεσολογισμικού και γλωσσών προγραμματισμού που υποστηρίζονται από το CUDA

Η κατανόηση του μοντέλου μνήμης CUDA είναι κρίσιμη για τη συγγραφή αποτελεσματικού κώδικα GPU. Το μοντέλο μνήμης CUDA ενοποιεί τους συστήματα μνήμης του host (CPU) και του device (GPU) και εκθέτει την πλήρη ιεραρχία μνήμης, επιτρέποντας στους dévelopτερς να ελέγχουν τη τοποθέτηση δεδομένων ρητά για βέλτιστη απόδοση.

Πλεονεκτήματα της Ιεραρχίας Μνήμης

Οι σύγχρονες υπολογιστικές συστήματα, συμπεριλαμβανομένων των GPU, χρησιμοποιούν μια ιεραρχία μνήμης για την βελτίωση της απόδοσης. Η αρχή της τοπικότητας παίζει σημαντικό ρόλο εδώ:

  1. Χρονική Τοπικότητα: Αν μια τοποθεσία δεδομένων αναφέρεται, είναι πιθανό να αναφερθεί ξανά σύντομα.
  2. Χωρική Τοπικότητα: Αν μια τοποθεσία μνήμης αναφέρεται, κοντινές τοποθεσίες είναι πιθανό να αναφερθούν επίσης.

Κατανοώντας και αξιοποιώντας αυτούς τους τύπους τοπικότητας, μπορείτε να γράψετε προγράμματα CUDA που ελαχιστοποιούν τους χρόνους πρόσβασης στη μνήμη και μεγιστοποιούν την απόδοση.

Λεπτομερής Ανάλυση των Τύπων Μνήμης CUDA

Το μοντέλο μνήμης CUDA εκθέτει διάφορους τύπους μνήμης, κάθε ένας με διαφορετικά πεδία, διάρκεια ζωής και χαρακτηριστικά απόδοσης. Εδώ είναι μια επισκόπηση των πιο συχνά χρησιμοποιούμενων τύπων μνήμης CUDA:

  1. Καταχωρητές: Η ταχύτερη μνήμη που είναι διαθέσιμη για τα νήματα CUDA, χρησιμοποιείται για την αποθήκευση μεταβλητών.
  2. Κοινή Μνήμη: Μνήμη κοινή μεταξύ των νημάτων εντός του ίδιου μπλοκ. Έχει χαμηλότερη καθυστέρηση από τη γлобική μνήμη και είναι χρήσιμη για τη συγχρονίωση των νημάτων.
  3. Τοπική Μνήμη: Μνήμη ιδιωτική σε κάθε νήμα, χρησιμοποιείται όταν οι καταχωρητές είναι ανεπαρκείς.
  4. Γлобική Μνήμη: Ο μεγαλύτερος χώρος μνήμης, προσβάσιμος από όλα τα νήματα. Έχει υψηλότερη καθυστέρηση και χρησιμοποιείται συνήθως για την αποθήκευση δεδομένων που πρέπει να προσεγγισθούν από πολλά νήματα.
  5. Σταθερή Μνήμη: Διαβάσιμη μόνο μνήμη που είναι αποθηκευμένη για σταθερά δεδομένα.
  6. Μνήμη Κειμένου: Ειδικευμένη διαβάσιμη μόνο μνήμη που είναι βελτιστοποιημένη για bestimmμένα μοτίβα πρόσβασης, συχνά χρησιμοποιείται σε γραφικές εφαρμογές.
div]:bg-bg-300 [&_pre]:-mr-4 md:[&_pre]:-mr-9″>

CUDA για Μηχανική Μάθηση: Πρακτικές Εφαρμογές

δομή μιας εφαρμογής CUDA C/C++

Δομή μιας εφαρμογής CUDA C/C++

Τώρα που έχουμε καλύψει τις βασικές έννοιες, ας εξερευνήσουμε πώς το CUDA μπορεί να εφαρμοστεί σε κοινές εργασίες μηχανικής μάθησης.

  1. Πολλαπλασιασμός Πινάκων

Ο πολλαπλασιασμός πινάκων είναι eine βασική εργασία σε πολλές αλγόριθμους μηχανικής μάθησης, ιδιαίτερα σε νευρωνικά δίκτυα. Το CUDA μπορεί να επιταχύνει σημαντικά αυτήν την εργασία. Εδώ είναι μια απλή υλοποίηση:

__global__ void matrixMulKernel(float *A, float *B, float *C, int N)
{
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
float sum = 0.0f;

<p>if (row &lt; N &amp;&amp; col &lt; N) {
for (int i = 0; i &lt; N; i++) {
sum += A[row * N + i] * B[i * N + col];
}
C[row * N + col] = sum;
}
}</p>

<p>// Host function to set up and launch the kernel
void matrixMul(float *A, float *B, float *C, int N)
{
dim3 threadsPerBlock(16, 16);
dim3 numBlocks((N + threadsPerBlock.x - 1) / threadsPerBlock.x,
(N + threadsPerBlock.y - 1) / threadsPerBlock.y);</p>

<p>matrixMulKernel&lt;&lt;&lt;numBlocks, threadsPerBlock&gt;&gt;&gt;(A, B, C, N);
}

Αυτή η υλοποίηση διαιρεί τον πίνακα εξόδου σε μπλοκ, με κάθε νήμα να υπολογίζει ένα στοιχείο του αποτελέσματος. Ενώ αυτή η βασική έκδοση είναι ήδη ταχύτερη από μια υλοποίηση CPU για μεγάλους πίνακες, υπάρχει δωμάτιο για βελτίωση χρησιμοποιώντας κοινή μνήμη και άλλες τεχνικές.

  1. Συνβολυτικές Επιχειρήσεις

Νευρωνικά Δίκτυα Συνβολυτικών βασίζονται πολύ στις συνβολυτικές επιχειρήσεις. Το CUDA μπορεί να επιταχύνει δραματικά αυτές τις υπολογίσεις. Εδώ είναι ένα απλοποιημένο πυρήνα συνβολυτικών 2D:


<p>__global__ void convolution2DKernel(float *input, float *kernel, float *output,
int inputWidth, int inputHeight,
int kernelWidth, int kernelHeight)
{
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;</p>

<p>if (x &lt; inputWidth &amp;&amp; y &lt; inputHeight) {
float sum = 0.0f;
for (int ky = 0; ky &lt; kernelHeight; ky++) {
for (int kx = 0; kx = 0 &amp;&amp; inputX = 0 &amp;&amp; inputY &lt; inputHeight) {
sum += input[inputY * inputWidth + inputX] *
kernel[ky * kernelWidth + kx];
}
}
}
output[y * inputWidth + x] = sum;
}
}</p>

Αυτός ο πυρήνας εκτελεί μια 2D συνβολυτική επιχείρηση, με κάθε νήμα να υπολογίζει ένα pixel εξόδου. Σε πρακτική εφαρμογή, πιο σύνθετες υλοποιήσεις θα χρησιμοποιούσαν κοινή μνήμη για να μειώσουν τις προσβάσεις στη γлобική μνήμη και να βελτιστοποιήσουν για διάφορα μεγέθη πυρήνα.

  1. Στοχαστική Κατηγοριοποίηση Κλίσης (SGD)

Η SGD είναι μια γωνιακή αλγόριθμος βελτίωσης στη μηχανική μάθηση. Το CUDA μπορεί να παραλληλοποιήσει τον υπολογισμό των gradient σε πολλαπλά σημεία δεδομένων. Εδώ είναι ένα απλοποιημένο παράδειγμα για γραμμική παλινδρόμηση:


<p>__global__ void sgdKernel(float *X, float *y, float *weights, float learningRate, int n, int d)
{
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i &lt; n) {
float prediction = 0.0f;
for (int j = 0; j &lt; d; j++) {
prediction += X[i * d + j] * weights[j];
}
float error = prediction - y[i];
for (int j = 0; j &lt; d; j++) {
atomicAdd(&amp;weights[j], -learningRate * error * X[i * d + j]);
}
}
}</p>

<p>void sgd(float *X, float *y, float *weights, float learningRate, int n, int d, int iterations)
{
int threadsPerBlock = 256;
int numBlocks = (n + threadsPerBlock - 1) / threadsPerBlock;</p>

<p>for (int iter = 0; iter &lt; iterations; iter++) {
sgdKernel&lt;&lt;&lt;numBlocks, threadsPerBlock&gt;&gt;&gt;(X, y, weights, learningRate, n, d);
}
}</p>

Αυτή η υλοποίηση ενημερώνει τα βάρη παράλληλα για κάθε σημείο δεδομένων. Η atomicAdd συνάρτηση χρησιμοποιείται για να χειριστεί ασφαλείς ενημερώσεις στα βάρη.

Βελτιστοποίηση του CUDA για Μηχανική Μάθηση

Ενώ τα παραπάνω παραδείγματα δείχνουν τις βασικές της χρήσης του CUDA για εργασίες μηχανικής μάθησης, υπάρχουν πολλές τεχνικές βελτίωσης που μπορούν να βελτιστοποιήσουν περαιτέρω την απόδοση:

  1. Συγχρονισμένη Πρόσβαση στη Μνήμη

Τα GPU επιτυγχάνουν την κορυφαία απόδοση όταν τα νήματα σε ένα warp προσπελαύνουν συνεχόμενες τοποθεσίες μνήμης. Βεβαιωθείτε ότι οι δομές δεδομένων και τα μοτίβα πρόσβασης σας προάγουν τη συγχρονισμένη πρόσβαση στη μνήμη.

  1. Χρήση Κοινής Μνήμης

Η κοινή μνήμη είναι πολύ ταχύτερη από τη γлобική μνήμη. Χρησιμοποιήστε την για να κρυπτογραφήσετε συχνά προσεγγιζόμενα δεδομένα εντός ενός μπλοκ νημάτων.

κατανόηση της ιεραρχίας μνήμης με το CUDA

Κατανόηση της ιεραρχίας μνήμης με το CUDA

Αυτή η εικόνα απεικονίζει την αρχιτεκτονική ενός πολυπύρηνου συστήματος με κοινή μνήμη. Κάθε επεξεργαστής έχει την δική του cache, επιτρέποντας γρήγορη πρόσβαση σε συχνά χρησιμοποιούμενα δεδομένα. Οι επεξεργαστές επικοινωνούν μέσω ενός κοινού 버ού, ο οποίος τους συνδέει με ένα μεγαλύτερο χώρο κοινής μνήμης.

Για παράδειγμα, στον πολλαπλασιασμό πινάκων:


<p>__global__ void matrixMulSharedKernel(float *A, float *B, float *C, int N)
{
__shared__ float sharedA[TILE_SIZE][TILE_SIZE];
__shared__ float sharedB[TILE_SIZE][TILE_SIZE];</p>

<p>int bx = blockIdx.x; int by = blockIdx.y;
int tx = threadIdx.x; int ty = threadIdx.y;</p>

<p>int row = by * TILE_SIZE + ty;
int col = bx * TILE_SIZE + tx;</p>

float sum = 0.0f;

<p>for (int tile = 0; tile &lt; (N + TILE_SIZE - 1) / TILE_SIZE; tile++) {
if (row &lt; N &amp;&amp; tile * TILE_SIZE + tx &lt; N)
sharedA[ty][tx] = A[row * N + tile * TILE_SIZE + tx];
else
sharedA[ty][tx] = 0.0f;</p>

<p>if (col &lt; N &amp;&amp; tile * TILE_SIZE + ty &lt; N)
sharedB[ty][tx] = B[(tile * TILE_SIZE + ty) * N + col];
else
sharedB[ty][tx] = 0.0f;</p>

__syncthreads();

<p>for (int k = 0; k &lt; TILE_SIZE; k++)
sum += sharedA[ty][k] * sharedB[k][tx];</p>

__syncthreads();
}

<p>if (row &lt; N &amp;&amp; col &lt; N)
C[row * N + col] = sum;
}</p>

Αυτή η βελτιστοποιημένη έκδοση χρησιμοποιεί κοινή μνήμη για να μειώσει τις προσβάσεις στη γлобική μνήμη, βελτιώνοντας σημαντικά την απόδοση για μεγάλους πίνακες.

  1. Ασύγχρονες Επιχειρήσεις

Το CUDA υποστηρίζει ασύγχρονες επιχειρήσεις, επιτρέποντας σας να перекαλύψετε υπολογισμό με μεταφορά δεδομένων. Αυτό είναι ιδιαίτερα χρήσιμο σε pipelines μηχανικής μάθησης όπου μπορείτε να προετοιμάσετε το επόμενο σύνολο δεδομένων ενώ το τρέχον σύνολο δεδομένων επεξεργάζεται.

cudaStream_t stream1, stream2;
cudaStreamCreate(&amp;stream1);
cudaStreamCreate(&amp;stream2);

<p>// Ασύγχρονες μεταφορές μνήμης και εκκίνηση πυρήνων
cudaMemcpyAsync(d_data1, h_data1, size, cudaMemcpyHostToDevice, stream1);
myKernel&lt;&lt;&lt;grid, block, 0, stream1&gt;&gt;&gt;(d_data1, ...);</p>

<p>cudaMemcpyAsync(d_data2, h_data2, size, cudaMemcpyHostToDevice, stream2);
myKernel&lt;&lt;&lt;grid, block, 0, stream2&gt;&gt;&gt;(d_data2, ...);</p>

<p>cudaStreamSynchronize(stream1);
cudaStreamSynchronize(stream2);
  1. Πυρήνες Τανυστών

Για εργασίες μηχανικής μάθησης, οι Πυρήνες Τανυστών της NVIDIA (διαθέσιμοι σε νεότερες αρχιτεκτονικές GPU) μπορούν να προσφέρουν σημαντικές επιταχύνσεις για πολλαπλασιασμούς πινάκων και συνβολυτικές επιχειρήσεις. Βιβλιοθήκες όπως cuDNN και cuBLAS αξιοποιούν αυτόματα τους Πυρήνες Τανυστών όταν είναι διαθέσιμοι.

Προκλήσεις και Συστάσεις

Ενώ το CUDA προσφέρει τεράστιες ωφέλειες για τη μηχανική μάθηση, είναι σημαντικό να είναι ενήμεροι για τις πιθανές προκλήσεις:

  1. Διαχείριση Μνήμης: Η μνήμη GPU είναι περιορισμένη σε σύγκριση με τη μνήμη συστήματος. Η αποτελεσματική διαχείριση μνήμης είναι κρίσιμη, ιδιαίτερα όταν εργάζεστε με μεγάλα σύνολα δεδομένων ή μοντέλα.
  2. Επιβάρυνση Μεταφοράς Δεδομένων: Η μεταφορά δεδομένων μεταξύ CPU και GPU μπορεί να είναι ένα μπουκάλι. Ελαχιστοποιήστε τις μεταφορές και χρησιμοποιήστε ασύγχρονες επιχειρήσεις όταν είναι δυνατό.
  3. Ακρίβεια: Τα GPU παραδοσιακά ξεχωρίζουν στις υπολογίσεις με μονάδα κινητής υποδιαστολής (FP32). Ενώ η υποστήριξη για διπλή ακρίβεια (FP64) έχει βελτιωθεί, είναι συχνά πιο αργή. Πολλές εργασίες μηχανικής μάθησης μπορούν να λειτουργήσουν καλά με χαμηλότερη ακρίβεια (π.χ. FP16), την οποία τα σύγχρονα GPU χειρίζονται πολύ αποτελεσματικά.
  4. Συγκρότηση Κώδικα: Η γραφή αποτελεσματικού κώδικα CUDA μπορεί να είναι πιο σύνθετη από τον κώδικα CPU. Η αξιοποίηση βιβλιοθηκών όπως cuDNN, cuBLAS και πλατφόρμων όπως TensorFlow ή PyTorch μπορεί να βοηθήσει να αφαλοίφθεί μέρος αυτής της σύνθετης διαδικασίας.

Μετακίνηση σε Πολλαπλά GPU

Όσο τα μοντέλα μηχανικής μάθησης μεγαλώνουν σε μέγεθος και πολυπλοκότητα, ένα μόνο GPU μπορεί να μην είναι πλέον αρκετό για να χειριστεί το φόρτο εργασίας. Το CUDA επιτρέπει την κλιμάκωση της εφαρμογής σας σε πολλαπλά GPU, είτε εντός ενός μόνο κόμβου είτε σε ένα cluster.

Λόγοι για τη Χρήση Πολλαπλών GPU

  1. Μέγεθος Πεδίου Προβλήματος: Το σύνολο δεδομένων ή το μοντέλο σας μπορεί να είναι πολύ μεγάλο για να χωρέσει στη μνήμη ενός μόνο GPU.
  2. Từput και Αποτελεσματικότητα: Ακόμη και αν μια εργασία ταιριάζει σε ένα μόνο GPU, η χρήση πολλαπλών GPU μπορεί να αυξήσει την απόδοση επεξεργασίας πολλαπλών εργασιών ταυτόχρονα.

Δομή Προγραμματισμού CUDA

Για να αξιοποιήσετε αποτελεσματικά το CUDA, είναι απαραίτητο να κατανοήσετε τη δομή προγραμματισμού του, η οποία περιλαμβάνει τη γραφή πυρήνων (συναρτήσεων που εκτελούνται στην GPU) και τη διαχείριση μνήμης μεταξύ του host (CPU) και του device (GPU).

Host vs. Device Μνήμη

Στο CUDA, η μνήμη διαχειρίζεται ξεχωριστά για τον host και το device. Οι ακόλουθες είναι οι κύριες συναρτήσεις που χρησιμοποιούνται για τη διαχείριση μνήμης:

  • cudaMalloc: Αναθέτει μνήμη στο device.
  • cudaMemcpy: Αντιγράφει δεδομένα μεταξύ host και device.
  • cudaFree: Απελευθερώνει μνήμη στο device.

Παράδειγμα: Πρόσθεση Δύο Πινάκων

Ας δούμε ένα παράδειγμα που προσθέτει δύο πίνακες χρησιμοποιώντας CUDA:


<p>__global__ void sumArraysOnGPU(float *A, float *B, float *C, int N) {
int idx = threadIdx.x + blockIdx.x * blockDim.x;
if (idx &lt; N) C[idx] = A[idx] + B[idx];
}</p>

<p>int main() {
int N = 1024;
size_t bytes = N * sizeof(float);</p>

<p>float *h_A, *h_B, *h_C;
h_A = (float*)malloc(bytes);
h_B = (float*)malloc(bytes);
h_C = (float*)malloc(bytes);</p>

<p>float *d_A, *d_B, *d_C;
cudaMalloc(&amp;d_A, bytes);
cudaMalloc(&amp;d_B, bytes);
cudaMalloc(&amp;d_C, bytes);</p>

<p>cudaMemcpy(d_A, h_A, bytes, cudaMemcpyHostToDevice);
cudaMemcpy(d_B, h_B, bytes, cudaMemcpyHostToDevice);</p>

<p>int blockSize = 256;
int gridSize = (N + blockSize - 1) / blockSize;</p>

<p>sumArraysOnGPU&lt;&lt;&lt;gridSize, blockSize&gt;&gt;&gt;(d_A, d_B, d_C, N);</p>

<p>cudaMemcpy(h_C, d_C, bytes, cudaMemcpyDeviceToHost);</p>

<p>cudaFree(d_A);
cudaFree(d_B);
cudaFree(d_C);</p>

free(h_A);
free(h_B);
free(h_C);

return 0;
}

Σε αυτό το παράδειγμα, η μνήμη αναθέτει τόσο στον host όσο και στο device, τα δεδομένα μεταφέρονται στο device και ο πυρήνας εκκινείται για την εκτέλεση του υπολογισμού.

Συμπέρασμα

Το CUDA είναι ένα ισχυρό εργαλείο για μηχανικούς μηχανικής μάθησης που επιθυμούν να επιταχύνουν τα μοντέλα τους και να χειριστούν μεγαλύτερα σύνολα δεδομένων. Κατανοώντας το μοντέλο μνήμης CUDA, βελτιστοποιώντας την πρόσβαση στη μνήμη και αξιοποιώντας πολλαπλά GPU, μπορείτε να βελτιστοποιήσετε σημαντικά την απόδοση των εφαρμογών μηχανικής μάθησης.

Ενώ έχουμε καλύψει τις βασικές και einige προηγμένες θεμάτων σε αυτό το άρθρο, το CUDA είναι ένα ευρύ πεδίο με συνεχείς εξελίξεις. Μείνετε ενημερωμένοι με τις τελευταίες εκδόσεις CUDA, αρχιτεκτονικές GPU και βιβλιοθήκες μηχανικής μάθησης για να αξιοποιήσετε το μέγιστο αυτής της ισχυρής τεχνολογίας.

Έχω περάσει τα τελευταία πέντε χρόνια βυθισμένος στον συναρπαστικό κόσμο της Μηχανικής Μάθησης και του Βαθιάς Μάθησης. Η δέσμευσή μου και η εξειδίκευσή μου με οδήγησαν να συμβάλλω σε πάνω από 50 διαφορετικά projects μηχανικής λογισμικού, με ιδιαίτερη έμφαση στο AI/ML. Η συνεχής περιέργειά μου με έχει οδηγήσει επίσης προς την Επεξεργασία Φυσικής Γλώσσας, ένα πεδίο που είμαι πρόθυμος να εξερευνήσω περαιτέρω.