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

Παράλληλη Επεξεργασία — Ιανουάριος 2025 — Θέματα 2ο–3ο

Μεταγραφή από τα IMG_5630, IMG_5636 και IMG_5633 (Εξεταστική Ιανουαρίου 2025).

IMG_5630.jpg

Τμήμα Μηχ. Η/Υ και Πληροφορικής, Πανεπιστήμιο Πατρών

Παράλληλη Επεξεργασία

Εξεταστική Περίοδος: Ιανουαρίου 2025


Θέμα 1ο (3 μονάδες) [απαντήστε εδώ]

1.1 Η οδηγία single nowait του OpenMP:

  • [ ] a. περιέχει φράγμα εκτέλεσης
  • [ ] b. είναι ισοδύναμη με την οδηγία master
  • [ ] c. παρέχει αμοιβαίο αποκλεισμό
  • [ ] d. τίποτα από τα υπόλοιπα

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

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

1.3 Η εντολή export OMP_DYNAMIC=FALSE απενεργοποιεί:

  • [ ] a. τη δημιουργία και εκτέλεση tasks
  • [ ] b. την εκτέλεση των επαναλήψεων με δυναμικό τρόπο
  • [ ] c. την εκτέλεση των επαναλήψεων με στατικό τρόπο
  • [ ] d. τίποτα από τα υπόλοιπα

1.4 Για ποια συστήματα δεν είναι κατάλληλο το μοντέλο προγραμματισμού MPI;

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

1.5 Δίνεται ο παρακάτω κώδικας OpenMP.

int test_var = 1;

#pragma omp parallel num_threads(4) reduction(*:test_var)
{
    test_var = omp_get_thread_num() - omp_get_num_threads();
}
Ποια θα είναι η τιμή της μεταβλητής test_var μετά την εκτέλεση της παράλληλης περιοχής; Εξηγήστε. - [ ] a. τίποτα από τα παρακάτω - [ ] b. 1 - [ ] c. -10 - [ ] d. 12

1.6 Δίνεται ο παρακάτω κώδικας OpenMP.

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;
}
Στον παραπάνω κώδικα και συγκεκριμένα στο σημείο αναφοράς εντός του task, η μεταβλητή e είναι: - [ ] a. lastprivate - [ ] b. threadprivate - [ ] c. firstprivate - [ ] d. private

IMG_5636.jpg

Θέμα 2ο (3 μονάδες)

2.1 Δίνεται ο παρακάτω κώδικας OpenMP. 2.2 Δίνεται το παρακάτω τμήμα κώδικα MPI.
c<br>int A[12];<br>omp_set_num_threads(4);<br>#pragma omp parallel for schedule(static,3)<br>for (int i = 0; i < 12; i++) {<br> A[i] = omp_get_thread_num();<br>}<br> c<br>int rank, size;<br>MPI_Comm_rank(MPI_COMM_WORLD, &rank);<br>MPI_Comm_rank(MPI_COMM_WORLD, &size);<br><br>int var1 = rank + 1 - size;<br>int var2 = 0, var3 = 0;<br>MPI_Allreduce(&var1, &var2, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);<br>MPI_Reduce(&var2, &var3, 1, MPI_INT, MPI_PROD, 0, MPI_COMM_WORLD);<br><br>printf("%d : %d\n", rank, var3);<br>
a. Περιγράψτε τι κάνει ο παραπάνω παράλληλος κώδικας.
b. Ποιες θα είναι οι τελικές τιμές του διανύσματος A; Εξηγήστε.
a. Τι τιμές εκτυπώνει ο παραπάνω κώδικας όταν εκτελεστεί με 3 διεργασίες (mpirun -n 3 <executable>);
b. Εξηγήστε την απάντησή σας.

IMG_5633.jpg

Τμήμα Μηχ. Η/Υ και Πληροφορικής, Πανεπιστήμιο Πατρών

Θέμα 3ο (2 μονάδες)

Στον παρακάτω κώδικα, η συνάρτηση initialize θέτει τιμές σε ένα διάνυσμα Α διάστασης Ν, χρησιμοποιώντας τη εξαιρετικά γρήγορη συνάρτηση work, που είναι υλοποιημένη κάπου αλλού. Θεωρήστε πως η διάσταση Ν είναι ι[Λείπει από τη φωτογραφία: υπόλοιπο της πρότασης] a. Εξηγήστε πώς διαμοιράζονται οι επαναλήψεις του βρόγχου στα νήματα. b. Αναφέρετε πιθανά προβλήματα απόδοσης του κώδικα και προτείνετε μια λύση.

extern double work(int i);
void initialize(double *A, int N)
{
    #pragma omp parallel for schedule(dynamic, 3)
    for (int i = 0; i < N; i++) {
        A[i] = work(i);
    }
}

Θέμα 4ο (2 μονάδες)

Ο παρακάτω σειριακός κώδικας αρχικοποιεί το διάνυσμα Α διάστασης Ν και μετά υπολογίζει ένα ειδικό άθροισμα τω[Λείπει από τη φωτογραφία: ν στοιχείων] του. Θεωρήστε πως η διάσταση Ν μπορεί να είναι αυθαίρετα μεγάλη. Βελτιστοποιήστε και παραλληλοποιήστε απ[Λείπει από τη φωτογραφία: ό τον παρακάτω] κώδικα με χρήση του μοντέλου MPI.

/* Κώδικας */ /* Βοηθητικά στοιχεία του MPI */
#include <stdio.h> // printf
#include <stdlib.h> // malloc
#include <math.h> // sqrt

