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

Παράλληλη Επεξεργασία — Ιούνιος 2025 — Θέματα 1°–4° (και στιγμιότυπα 24/6)

Μεταγραφή από τις φωτογραφίες Ιουνίου 2025 (IMG_20250625_) και τα στιγμιότυπα 2025-06-24_.

IMG_20250625_150958.jpg

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

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

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

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

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

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

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

1.4 Σε ποια συστήματα μπορεί να χρησιμοποιηθεί το μοντέλο προγραμματισμού OpenMP;

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

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

int var = 0;
#pragma omp parallel num_threads(4) reduction(max:var)
{
    var = omp_get_thread_num() - omp_get_num_threads();
}

Ποια θα είναι η τιμή της μεταβλητής test_var μετά την εκτέλεση της παράλληλης περιοχής; Εξηγήστε.

[sic: η εκφώνηση αναφέρει τη μεταβλητή test_var, ενώ ο κώδικας ορίζει var]

  • [ ] a. τίποτα από τα παρακάτω
  • [ ] b. -4
  • [ ] c. 3
  • [ ] d. 4

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, η μεταβλητή c είναι:

  • [ ] a. lastprivate
  • [ ] b. threadprivate
  • [ ] c. firstprivate
  • [ ] d. private

2025-06-24_10.32.08.png

1.2 Ποιο από τα παρακάτω δεν είναι τμήμα του προγραμματιστικού μοντέλου OpenMP;

  • [ ] a. Μεταγλωττιστής
  • [ ] b. Οδηγίες προς τον μεταγλωττιστή
  • [ ] c. Βιβλιοθήκη χρόνου εκτέλεσης
  • [ ] d. Μεταβλητές συστήματος

2025-06-24_12.14.42.png

1.3 Η εντολή export OMP_DYNAMIC=FALSE απενεργοποιεί: - [ ] a. τη δημιουργία και εκτέλεση tasks - [ ] b. την εκτέλεση των επαναλήψεων με δυναμικό τρόπο - [ ] c. την εκτέλεση των επαναλήψεων με στατικό τρόπο - [ ] d. τίποτα από τα υπόλοιπα

Σημείωση: η φωτογραφία περιέχει χειρόγραφες απαντήσεις φοιτητή.

2025-06-24_12.04.26.png

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

Σημείωση: η φωτογραφία περιέχει χειρόγραφες απαντήσεις φοιτητή.

2025-06-24_13.20.52.png

1.2 Η μέγιστη ονομαστική απόδοση ενός υπολογιστικού συστήματος δεν εξαρτάται από: - [ ] a. το μέγεθος της κρυφής μνήμης - [ ] b. τον αριθμό των πυρήνων - [ ] c. τη συχνότητα ρολογιού - [ ] d. την ακρίβεια των πράξεων κινητής υποδιαστολής

1.4 Σε ποια συστήματα δεν μπορεί να εκτελ[δυσανάγνωστο] [δυσανάγνωστο] μοντέλου προγραμ[δυσανάγνωστο]

[Λείπει από τη φωτογραφία: το υπόλοιπο της εκφώνησης του 1.4 και οι επιλογές απάντησης]

Σημείωση: η φωτογραφία περιέχει χειρόγραφες απαντήσεις φοιτητή.

2025-06-24_18.11.48.png

1.2 Η συλλογική (collective) επικοινωνία στο MPI, σε σχέση με την υλοποίηση των αντίστοιχων λειτουργιών χρησιμοποιώντας μόνο συναρτήσεις επικοινωνίας «σημείο-προς-σημείο» (point-to-point): [0,5] - [ ] a. Υποχρεώνει το πρόγραμμα MPI να μεταφέρει περισσότερα δεδομένα μεταξύ διεργασιών - [ ] b. Κάνει το πρόγραμμα MPI πιο ευανάγνωστο και πιο αποτελεσματικό στη μεταφορά δεδομένων - [ ] c. Κάνει το πρόγραμμα MPI πιο γρήγορο στο τμήμα εκείνο που πραγματοποιεί τους υπολογισμούς - [ ] d. Όλα τα υπόλοιπα

Απάντηση:

Σημείωση: η φωτογραφία περιέχει χειρόγραφες απαντήσεις φοιτητή.

IMG_20250625_151000.jpg

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

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

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

a. Περιγράψτε τι κάνει ο παραπάνω παράλληλος κώδικας.

b. Ποιες θα είναι οι τελικές τιμές του διανύσματος Α; Εξηγήστε.

2.2 Δίνεται το παρακάτω τμήμα κώδικα MPI.

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

a. Τι τιμές εκτυπώνει ο παραπάνω κώδικας όταν εκτελεστεί με 4 διεργασίες (mpirun -n 4 <executable>);

