Μετάβαση στο περιεχόμενο

Λογισμικό & Προγραμματισμός Συστημάτων Υψηλής Επίδοσης — Ενδεικτικά Θέματα (2025-26)

Μεταγραφή από το αρχείο ΛΠΣΥΕ Ενδεικτικά Θέματα.pdf του eClass.

ΛΠΣΥΕ Ενδεικτικά Θέματα.pdf

Λογισμικό και Προγραμματισμός Συστημάτων Υψηλής Επίδοσης

Ενδεικτικά Θέματα

1. Η μέγιστη ονομαστική απόδοση ενός υπολογιστικού συστήματος μπορεί να διπλασιαστεί όταν:

  • [ ] a. διπλασιάσουμε το μέγεθος της κρυφής μνήμης
  • [ ] b. υποδιπλασιάσουμε τον αριθμό των πυρήνων
  • [ ] c. υποδιπλασιάσουμε την ακρίβεια των πράξεων κινητής υποδιαστολής (double σε float)
  • [ ] d. τίποτα από τα υπόλοιπα

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

  • [ ] a. 2
  • [ ] b. 5
  • [ ] c. 10
  • [ ] d. τίποτα από τα υπόλοιπα

3. Ένα υπολογιστικό σύστημα διαθέτει 4 πυρήνες που λειτουργούν σε συχνότητα 2.1 GHz. Κάθε πυρήνας υποστηρίζει διανυσματικές εντολές (SIMD) μεγέθους 8. Στις εντολές αυτές περιλαμβάνεται και η εντολή “fused-multiply-add” (FMA) (δηλ. μία εντολή που εκτελεί την πράξη a := b*c + d). Επίσης, κάθε εντολή (απλή ή SIMD) ολοκληρώνεται σε ένα κύκλο ρολογιού. Ποια είναι η μέγιστη ονομαστική απόδοση (σε GFLOP/s) του συστήματος;

4. Ποια θα είναι η τιμή της μεταβλητής test_var μετά την εκτέλεση της παράλληλης περιοχής στον παρακάτω OpenMP κώδικα;

int test_var = 1;

#pragma omp parallel num_threads(4) reduction(*:test_var)
{
    test_var = omp_get_thread_num() - omp_get_num_threads();
}
  • [ ] a. τίποτα από τα παρακάτω
  • [ ] b. 1
  • [ ] c. -10
  • [ ] d. 12

5. Στον παρακάτω κώδικα OpenMP και συγκεκριμένα στο σημείο αναφοράς εντός του task, η μεταβλητή b είναι:

int main(int argc, char *argv[]) {
  int b,c ;
  #pragma omp parallel shared(c) private(b)
  {
    int d ;
    #pragma omp task
    {
      int e ;
      // σημείο αναφοράς εντός του task
    }
  }
  // more code here
  return 0;
}
  • [ ] a. shared
  • [ ] b. private
  • [ ] c. threadprivate
  • [ ] d. firstprivate

6. Ποιες θα είναι οι τελικές τιμές του διανύσματος Α μετά την εκτέλεση του παρακάτω OpenMP κώδικα; Εξηγήστε .

int A[12];
omp_set_num_threads(4);
#pragma omp parallel for schedule(static,3)
for (int i = 0; i < 12; i++) {
    A[i] = omp_get_thread_num();
}

7. Ποια τιμή θα εκτυπωθεί όταν ο παρακάτω κώδικας MPI εκτελεστεί με 4 διεργασίες (mpirun -n 4 ); Εξηγήστε.

int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);

double var1 = rank+1.0;
double var2 = 0.0, var3 = 0.0;
MPI_Reduce(&var1,&var2,1, MPI_DOUBLE, MPI_SUM,0, MPI_COMM_WORLD);
MPI_Bcast(&var2, 1, MPI_DOUBLE, 0,  MPI_COMM_WORLD);
MPI_Allreduce(&var2, &var3, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);

if (rank == 1) printf("%f\n", var3);