#define N (1024*1024)
// υλοποιημένη κάπου αλλού
extern double initialize(int i);

int main (int argc, char **argv)
{
    double b[2] = {5.0, 10.0};
    double *A = (double *)malloc(N*sizeof(double));

    for (int i = 0; i < N; i++) A[i] = initialize(i);

    Χρήστης#1320 = 0.0;
    for (int i = 0; i < N; i++)
        s += b[0]*sqrt(A[i]) + b[1];

    printf("sum=%lf\n", s);
    free(A);
    return 0;
}
int MPI_Init(int *argc, char ***argv);
int MPI_Finalize();
int MPI_Comm_size(MPI_Comm comm, int *size);
int MPI_Comm_rank(MPI_Comm comm, int *rank);
int MPI_Send(const void *buf, int count, MPI_Datatype datatype,
    int dest, int tag, MPI_Comm comm);
int MPI_Recv(void *buf, int count, MPI_Datatype datatype,
    int source, int tag, MPI_Comm comm, MPI_Status *s[Λείπει από τη φωτογραφία: ...]
int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype,
    int root, MPI_Comm comm);
int MPI_Reduce(void *sendbuf, void *recvbuf, int count,
    MPI_Datatype datatype, MPI_Op op, int root,
    MPI_Comm comm);
int MPI_Allreduce(void *sendbuf, void *recvbuf, int count,
    MPI_Datatype datatype, MPI_Op op,
    MPI_Comm comm);

Constants: MPI_COMM_WORLD
Elementary Datatypes: MPI_CHAR, MPI_SHORT, MPI_INT, MPI_LO[Λείπει από τη φωτογραφία: NG]
                      MPI_FLOAT, MPI_DOUBLE, MPI_BYTE
Reduction Operations: MPI_MAX, MPI_MIN, MPI_SUM, MPI_PROD

Διάρκεια Εξέτασης: 2 ώρες

Λύσεις (🟡 community)

Κατάσταση: 🟡 community Πηγή: χειρόγραφη λύση φοιτητή (φωτογραφία IMG_6261.jpg)

Προσοχή

Το μεταγραφικό σημειώνει session_hint 2025-06· η αντιστοίχιση στη συνεδρία 2025-01 γίνεται από τον κώδικα (A[12], schedule(static,3)) που εμφανίζεται στη συνεδρία. Τα ερωτήματα a) και b) της εκφώνησης είναι δυσανάγνωστα στη φωτογραφία· η λύση δεν μπορεί να ελεγχθεί πλήρως ως προς αυτά. Στο MPI μέρος, το var3 των rank 1 και 2 είναι 0 ως αρχική τιμή του καταχωρητή· το MPI_Reduce δεν ορίζει το recvbuf των μη-ριζικών διεργασιών, άρα το 0 δεν είναι αποτέλεσμα.

Λύσεις — Παράλληλη Επεξεργασία — Ιανουάριος 2025

IMG_6261.jpg

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();
}

[Σχήμα: αγκύλη που ομαδοποιεί δύο ερωτήματα] a) [δυσανάγνωστο] ο [δυσανάγνωστο]; b) Ποιες είναι οι [δυσανάγνωστο] του A;

Λύση

Χρησιμοποιούμε 4 threads. Το loop for μοιράζεται με schedule(static,3) = κάθε thread παίρνει 3 συνεχόμενες επαναλήψεις. Το omp_get_thread_num() επιστρέφει τον αριθμό του τρέχοντος thread. Ο πίνακας A [δυσανάγνωστο] που δείχνουν ποιο thread έγραψε σε κάθε θέση.

thread Επεξεργάζεται τις θέσεις A[i]
0 i=0, 1, 2
1 i=3, 4, 5
2 i=6, 7, 8
3 i=9, 10, 11

A[0]=0, A[1]=0, A[2]=0, A[3]=1, A[4]=1, A[5]=1, A[6]=2, A[7]=2, A[8]=2, A[9]=3, A[10]=3, A[11]=3

A = {0,0,0,1,1,1,2,2,2,3,3,3}

int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
int var1 = rank + 1 - size;
int var2 = 0, var3 = 0;
MPI_Allreduce(&var1, &var2, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
MPI_Reduce(&var2, &var3, 1, MPI_INT, MPI_PROD, 0, MPI_COMM_WORLD);
printf("%d : %d\n", rank, var3);

[Σχήμα: αγκύλη με σημείωση «[δυσανάγνωστο] 3 διεργασίες», «mpirun -n 3 » και «[δυσανάγνωστο]»]

var1 = rank + 1 − 3

rank var1
0 0 + 1 − 3 = −2
1 1 + 1 − 3 = −1
2 2 + 1 − 3 = 0

Βήμα 2: var2 = −2 + (−1) + 0 = −3. Άρα και τα 3 processes έχουν var2 = −3.

Βήμα 3: var3 = (−3)(−3)(−3) = −27. Μόνο το process με rank=0 παίρνει το αποτέλεσμα στο var3, στα άλλα δύο (rank=1,2) θα έχουν var3=0 (δεν συνεισφέρουν σε αυτά).

Βήμα 4: - rank=0 → −27 - rank=1 → 0 - rank=2 → 0

Η σειρά [δυσανάγνωστο] διαφέρει αναλόγως της εκτέλεσης.

Σημείωση: ολόκληρη η σελίδα είναι γραμμένη με το χέρι (και η εκφώνηση και η λύση), δεν υπάρχει τυπωμένο κείμενο. Σημείωση: η φωτογραφία περιέχει χειρόγραφες απαντήσεις φοιτητή.

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


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