b. Εξηγήστε την απάντησή σας.

IMG_20250625_151006.jpg

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

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

Στον παρακάτω κώδικα, η συνάρτηση initialize θέτει τιμές σε ένα διάνυσμα A διάστασης N χρησιμοποιώντας την thread-safe και χρονοβόρα συνάρτηση work, που είναι υλοποιημένη κάπου αλλού.

a. Εξηγήστε πώς διαμοιράζονται οι επαναλήψεις του βρόχου στα νήματα.

b. Γράψτε μία ισοδύναμη υλοποίηση του παράλληλου κώδικα που θα βασίζεται στο μοντέλο εργασιών (tasks) του OpenMP.

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

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

Ο παρακάτω σειριακός κώδικας αρχικοποιεί το διάνυσμα A διάστασης N και μετά υπολογίζει ένα ειδικό άθροισμα των στοιχείων του. Θεωρήστε πως η διάσταση N μπορεί να είναι αυθαίρετα μεγάλη. Βελτιστοποιήστε και παραλληλοποιήστε αποδοτικά τον κώδικα με χρήση του μοντέλου MPI.

[Σχήμα: δύο στήλες. Αριστερά ο κώδικας, δεξιά τα βοηθητικά στοιχεία MPI. Το πλήρες κείμενο του Θέματος 4° βρίσκεται στο IMG_20250625_151007.jpg.]

/* Κώδικας */
#include <stdio.h>  // printf
#include <stdlib.h> // malloc
#include <math.h>   // sqrt

#define N (1024*1024)

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

[Λείπει από τη φωτογραφία: το υπόλοιπο του κώδικα (συνάρτηση main)]

[Λείπει από τη φωτογραφία: το υπόλοιπο των βοηθητικών στοιχείων MPI, από το MPI_Recv και κάτω]

Σημείωση: στη φωτογραφία φαίνεται αντίστροφο κείμενο της πίσω όψης του φύλλου, το οποίο δεν μεταγράφεται.

IMG_20250625_151007.jpg

[στο πάνω άκρο της φωτογραφίας φαίνεται κομμένο τμήμα προηγούμενου κώδικα: A[i] = work(i); και }]

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

Ο παρακάτω σειριακός κώδικας αρχικοποιεί το διάνυσμα A διάστασης N και μετά υπολογίζει ένα ειδικό άθροισμα των στοιχείων του. Θεωρήστε πως η διάσταση N μπορεί να είναι αυθαίρετα μεγάλη. Βελτιστοποιήστε και παραλληλοποιήστε αποδοτικά τον κώδικα με χρήση του μοντέλου 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 = 4.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*sqrt(A[i]);

  printf("sum=%lf\n", s);
  free(A);
  return 0;
}
/* Βοηθητικά στοιχεία του MPI */
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 *status);
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_LONG, MPI_FLOAT, MPI_DOUBLE, MPI_BYTE

Reduction Operations: MPI_MAX, MPI_MIN, MPI_SUM, MPI_PROD

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

Λύσεις (⚪ AI, μη επαληθευμένο)

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

Προσοχή

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

AI λύσεις — 2025-06

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

1.1 (IMG_20250625_150958.jpg)

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

Επιλογή c: τίποτα από τα υπόλοιπα. Το single nowait εκτελεί το block από ένα μόνο νήμα, αλλά χωρίς το τελικό φράγμα. Δεν είναι ισοδύναμο με master (το master εκτελείται πάντα από το master thread), δεν παρέχει αμοιβαίο αποκλεισμό και, λόγω nowait, δεν περιέχει φράγμα.

1.2 (IMG_20250625_150958.jpg)

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

Επιλογή c: 10. Από τον νόμο Amdahl, αν το μη παραλληλοποιήσιμο κλάσμα είναι \(s=0.10\), η μέγιστη χρονοβελτίωση για άπειρα νήματα είναι \(1/s = 1/0.10 = 10\).

1.3 (IMG_20250625_150958.jpg)

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

Επιλογή c: τίποτα από τα υπόλοιπα. Το OMP_DYNAMIC=TRUE επιτρέπει στον runtime να προσαρμόζει δυναμικά το πλήθος των νημάτων. Δεν ενεργοποιεί αυτόματη παραλληλοποίηση από τον μεταγλωττιστή, ούτε dynamic χρονοδρομολόγηση επαναλήψεων, ούτε εμφωλευμένο παραλληλισμό.

1.4 (IMG_20250625_150958.jpg)

Αξιοπιστία: μία λύση AI, δεν έχει αντιπαραβληθεί με δεύτερη · εμπιστοσύνη λύτη: μέτρια