8. Ποιο μήνυμα θα εκτυπώσει η διεργασία με rank = 2 όταν ο παρακάτω κώδικας MPI εκτελεστεί με 4 διεργασίες;

#include <mpi.h>
#include <stdio.h>

int main(int argc, char** argv) {
  MPI_Init(&argc, &argv);

  int rank, size;
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &size);

  int input = rank + 2; // Each process has a value: rank + 2
  int output;

  MPI_Scan(&input, &output, 1, MPI_INT, MPI_PROD, MPI_COMM_WORLD);
  printf("Process %d: output = %d\n", rank, output);

  MPI_Finalize();
  return 0;
}
  • [ ] a. 6
  • [ ] b. 9
  • [ ] c. 24
  • [ ] d. 41

9. Ποια από τα παρακάτω είναι αληθές για τις συλλογικές (collective) λειτουργίες Ι/Ο του MPI;

  • [ ] a. Όλες οι διεργασίες απαιτείται να συμμετέχουν στην λειτουργία I/O.
  • [ ] b. Είναι πιο αποδοτικές από τις ανεξάρτητες λειτουργίες Ι/Ο.
  • [ ] c. Εκτελούνται με συναρτήσεις όπως οι MPI_File_read_all() και MPI_File_write_all().
  • [ ] d. Όλα τα παραπάνω.

10. Ποια από τις παρακάτω προτάσεις είναι σωστή σχετικά με το παράλληλο I/O με MPI;

  • [ ] a. Το MPI υποστηρίζει μόνο συλλογικές λειτουργίες I/O, όπου όλες οι διεργασίες πρέπει να συμμετέχουν στην ίδια λειτουργία ανάγνωσης ή εγγραφής.
  • [ ] b. Το MPI υποστηρίζει τόσο συλλογικές όσο και ατομικές λειτουργίες I/O, επιτρέποντας μεγαλύτερη ευελιξία στη διαχείριση αρχείων.
  • [ ] c. Το MPI δεν υποστηρίζει καθόλου λειτουργίες I/O, και οι προγραμματιστές πρέπει να χρησιμοποιούν εξωτερικές βιβλιοθήκες για ανάγνωση και εγγραφή αρχείων.
  • [ ] d. Το MPI υποστηρίζει μόνο ατομικές λειτουργίες I/O, όπου κάθε διεργασία διαχειρίζεται τα δικά της αρχεία ανεξάρτητα από τις άλλες διεργασίες.

11. Ποιο από τα παρακάτω είναι αληθές σχετικά με τα SSE και AVX;

  • [ ] a. Το SSE λειτουργεί με καταχωρητές 128-bits, ενώ το AVX λειτουργεί με καταχωρητές 256-bits.
  • [ ] b. Το AVX είναι υποσύνολο του SSE.
  • [ ] c. Το SSE υποστηρίζει πράξεις κινητής υποδιαστολής ενώ το AVX όχι.
  • [ ] d. Το AVX δεν είναι διαθέσιμο σε επεξεργαστές της Intel.

12. Ποια συνάρτηση θα χρησιμοποιούσατε για να φορτώσετε 4 αριθμούς κινητής υποδιαστολής απλής ακρίβειας σε έναν καταχωρητή SSE;

  • [ ] a. _mm_load_ps()
  • [ ] b. _mm_load_pd()
  • [ ] c. _mm256_load_ps()
  • [ ] d. _mm256_load_pd()

13. Τι κάνει το παρακάτω τμήμα κώδικα με τις εντολές AVX;

__m256 a = _mm256_load_ps(array1);
__m256 b = _mm256_load_ps(array2);
__m256 c = _mm256_add_ps(a, b);
  • [ ] a. Προσθέτει τα στοιχεία δύο διανυσμάτων με 4 floats το καθένα
  • [ ] b. Προσθέτει τα στοιχεία δύο διανυσμάτων με 8 floats το καθένα
  • [ ] c. Προσθέτει τα στοιχεία δύο διανυσμάτων με 8 doubles το καθένα
  • [ ] d. Προσθέτει τα στοιχεία δύο διανυσμάτων με 8 integers το καθένα