Επιλογή b: σε όλα τα υπόλοιπα. Με την παραδοχή ότι αναφερόμαστε σε συστήματα που παρέχουν κοινό χώρο διευθύνσεων, το OpenMP μπορεί να χρησιμοποιηθεί σε κοινή μνήμη, κατανεμημένη κοινή μνήμη και NUMA. Απαιτείται shared-memory μοντέλο, όχι κατ’ ανάγκη μόνο UMA.

1.5 (IMG_20250625_150958.jpg)

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

Επιλογή a: τίποτα από τα παρακάτω. Με 4 νήματα, τα tid = 0,1,2,3 δίνουν τιμές \(0-4=-4\), \(1-4=-3\), \(2-4=-2\), \(3-4=-1\). Η reduction(max:var) συνδυάζει και την αρχική τιμή var=0, άρα το τελικό αποτέλεσμα είναι \(\max(0,-4,-3,-2,-1)=0\). Το \(0\) δεν υπάρχει στις επιλογές b–d. Αν το test_var της εκφώνησης είναι κυριολεκτικά άλλη μεταβλητή, τότε δεν ορίζεται.

1.6 (IMG_20250625_150958.jpg)

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

Καμία από τις a–d: η c είναι shared. Δηλώνεται ρητά shared(c) στην παράλληλη περιοχή και οι μεταβλητές με shared attribute παραμένουν shared μέσα στο task. Το b, που είναι private στην παράλληλη περιοχή, θα ήταν firstprivate μέσα στο task. Εφόσον δεν υπάρχει επιλογή shared, το ερώτημα έχει λανθασμένες επιλογές.

1.2 (2025-06-24_10.32.08.png)

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

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

1.3 (2025-06-24_12.14.42.png)

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

Επιλογή d: τίποτα από τα υπόλοιπα. Το OMP_DYNAMIC=FALSE απενεργοποιεί τη δυναμική προσαρμογή του πλήθους των νημάτων. Δεν απενεργοποιεί tasks, ούτε καθορίζει αν οι επαναλήψεις εκτελούνται με δυναμικό ή στατικό τρόπο.

2025-06-24_12.04.26.png

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

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

1.2 (2025-06-24_13.20.52.png)

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

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

1.4 (2025-06-24_13.20.52.png)

Αξιοπιστία: λύθηκε με βάση τις φωτογραφίες του θέματος· μία λύση AI

Στην εικόνα 2025-06-24_13.20.52.png το κάτω μέρος περιέχει την αρχή της ερώτησης 1.4 («1.4 Σε ποια συστήματα δεν μπορεί να εκτελεστεί [..] μοντέλου προγραμμ[...]»), η οποία είναι κομμένη στο κάτω περιθώριο. Η ερώτηση είναι πανομοιότυπη με την ερώτηση 1.4 της εξέτασης Ιουνίου 2024 (θέμα Ομάδας Α, IMG_2052.jpg), όπου εμφανίζονται οι ίδιες ακριβώς ερωτήσεις 1.1–1.6:

«1.4 Σε ποια συστήματα δεν μπορεί να εκτελεστεί κώδικας του μοντέλου προγραμματισμού MPI: - a. Συστήματα κοινής μνήμης - b. Συστήματα κατανεμημένης μνήμης - c. Συστήματα κατανεμημένης κοινής μνήμης - d. Μπορεί να εκτελεστεί σε όλα τα παραπάνω»

Αιτιολόγηση: Το μοντέλο MPI (Message Passing Interface) έχει σχεδιαστεί πρωταρχικά για συστήματα κατανεμημένης μνήμης (distributed memory), αλλά οι σύγχρονες υλοποιήσεις του (όπως OpenMPI, MPICH) υποστηρίζουν πλήρως και συστήματα κοινής μνήμης (shared memory) μέσω ενδο-επεξεργαστικής επικοινωνίας / διαμοιραζόμενης μνήμης (IPC / POSIX shared memory), καθώς και συστήματα κατανεμημένης κοινής μνήμης (DSM). Επομένως, κώδικας MPI μπορεί να εκτελεστεί σε όλα τα προαναφερθέντα συστήματα.

Επιλογή: d. Μπορεί να εκτελεστεί σε όλα τα παραπάνω

1.2 (2025-06-24_18.11.48.png)

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

Επιλογή b. Οι συλλογικές συναρτήσεις MPI κάνουν συνήθως πιο ευανάγνωστο τον κώδικα και μπορούν να υλοποιηθούν με βελτιστοποιημένους αλγόριθμους επικοινωνίας, βελτιώνοντας τη μεταφορά δεδομένων. Δεν κάνουν κατ’ ανάγκη ταχύτερο το υπολογιστικό τμήμα ούτε μεταφέρουν υποχρεωτικά περισσότερα δεδομένα.