14. Ποιο από τα παρακάτω είναι αληθές σχετικά με τις απαιτήσεις στοίχισης για εντολές SIMD;

  • [ ] a. Το SSE απαιτεί στοίχιση σε 16 bytes για τη συνάρτηση _mm_load_ps().
  • [ ] b. Το AVX απαιτεί στοίχιση σε 32 bytes για τη συνάρτηση _mm256_load_ps().
  • [ ] c. Η πρόσβαση σε μη στοιχισμένη μνήμη μπορεί να οδηγήσει σε σφάλματα ή χαμηλή απόδοση.
  • [ ] d. Όλα τα παραπάνω.

15. Αναφέρετε και διορθώστε πιθανά σφάλματα τον παρακάτω πολυνηματικό κώδικα με εντολές SSE.

int nThreads = 16;
float*data;
posix_memalign((void **)&data, 16, 32*sizeof(float)); // allocate 16-Byte aligned memory for 32 float values
for (int i=0; i<32; ++i) data[i] = drand48();

omp_set_num_threads(nThreads);
#pragma omp parallel
{
  float * const myData = data + 2*omp_get_thread_num();
  __m128 c4 = _mm_set1_ps(M_PI);  // = 3.141592…
  __m128 r4 = _mm_load_ps(myData);
  __m128 a4 = _mm_mul_ps(c4, _mm_mul_ps(r4, r4));
  _mm_store_ps(myData, a4);
}

16. Ποια είναι η σωστή σύνταξη για να εκτελεστεί μια συνάρτηση CUDA με 256 νήματα ανά μπλοκ και 10 μπλοκς;

  • [ ] a. kernel<<<10, 256>>>();
  • [ ] b. kernel<<<256, 10>>>();
  • [ ] c. kernel<<<10, 256, 0>>>();
  • [ ] d. kernel<<<256, 10, 0>>>();

17. Ποιος είναι ο σκοπός της χρήση του __global__ στην CUDA;

  • [ ] a. Καθορίζει μια συνάρτηση που τρέχει στον κύριο σύστημα (host) αλλά μπορεί να κληθεί από την κάρτα γραφικών.
  • [ ] b. Καθορίζει μια συνάρτηση που τρέχει στην κάρτα γραφικών και μπορεί να κληθεί από το κύριο σύστημα (host).
  • [ ] c. Καθορίζει μια συνάρτηση που τρέχεις στην κάρτα γραφικών και μπορεί να κληθεί μόνο από την κάρτα γραφικών.
  • [ ] d. Καθορίζει μια συνάρτηση που τρέχει στον επεξεργαστή (host) και μπορεί να κληθεί μόνο από τον επεξεργαστή (host).

18. Ποια συνάρτηση CUDA χρησιμοποιείται για να αντιγράψει δεδομένα από το κύριο σύστημα (host) στην κάρτα γραφικών;

  • [ ] a. cudaMemcpyToHost()
  • [ ] b. cudaMemcpyToDevice()
  • [ ] c. cudaMemcpy() με cudaMemcpyHostToDevice
  • [ ] d. cudaMemcpy() με cudaMemcpyDeviceToHost

19. Ποιος είναι ο σκοπός χρήσης της συνάρτησης __syncthreads() σε έναν υπολογιστικό πυρήνα CUDA;

  • [ ] a. Συγχρονίζει όλα τα νήματα σε ένα πλέγμα (grid)
  • [ ] b. Συγχρονίζει όλα τα νήματα σε ένα μπλοκ
  • [ ] c. Συγχρονίζει όλα τα νήματα σε ένα warp
  • [ ] d. Συγχρονίζει το κύριο σύστημα και την κάρτα γραφικών

20. Ποια είναι η έξοδος από την εκτέλεση του παρακάτω κώδικα CUDA;

__global__ void kernel(int *a) {
  int tid = threadIdx.x;
  a[tid] = tid * 2;
}

int main() {
  int h_a[4], *d_a;
  cudaMalloc(&d_a, 4 * sizeof(int));
  kernel<<<1, 4>>>(d_a);
  cudaMemcpy(h_a, d_a, 4*sizeof(int), cudaMemcpyDeviceToHost);
  printf("%d %d %d %d", h_a[0], h_a[1], h_a[2], h_a[3]);
  cudaFree(d_a);
  return 0;
}
  • [ ] a. 0 1 2 3
  • [ ] b. 0 2 4 6
  • [ ] c. 0 0 0 0
  • [ ] d. 1 2 3 4

21. Αναφέρετε τα σφάλματα στον παρακάτω κώδικα και προτείνετε μια λύση.

__global__ void kernel(int *array)
{
  // do something
}

int main()
{
  int * b;
  cudaMalloc(&b, 512*sizeof(int));

  for (int i=0; i<512; i++) b[i] = i;

  kernel<<< 16, 32 >>>(b);

  cudaFree(b);
  return 0;
}
Λύσεις (⚪ AI, μη επαληθευμένο)

Κατάσταση: ⚪ AI, μη επαληθευμένο Πηγή: λύσεις AI (deepseek-flash, gemini-3.7-flash), ανεξάρτητη επανάληψη και αντιπαραβολή· καμία δεν έχει ελεγχθεί από άνθρωπο

Προσοχή

Λύση από AI — ελέγξτε την· δεν έχει επαληθευτεί από άνθρωπο. Η ένδειξη «δύο λύσεις συμφωνούν» δεν σημαίνει ότι η απάντηση είναι σωστή.

AI λύσεις — 2025-sample

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

Θέμα 1

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

c. υποδιπλασιάσουμε την ακρίβεια των πράξεων κινητής υποδιαστολής (double σε float). Η μέγιστη απόδοση αυξάνεται όταν σε ίδιο πλάτος SIMD καταχωρητή χωρούν διπλάσια στοιχεία απλής ακρίβειας. Η κρυφή μνήμη δεν αλλάζει την ονομαστική κορυφή και το υποδιπλασιασμό πυρήνων μειώνει την απόδοση.

Θέμα 2

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

b. 5. Από τον νόμο Amdahl, αν \(f=0.20\) είναι το μη παραλληλοποιήσιμο κλάσμα, τότε \(S_{\max}=1/f=1/0.20=5\).

Θέμα 3

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

Η κορυφή είναι [ 4 \text{ cores}\times 2.1\times10^9\ \mathrm{Hz}\times 8\ \text{lane}\times 2\ \mathrm{FLOP/lane} =134.4\times10^9\ \mathrm{FLOP/s}. ] Άρα μέγιστη ονομαστική απόδοση: \(134.4\ \mathrm{GFLOP/s}\).

Θέμα 4

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

a. τίποτα από τα παρακάτω. Κάθε νήμα \(t=0,1,2,3\) θέτει την ιδιωτική του τιμή σε \(t-4=-4,-3,-2,-1\). Το γινόμενο είναι [ 1\cdot(-4)(-3)(-2)(-1)=24, ] το οποίο δεν υπάρχει στις επιλογές b, c, d.

Θέμα 5

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

d. firstprivate. Η μεταβλητή \(b\) είναι private στην παράλληλη περιοχή. Όταν δημιουργείται ένα explicit task, οι αυτόματες μεταβλητές του περιβάλλοντος που είναι private αντιγράφονται κατά την εκτέλεση του task ως firstprivate, εκτός αν δοθεί άλλος χαρακτηρισμός.

Θέμα 6

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

Με schedule(static,3) και 12 επαναλήψεις, τα διαδοχικά chunks των 3 ανατίθενται κυκλικά στα 4 νήματα: - νήμα 0: \(i=0,1,2\) - νήμα 1: \(i=3,4,5\) - νήμα 2: \(i=6,7,8\) - νήμα 3: \(i=9,10,11\)