Θέμα 2° (IMG_20250625_151000.jpg)

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

2.1
α. Ο κώδικας θέτει 4 νήματα και μοιράζει τις 16 επαναλήψεις με schedule(static,2): chunks των 2 διαδοχικών επαναλήψεων κατανέμονται κυκλικά στα νήματα. Κάθε νήμα παίρνει 2 chunks, δηλαδή 4 επαναλήψεις, και γράφει στο A[i] το id του.

β. Τα chunks είναι: [0,1], [2,3], [4,5], [6,7], [8,9], [10,11], [12,13], [14,15]. Άρα: - Νήμα 0: i=0,1,8,9 - Νήμα 1: i=2,3,10,11 - Νήμα 2: i=4,5,12,13 - Νήμα 3: i=6,7,14,15

Επομένως: [ A=[0,0,1,1,2,2,3,3,0,0,1,1,2,2,3,3]. ]

2.2
α. Εκτυπώνει: 8.000000.

β. Αρχικά var1 = rank-1, άρα για ranks \(0,1,2,3\): \(-1,0,1,2\). Η MPI_Reduce με MPI_SUM στον root 0 δίνει \( -1+0+1+2=2 \). Η MPI_Bcast μεταδίδει το var2=2 σε όλες τις διεργασίες. Η MPI_Allreduce με MPI_SUM υπολογίζει \(2+2+2+2=8\) και το βάζει στο var3 όλων. Η διεργασία rank 1 τυπώνει var3=8.

Θέμα 3° (IMG_20250625_151006.jpg)

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

α. Με schedule(dynamic,2) ο βρόχος χωρίζεται σε chunks των 2 διαδοχικών επαναλήψεων (το τελευταίο chunk μπορεί να είναι μικρότερο). Κάθε νήμα παίρνει δυναμικά το επόμενο διαθέσιμο chunk μόλις τελειώσει το προηγούμενο. Έτσι επιτυγχάνεται καλύτερη εξισορρόπηση φόρτου όταν το work(i) έχει μεταβλητό χρόνο.

β. Ισοδύναμη υλοποίηση με tasks:

extern double work(int i);

void initialize(double *A, int N)
{
    #pragma omp parallel
    {
        #pragma omp single
        {
            for (int i = 0; i < N; i += 2) {
                #pragma omp task firstprivate(i)
                {
                    int end = i + 2;
                    if (end > N) end = N;
                    for (int j = i; j < end; ++j)
                        A[j] = work(j);
                }
            }
            #pragma omp taskwait
        }
    }
}
Το A είναι κοινόχρηστο. Κάθε task αναλαμβάνει ένα chunk έως 2 επαναλήψεων, αντίστοιχο με το schedule(dynamic,2).

Θέμα 4° (IMG_20250625_151007.jpg)

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

Η μερική εμφάνιση του Θέματος 4° στην IMG_20250625_151006.jpg είναι κομμένη· η πλήρης λύση δίνεται εδώ μία φορά.

Βελτιστοποίηση: αφού το A[i] χρησιμοποιείται μόνο μία φορά, δεν χρειάζεται να αποθηκεύσουμε ολόκληρο το διάνυσμα A. Κάθε διεργασία αναλαμβάνει ένα συνεχές block δεικτών, υπολογίζει τοπικό άθροισμα και στο τέλος γίνεται MPI_Reduce με MPI_SUM. Επειδή \(b=4.0\) είναι σταθερά, βγάζουμε το \(b\) έξω από το άθροισμα.

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

#define N (1024LL*1024LL)

extern double initialize(int i);

int main(int argc, char **argv)
{
    int rank, size;
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    const double b = 4.0;
    long long n = (long long)N;

    long long start = (long long)rank * n / size;
    long long end   = (long long)(rank + 1) * n / size;

    double local_sum = 0.0;
    for (long long i = start; i < end; ++i) {
        double a = initialize((int)i);
        local_sum += sqrt(a);
    }

    double total_sum = 0.0;
    MPI_Reduce(&local_sum, &total_sum, 1, MPI_DOUBLE, MPI_SUM, 0,
               MPI_COMM_WORLD);

    if (rank == 0) {
        printf("sum=%lf\n", b * total_sum);
    }

    MPI_Finalize();
    return 0;
}
Η διαμέριση start = rank*n/size, end = (rank+1)*n/size καλύπτει όλους τους δείκτες ακριβώς μία φορά, ακόμη και όταν η \(N\) δεν διαιρείται ακριβώς με το πλήθος διεργασιών. Αν η \(N\) μπορεί να ξεπεράσει το INT_MAX, πρέπει να αλλάξει αντίστοιχα και η υπογραφή της initialize σε long long.

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


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