Άρα [ A=[0,0,0,1,1,1,2,2,2,3,3,3]. ]

Θέμα 7

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

Οι αρχικές τιμές είναι \(var1=rank+1\), δηλαδή \(1,2,3,4\).
Το MPI_Reduce με MPI_SUM στη ρίζα 0 δίνει \(var2=1+2+3+4=10\) στη rank 0.
Το MPI_Bcast μεταδίδει το \(var2=10\) σε όλες τις διεργασίες.
Το MPI_Allreduce αθροίζει τα \(var2\) όλων των διεργασιών: [ 10+10+10+10=40. ] Άρα η rank 1 εκτυπώνει:

40.000000

Θέμα 8

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

c. 24. Το MPI_Scan είναι inclusive scan. Για rank 2 υπολογίζεται το γινόμενο των input από τις ranks 0,1,2: [ (0+2)(1+2)(2+2)=2\cdot3\cdot4=24. ]

Θέμα 9

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: μέτρια

d. Όλα τα παραπάνω. Οι συλλογικές λειτουργίες I/O απαιτούν συμμετοχή όλων των διεργασιών, υλοποιούνται π.χ. με MPI_File_read_all()/MPI_File_write_all() και συνήθως συνδυάζουν προσπελάσεις για καλύτερη απόδοση. Αυστηρά, η απόδοση έναντι της ανεξάρτητης I/O δεν είναι εγγυημένη σε κάθε περίπτωση.

Θέμα 10

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

b. Το MPI υποστηρίζει τόσο συλλογικές όσο και ατομικές λειτουργίες I/O, επιτρέποντας μεγαλύτερη ευελιξία στη διαχείριση αρχείων. Οι υπόλοιπες επιλογές περιορίζουν λανθασμένα το MPI I/O σε μία μόνο κατηγορία ή το θεωρούν ανύπαρκτο.

Θέμα 11

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

a. Το SSE λειτουργεί με καταχωρητές 128-bits, ενώ το AVX λειτουργεί με καταχωρητές 256-bits. Το AVX επεκτείνει το SSE· δεν είναι υποσύνολό του, και υποστηρίζει και αυτό κινητή υποδιαστολή.

Θέμα 12

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

a. _mm_load_ps(). Φορτώνει 4 αριθμούς float (128 bits) σε καταχωρητή SSE. Το _mm_load_pd() φορτώνει doubles και τα _mm256_* είναι AVX.

Θέμα 13

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

b. Προσθέτει τα στοιχεία δύο διανυσμάτων με 8 floats το καθένα. Το __m256 και το _mm256_add_ps αντιστοιχούν σε 8 στοιχεία απλής ακρίβειας float.

Θέμα 14

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

d. Όλα τα παραπάνω. Το _mm_load_ps() απαιτεί στοίχιση 16 bytes, το _mm256_load_ps() 32 bytes, και οι μη στοιχισμένες προσπελάσεις μπορούν να προκαλέσουν σφάλμα ή πτώση απόδοσης.

Θέμα 15

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

Σφάλματα: 1. Το _mm_load_ps()/_mm_store_ps() απαιτούν στοίχιση 16 bytes. Με data + 2*thread_num, τα μονά νήματα δείχνουν σε διεύθυνση με στοίχιση 8 bytes, άρα μη έγκυρη aligned πρόσβαση. 2. Κάθε νήμα φορτώνει/αποθηκεύει 4 floats, αλλά το βήμα είναι 2 floats. Άρα οι περιοχές επικαλύπτονται και υπάρχει data race. 3. Με nThreads=16, το νήμα 15 γράφει/διαβάζει πέρα από τα 32 στοιχεία, εκτός ορίων.

Διόρθωση: χρησιμοποιούμε 8 νήματα και βήμα 4 floats, ώστε κάθε νήμα να επεξεργάζεται ένα aligned, μη επικαλυπτόμενο διάνυσμα 4 floats:

int nThreads = 8; // 32/4
float *data;
if (posix_memalign((void **)&data, 16, 32*sizeof(float)) != 0) {
    /* handle error */
}
for (int i = 0; i < 32; ++i) data[i] = drand48();

omp_set_num_threads(nThreads);
#pragma omp parallel
{
    int tid = omp_get_thread_num();
    float * const myData = data + 4*tid;
    __m128 c4 = _mm_set1_ps((float)M_PI);
    __m128 r4 = _mm_load_ps(myData);
    __m128 a4 = _mm_mul_ps(c4, _mm_mul_ps(r4, r4));
    _mm_store_ps(myData, a4);
}

Θέμα 16

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

a. kernel<<<10, 256>>>(); Η σύνταξη είναι kernel<<<αριθμός_blocks, νήματα_ανά_block>>>(...). Η c είναι επίσης συντακτικά αποδεκτή ως kernel<<<10, 256, 0>>>() αν δηλωθεί 0 δυναμική shared μνήμη, αλλά η τυπική/ζητούμενη μορφή είναι η a.

Θέμα 17

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

b. Καθορίζει μια συνάρτηση που τρέχει στην κάρτα γραφικών και μπορεί να κληθεί από το κύριο σύστημα (host). Το __global__ δηλώνει CUDA kernel που εκτελείται στη συσκευή και καλείται από τον host.

Θέμα 18

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

c. cudaMemcpy() με cudaMemcpyHostToDevice. Αυτή η κλήση αντιγράφει δεδομένα από τη μνήμη του host στη μνήμη της συσκευής.

Θέμα 19

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

b. Συγχρονίζει όλα τα νήματα σε ένα μπλοκ. Το __syncthreads() είναι barrier για τα νήματα του ίδιου block, όχι για ολόκληρο το grid ή το warp.

Θέμα 20

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

b. 0 2 4 6. Το kernel εκτελείται με threadIdx.x = 0,1,2,3, άρα γράφει a[tid] = tid*2, δηλαδή \(0,2,4,6\).

Θέμα 21

Αξιοπιστία: δύο ανεξάρτητες λύσεις συμφωνούν · εμπιστοσύνη λύτη: υψηλή

Σφάλματα: 1. Το b είναι δείκτης σε μνήμη συσκευής (device), αλλά ο βρόχος b[i] = i εκτελείται στον host. Η άμεση εγγραφή σε device pointer από host είναι μη έγκυρη. 2. Σε C++ συχνά απαιτείται cast στη cudaMalloc, π.χ. cudaMalloc((void**)&b, ...), και έλεγχος επιστροφής. 3. Αν το kernel υπολογίζει αποτελέσματα που θα χρησιμοποιήσει ο host, χρειάζεται αντιγραφή πίσω με cudaMemcpy(..., cudaMemcpyDeviceToHost). 4. Πρέπει να χρησιμοποιείται μοναδικό καθολικό index νήματος, π.χ. blockIdx.x*blockDim.x + threadIdx.x, και όχι μόνο threadIdx.x, αν θέλουμε επεξεργασία 512 στοιχείων. 5. Καλό είναι να προστεθεί cudaDeviceSynchronize() και έλεγχος σφαλμάτων πριν από το cudaFree.

Ενδεικτική διόρθωση: ```c global void kernel(int *array, int n) { int idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx < n) { // π.χ. array[idx] = array[idx] + 1; } }

int main() { int h_b[512]; int *d_b;

for (int i = 0; i < 512; ++i) h_b[i] = i;

cudaMalloc((void**)&d_b, 512 * sizeof(int));
cudaMemcpy(d_b, h_b, 512 * sizeof(int), cudaMemcpyHostToDevice);

kernel<<<16, 32>>>(d_b, 512);
cudaDeviceSynchronize();

cudaMemcpy(h_b, d_b, 512 * sizeof(int), cudaMemcpyDeviceToHost);
cudaFree(d_b);
return 0;

}

✔ Επιβεβαίωσε · ✖ Βρήκα λάθος


✖ Βρήκα λάθος στη μεταγραφή