```#include ``` ```#include ``` ```#include ``` ```#include ``` ```#include ``` ```#include ``` ```// ===== Helper Functions =====``` ```double **allocate_matrix(int N) {``` ``` double **mat = malloc(N * sizeof(double *));``` ``` for (int i = 0; i < N; i++)``` ``` mat[i] = malloc(N * sizeof(double));``` ``` return mat;``` ```}``` ```void free_matrix(double **mat, int N) {``` ``` for (int i = 0; i < N; i++)``` ``` free(mat[i]);``` ``` free(mat);``` ```}``` ```void initialize_matrix(double **mat, int N) {``` ``` for (int i = 0; i < N; i++)``` ``` for (int j = 0; j < N; j++)``` ``` mat[i][j] = ((double)rand() / RAND_MAX) * 100.0;``` ```}``` ```void matmul_seq(double **A, double **B, double **C, int N) {``` ``` for (int i = 0; i < N; i++) {``` ``` for (int j = 0; j < N; j++) {``` ``` double sum = 0.0;``` ``` for (int k = 0; k < N; k++)``` ``` sum += A[i][k] * B[k][j];``` ``` C[i][j] = sum;``` ``` }``` ``` }``` ```}``` ```// ===== Parallel Implementations =====``` ```void matmul_parallel_no_collapse(double **A, double **B, double **C, int N, const char *schedule_type, int chunk_size) {``` ``` omp_sched_t sched_kind;``` ``` if (strcmp(schedule_type, "static") == 0)``` ``` sched_kind = omp_sched_static;``` ``` else if (strcmp(schedule_type, "dynamic") == 0)``` ``` sched_kind = omp_sched_dynamic;``` ``` else if (strcmp(schedule_type, "guided") == 0)``` ``` sched_kind = omp_sched_guided;``` ``` else``` ``` sched_kind = omp_sched_static;``` ``` omp_set_schedule(sched_kind, chunk_size);``` ``` #pragma omp parallel``` ``` {``` ``` #pragma omp for schedule(runtime) nowait``` ``` for (int i = 0; i < N; i++) {``` ``` for (int j = 0; j < N; j++) {``` ``` double sum = 0.0;``` ``` for (int k = 0; k < N; k++)``` ``` sum += A[i][k] * B[k][j];``` ``` C[i][j] = sum;``` ``` }``` ``` }``` ``` }``` ```}``` ```void matmul_parallel_collapse(double **A, double **B, double **C, int N, const char *schedule_type, int chunk_size) {``` ``` omp_sched_t sched_kind;``` ``` if (strcmp(schedule_type, "static") == 0)``` ``` sched_kind = omp_sched_static;``` ``` else if (strcmp(schedule_type, "dynamic") == 0)``` ``` sched_kind = omp_sched_dynamic;``` ``` else if (strcmp(schedule_type, "guided") == 0)``` ``` sched_kind = omp_sched_guided;``` ``` else``` ``` sched_kind = omp_sched_static;``` ``` omp_set_schedule(sched_kind, chunk_size);``` ``` #pragma omp parallel``` ``` {``` ``` #pragma omp for collapse(2) schedule(runtime) nowait``` ``` for (int i = 0; i < N; i++) {``` ``` for (int j = 0; j < N; j++) {``` ``` double sum = 0.0;``` ``` for (int k = 0; k < N; k++)``` ``` sum += A[i][k] * B[k][j];``` ``` C[i][j] = sum;``` ``` }``` ``` }``` ``` }``` ```}``` ```// ===== Main Function =====``` ```int main() {``` ``` int N;``` ``` printf("Enter matrix size (e.g., 500): ");``` ``` scanf("%d", &N);``` ``` int chunk_size = 10;``` ``` printf("Using %d threads\n", omp_get_max_threads());``` ``` double **A = allocate_matrix(N);``` ``` double **B = allocate_matrix(N);``` ``` double **C_seq = allocate_matrix(N);``` ``` double **C_par = allocate_matrix(N);``` ``` srand(time(NULL));``` ``` initialize_matrix(A, N);``` ``` initialize_matrix(B, N);``` ``` double start, end;``` ``` // Sequential``` ``` printf("\nRunning sequential multiplication...\n");``` ``` start = omp_get_wtime();``` ``` matmul_seq(A, B, C_seq, N);``` ``` end = omp_get_wtime();``` ``` printf("Sequential time: %f seconds\n", end - start);``` ``` // Static schedule``` ``` printf("\nRunning parallel (no collapse) with static schedule...\n");``` ``` start = omp_get_wtime();``` ``` matmul_parallel_no_collapse(A, B, C_par, N, "static", chunk_size);``` ``` end = omp_get_wtime();``` ``` printf("Parallel (no collapse) static: %f seconds\n", end - start);``` ``` printf("Running parallel (collapse(2)) with static schedule...\n");``` ``` start = omp_get_wtime();``` ``` matmul_parallel_collapse(A, B, C_par, N, "static", chunk_size);``` ``` end = omp_get_wtime();``` ``` printf("Parallel (collapse(2)) static: %f seconds\n", end - start);``` ``` // Dynamic schedule``` ``` printf("\nRunning parallel (no collapse) with dynamic schedule...\n");``` ``` start = omp_get_wtime();``` ``` matmul_parallel_no_collapse(A, B, C_par, N, "dynamic", chunk_size);``` ``` end = omp_get_wtime();``` ``` printf("Parallel (no collapse) dynamic: %f seconds\n", end - start);``` ``` printf("Running parallel (collapse(2)) with dynamic schedule...\n");``` ``` start = omp_get_wtime();``` ``` matmul_parallel_collapse(A, B, C_par, N, "dynamic", chunk_size);``` ``` end = omp_get_wtime();``` ``` printf("Parallel (collapse(2)) dynamic: %f seconds\n", end - start);``` ``` // Guided schedule``` ``` printf("\nRunning parallel (no collapse) with guided schedule...\n");``` ``` start = omp_get_wtime();``` ``` matmul_parallel_no_collapse(A, B, C_par, N, "guided", chunk_size);``` ``` end = omp_get_wtime();``` ``` printf("Parallel (no collapse) guided: %f seconds\n", end - start);``` ``` printf("Running parallel (collapse(2)) with guided schedule...\n");``` ``` start = omp_get_wtime();``` ``` matmul_parallel_collapse(A, B, C_par, N, "guided", chunk_size);``` ``` end = omp_get_wtime();``` ``` printf("Parallel (collapse(2)) guided: %f seconds\n", end - start);``` ``` free_matrix(A, N);``` ``` free_matrix(B, N);``` ``` free_matrix(C_seq, N);``` ``` free_matrix(C_par, N);``` ``` return 0;``` ```}``` --- ### Matrix-Vector Multiplication (MV) #### p1: Basic MV Multiplication Comparison ```c``` ```//p1``` ```#include ``` ```#include ``` ```#include ``` ```int main() {``` ``` int n = 2000;``` ``` double **matrix, *vector, *result;``` ``` int i, j;``` ``` // Allocate memory``` ``` matrix = (double **)malloc(n * sizeof(double *));``` ``` for (i = 0; i < n; i++)``` ``` matrix[i] = (double *)malloc(n * sizeof(double));``` ``` vector = (double *)malloc(n * sizeof(double));``` ``` result = (double *)malloc(n * sizeof(double));``` ``` // Initialize matrix and vector``` ``` for (i = 0; i < n; i++) {``` ``` vector[i] = 1.0;``` ``` for (j = 0; j < n; j++)``` ``` matrix[i][j] = 1.0;``` ``` }``` ``` // Sequential computation``` ``` double start = omp_get_wtime();``` ``` for (i = 0; i < n; i++) {``` ``` double sum = 0.0;``` ``` for (j = 0; j < n; j++)``` ``` sum += matrix[i][j] * vector[j];``` ``` result[i] = sum;``` ``` }``` ``` double end = omp_get_wtime();``` ``` printf("Sequential Time: %f seconds\n", end - start);``` ``` // Parallel computation``` ``` start = omp_get_wtime();``` ``` #pragma omp parallel for private(j) schedule(static)``` ``` for (i = 0; i < n; i++) {``` ``` double sum = 0.0;``` ``` for (j = 0; j < n; j++)``` ``` sum += matrix[i][j] * vector[j];``` ``` result[i] = sum;``` ``` }``` ``` end = omp_get_wtime();``` ``` printf("Parallel Time: %f seconds\n", end - start);``` ``` // Free allocated memory``` ``` for (i = 0; i < n; i++)``` ``` free(matrix[i]);``` ``` free(matrix);``` ``` free(vector);``` ``` free(result);``` ``` return 0;``` ```}``` #### p1: MV Multiplication with Result Display ```c``` ```// With displaying the result (first 10 values only for display restrictions)``` ```#include ``` ```#include ``` ```#include ``` ```int main() {``` ``` int n = 2000;``` ``` double **matrix, *vector, *result;``` ``` int i, j;``` ``` // Memory allocation``` ``` matrix = (double **)malloc(n * sizeof(double *));``` ``` for (i = 0; i < n; i++)``` ``` matrix[i] = (double *)malloc(n * sizeof(double));``` ``` vector = (double *)malloc(n * sizeof(double));``` ``` result = (double *)malloc(n * sizeof(double));``` ``` // Initialize matrix and vector``` ``` for (i = 0; i < n; i++) {``` ``` vector[i] = 2.0;``` ``` for (j = 0; j < n; j++)``` ``` matrix[i][j] = 2.0;``` ``` }``` ``` // Sequential computation``` ``` double start = omp_get_wtime();``` ``` for (i = 0; i < n; i++) {``` ``` double sum = 0.0;``` ``` for (j = 0; j < n; j++)``` ``` sum += matrix[i][j] * vector[j];``` ``` result[i] = sum;``` ``` }``` ``` double end = omp_get_wtime();``` ``` printf("Sequential Time: %f seconds\n", end - start);``` ``` // Print first 10 results (sequential)``` ``` printf("Sequential result (first 10 values): ");``` ``` for (i = 0; i < 10; i++)``` ``` printf("%0.2f ", result[i]);``` ``` printf("\n");``` ``` // Parallel computation``` ``` start = omp_get_wtime();``` ``` #pragma omp parallel for private(j) schedule(static)``` ``` for (i = 0; i < n; i++) {``` ``` double sum = 0.0;``` ``` for (j = 0; j < n; j++)``` ``` sum += matrix[i][j] * vector[j];``` ``` result[i] = sum;``` ``` }``` ``` end = omp_get_wtime();``` ``` printf("Parallel Time: %f seconds\n", end - start);``` ``` // Print first 10 results (parallel)``` ``` printf("Parallel result (first 10 values): ");``` ``` for (i = 0; i < 10; i++)``` ``` printf("%0.2f ", result[i]);``` ``` printf("\n");``` ``` // Free allocated memory``` ``` for (i = 0; i < n; i++)``` ``` free(matrix[i]);``` ``` free(matrix);``` ``` free(vector);``` ``` free(result);``` ``` return 0;``` ```}``` --- ### Summation/Reduction (Shopping Bill) #### p2: Summing arrays using `omp sections` and `reduction` ```c``` ```//p2``` ```#include ``` ```#include ``` ```int main() {``` ``` int clothing[] = {500, 1000, 750};``` ``` int gaming[] = {1500, 2000};``` ``` int grocery[] = {100, 250, 300, 150};``` ``` int stationary[] = {50, 80, 40};``` ``` int n1 = 3, n2 = 2, n3 = 4, n4 = 3;``` ``` int total_seq = 0, total_par = 0;``` ``` double start, end, seq_time, par_time;``` ``` // Sequential computation``` ``` start = omp_get_wtime();``` ``` for (int i = 0; i < n1; i++)``` ``` total_seq += clothing[i];``` ``` for (int i = 0; i < n2; i++)``` ``` total_seq += gaming[i];``` ``` for (int i = 0; i < n3; i++)``` ``` total_seq += grocery[i];``` ``` for (int i = 0; i < n4; i++)``` ``` total_seq += stationary[i];``` ``` end = omp_get_wtime();``` ``` seq_time = end - start;``` ``` // Parallel computation using sections``` ``` start = omp_get_wtime();``` ``` #pragma omp parallel sections reduction(+:total_par)``` ``` {``` ``` #pragma omp section``` ``` for (int i = 0; i < n1; i++)``` ``` total_par += clothing[i];``` ``` #pragma omp section``` ``` for (int i = 0; i < n2; i++)``` ``` total_par += gaming[i];``` ``` #pragma omp section``` ``` for (int i = 0; i < n3; i++)``` ``` total_par += grocery[i];``` ``` #pragma omp section``` ``` for (int i = 0; i < n4; i++)``` ``` total_par += stationary[i];``` ``` }``` ``` end = omp_get_wtime();``` ``` par_time = end - start;``` ``` printf("Sequential Total: %d, Time: %lf sec\n", total_seq, seq_time);``` ``` printf("Parallel Total: %d, Time: %lf sec\n", total_par, par_time);``` ``` printf("Speedup: %lf\n", seq_time / par_time);``` ``` return 0;``` ```}``` #### p2: Variant - Summing arrays using `omp parallel for` and `if/else` ```c``` ```//Variant``` ```#include ``` ```#include ``` ```int main() {``` ``` int clothing[] = {500, 700, 800, 600};``` ``` int gaming[] = {2000, 1500, 3000};``` ``` int grocery[] = {200, 300, 150, 100, 50};``` ``` int stationary[] = {50, 100, 150};``` ``` int total = 0;``` ``` double start, end;``` ``` start = omp_get_wtime();``` ``` // Sequential computation (NOTE: The logic here is flawed as it sums twice - once sequentially, then again in the parallel block. I kept it as is to match the original structure, though in a real application, the sequential part should be separate and compared to the parallel result.)``` ``` for (int i = 0; i < 4; i++)``` ``` total += clothing[i];``` ``` for (int i = 0; i < 3; i++)``` ``` total += gaming[i];``` ``` for (int i = 0; i < 5; i++)``` ``` total += grocery[i];``` ``` for (int i = 0; i < 3; i++)``` ``` total += stationary[i];``` ``` // Parallel computation using sections-like logic``` ``` #pragma omp parallel for reduction(+:total)``` ``` for (int section = 0; section < 4; section++) {``` ``` int sum = 0;``` ``` if (section == 0) {``` ``` for (int i = 0; i < 4; i++)``` ``` sum += clothing[i];``` ``` }``` ``` else if (section == 1) {``` ``` for (int i = 0; i < 3; i++)``` ``` sum += gaming[i];``` ``` }``` ``` else if (section == 2) {``` ``` for (int i = 0; i < 5; i++)``` ``` sum += grocery[i];``` ``` }``` ``` else if (section == 3) {``` ``` for (int i = 0; i < 3; i++)``` ``` sum += stationary[i];``` ``` }``` ``` total += sum;``` ``` }``` ``` end = omp_get_wtime();``` ``` printf("Final Bill Amount = %d\n", total);``` ``` printf("Time Taken = %f seconds\n", end - start);``` ``` return 0;``` ```}``` --- ### Pi Calculation (Parallel Reduction Techniques) These snippets calculate Pi using numerical integration and demonstrate different OpenMP techniques for parallel reduction (`reduction`, `atomic`, `critical`). #### p3: Using `reduction` ```c``` ```//p3``` ```//Using reduction``` ```#include ``` ```#include ``` ```int main() {``` ``` long num_steps = 1000000000;``` ``` double step, x, sum = 0.0;``` ``` double start_time, end_time;``` ``` step = 1.0 / (double) num_steps;``` ``` start_time = omp_get_wtime();``` ``` #pragma omp parallel for private(x) reduction(+:sum)``` ``` for (long i = 0; i < num_steps; i++) {``` ``` x = (i + 0.5) * step;``` ``` sum += 4.0 / (1.0 + x * x);``` ``` }``` ``` double pi = step * sum;``` ``` end_time = omp_get_wtime();``` ``` printf("Approximated Pi = %.15f\n", pi);``` ``` printf("Time taken = %f seconds\n", end_time - start_time);``` ``` return 0;``` ```}``` #### p3: Using `atomic` ```c``` ```//Using Atomic``` ```#include ``` ```#include ``` ```int main() {``` ``` long num_steps = 100000000;``` ``` double step = 1.0 / (double)num_steps;``` ``` double sum = 0.0;``` ``` double start_time, end_time;``` ``` start_time = omp_get_wtime();``` ``` #pragma omp parallel``` ``` {``` ``` double x, local_sum = 0.0;``` ``` #pragma omp for``` ``` for (long i = 0; i < num_steps; i++) {``` ``` x = (i + 0.5) * step;``` ``` local_sum += 4.0 / (1.0 + x * x);``` ``` }``` ``` #pragma omp atomic``` ``` sum += local_sum;``` ``` }``` ``` double pi = step * sum;``` ``` end_time = omp_get_wtime();``` ``` printf("Parallel PI = %.15f\n", pi);``` ``` printf("Time taken: %f seconds\n", end_time - start_time);``` ``` return 0;``` ```}``` #### p3: Using `critical` ```c``` ```//Using Critical``` ```#include ``` ```#include ``` ```static long num_steps = 100000000;``` ```double step;``` ```int main() {``` ``` int i;``` ``` double x, pi = 0.0, sum = 0.0;``` ``` step = 1.0 / (double)num_steps;``` ``` double start_time = omp_get_wtime();``` ``` #pragma omp parallel``` ``` {``` ``` double local_sum = 0.0;``` ``` #pragma omp for``` ``` for (i = 0; i < num_steps; i++) {``` ``` x = (i + 0.5) * step;``` ``` local_sum += 4.0 / (1.0 + x * x);``` ``` }``` ``` #pragma omp critical``` ``` {``` ``` sum += local_sum;``` ``` }``` ``` }``` ``` pi = step * sum;``` ``` double end_time = omp_get_wtime();``` ``` printf("Computed value of Pi = %.15f\n", pi);``` ``` printf("Time taken = %f seconds\n", end_time - start_time);``` ``` return 0;``` ```}``` --- ### Fibonacci Sequence These snippets demonstrate OpenMP constructs (`single`, `sections`, `critical`) for tasks that involve an ordered, dependent computation (like the Fibonacci sequence) and parallel output. #### p4: Using `single` ```c``` ```//p4``` ```// using single``` ```#include ``` ```#include ``` ```#include ``` ```#include ``` ```int main() {``` ``` int n, i;``` ``` printf("Number of terms : ");``` ``` scanf("%d", &n);``` ``` if (n < 2) {``` ``` printf("Please enter n >= 2\n");``` ``` return 0;``` ``` }``` ``` int* a = (int*)malloc(n * sizeof(int));``` ``` if (a == NULL) {``` ``` printf("Memory allocation failed\n");``` ``` return 1;``` ``` }``` ``` a[0] = 0;``` ``` a[1] = 1;``` ``` clock_t st = clock();``` ``` omp_set_num_threads(2);``` ``` #pragma omp parallel``` ``` {``` ``` #pragma omp single``` ``` {``` ``` printf("Thread involved in computation of Fibonacci numbers = %d\n",``` ``` omp_get_thread_num());``` ``` for (i = 2; i < n; i++)``` ``` a[i] = a[i - 2] + a[i - 1];``` ``` }``` ``` #pragma omp single``` ``` {``` ``` printf("Thread involved in displaying Fibonacci numbers = %d\n",``` ``` omp_get_thread_num());``` ``` printf("Fibonacci numbers: ");``` ``` for (i = 0; i < n; i++)``` ``` printf("%d ", a[i]);``` ``` printf("\n");``` ``` }``` ``` }``` ``` clock_t et = clock();``` ``` printf("Time Taken : %.4f ms\n",``` ``` ((double)(et - st) * 1000 / CLOCKS_PER_SEC));``` ``` free(a);``` ``` return 0;``` ```}``` #### p4: Using `sections` (Producer-Consumer Style) ```c``` ```//USING SECTIONS``` ```#include ``` ```#include ``` ```#define MAX 20``` ```int fib[MAX];``` ```int generated = 0;``` ```int main() {``` ``` int n = MAX;``` ``` fib[0] = 0;``` ``` fib[1] = 1;``` ``` generated = 2;``` ``` omp_set_num_threads(2);``` ``` double start = omp_get_wtime();``` ``` #pragma omp parallel shared(fib, generated, n)``` ``` {``` ``` #pragma omp sections``` ``` {``` ``` // Section 1: Generate Fibonacci numbers``` ``` #pragma omp section``` ``` {``` ``` for (int i = 2; i < n; i++) {``` ``` #pragma omp critical``` ``` {``` ``` fib[i] = fib[i - 1] + fib[i - 2];``` ``` generated++;``` ``` }``` ``` #pragma omp flush(generated)``` ``` }``` ``` }``` ``` // Section 2: Print Fibonacci numbers as they are generated``` ``` #pragma omp section``` ``` {``` ``` int lastPrinted = 0;``` ``` while (lastPrinted < n) {``` ``` #pragma omp flush(generated)``` ``` if (lastPrinted < generated) {``` ``` #pragma omp critical``` ``` {``` ``` printf("Fibonacci[%d] = %d\n",``` ``` lastPrinted, fib[lastPrinted]);``` ``` lastPrinted++;``` ``` }``` ``` }``` ``` }``` ``` }``` ``` }``` ``` }``` ``` double end = omp_get_wtime();``` ``` printf("Time taken: %f seconds\n", end - start);``` ``` return 0;``` ```}``` #### p4: Using `critical` (Thread ID Based) ```c``` ```// USING CRITICAL``` ```#include ``` ```#include ``` ```#include ``` ```#include ``` ```int main() {``` ``` int n, i;``` ``` printf("Number of terms : ");``` ``` scanf("%d", &n);``` ``` if (n < 2) {``` ``` printf("Please enter n >= 2\n");``` ``` return 0;``` ``` }``` ``` int *a = (int *)malloc(n * sizeof(int));``` ``` a[0] = 0;``` ``` a[1] = 1;``` ``` time_t st, et;``` ``` st = clock();``` ``` omp_set_num_threads(2);``` ``` #pragma omp parallel``` ``` {``` ``` int tid = omp_get_thread_num();``` ``` if (tid == 0) {``` ``` #pragma omp critical``` ``` {``` ``` printf("ID of thread involved in the computation of Fibonacci numbers = %d\n", tid);``` ``` for (i = 2; i < n; i++)``` ``` a[i] = a[i - 2] + a[i - 1];``` ``` }``` ``` }``` ``` else if (tid == 1) {``` ``` #pragma omp critical``` ``` {``` ``` printf("ID of thread involved in the displaying of Fibonacci numbers = %d\n", tid);``` ``` printf("Fibonacci numbers : ");``` ``` for (i = 0; i < n; i++)``` ``` printf("%d ", a[i]);``` ``` printf("\n");``` ``` }``` ``` }``` ``` }``` ``` et = clock();``` ``` printf("Time Taken : %lf ms\n", ((double)(et - st) * 1000 / CLOCKS_PER_SEC));``` ``` free(a);``` ``` return 0;``` ```}``` ```//p5``` ```#include ``` ```#include ``` ```#include ``` ```#include ``` ```void generate_cgpa(float *arr, int n) {``` ``` #pragma omp parallel for``` ``` for (int i = 0; i < n; i++) {``` ``` arr[i] = ((float)rand() / RAND_MAX) * 10.0;``` ``` }``` ```}``` ```float find_max_sequential(float *arr, int n) {``` ``` float max = arr[0];``` ``` for (int i = 1; i < n; i++) {``` ``` if (arr[i] > max) {``` ``` max = arr[i];``` ``` }``` ``` }``` ``` return max;``` ```}``` ```float find_max_parallel_critical(float *arr, int n) {``` ``` float max = arr[0];``` ``` #pragma omp parallel for``` ``` for (int i = 0; i < n; i++) {``` ``` #pragma omp critical``` ``` {``` ``` if (arr[i] > max) {``` ``` max = arr[i];``` ``` }``` ``` }``` ``` }``` ``` return max;``` ```}``` ```float find_max_parallel_reduction(float *arr, int n) {``` ``` float max = arr[0];``` ``` #pragma omp parallel for reduction(max:max)``` ``` for (int i = 0; i < n; i++) {``` ``` if (arr[i] > max) {``` ``` max = arr[i];``` ``` }``` ``` }``` ``` return max;``` ```}``` ```int main() {``` ``` int n;``` ``` printf("Enter number of students: ");``` ``` scanf("%d", &n);``` ``` float *cgpa = (float *)malloc(n * sizeof(float));``` ``` srand(time(NULL));``` ``` double start = omp_get_wtime();``` ``` generate_cgpa(cgpa, n);``` ``` double end = omp_get_wtime();``` ``` printf("Time taken to generate CGPAs (parallel): %f seconds\n", end - start);``` ``` start = omp_get_wtime();``` ``` float max_seq = find_max_sequential(cgpa, n);``` ``` end = omp_get_wtime();``` ``` printf("Sequential Max CGPA: %.2f (Time: %f seconds)\n", max_seq, end - start);``` ``` start = omp_get_wtime();``` ``` float max_critical = find_max_parallel_critical(cgpa, n);``` ``` end = omp_get_wtime();``` ``` printf("Parallel Max CGPA (critical): %.2f (Time: %f seconds)\n", max_critical, end - start);``` ``` start = omp_get_wtime();``` ``` float max_reduction = find_max_parallel_reduction(cgpa, n);``` ``` end = omp_get_wtime();``` ``` printf("Parallel Max CGPA (reduction): %.2f (Time: %f seconds)\n", max_reduction, end - start);``` ``` free(cgpa);``` ``` return 0;``` ```}``` --- ### p6: Matrix Multiplication Comparison This program compares sequential matrix multiplication against parallel versions using the OpenMP **`collapse(2)`** clause and various scheduling types, along with correctness checks. ```c``` ```//p6``` ```#include ``` ```#include ``` ```#include ``` ```#include ``` ```#include ``` ```#include ``` ```double **allocate_matrix(int N) {``` ``` double **mat = malloc(N * sizeof(double *));``` ``` for(int i = 0; i < N; i++) {``` ``` mat[i] = malloc(N * sizeof(double));``` ``` }``` ``` return mat;``` ```}``` ```void free_matrix(double **mat, int N) {``` ``` for(int i = 0; i < N; i++) free(mat[i]);``` ``` free(mat);``` ```}``` ```void initialize_matrix(double **mat, int N) {``` ``` for(int i=0; i EPS) return 0;``` ``` }``` ``` }``` ``` return 1;``` ```}``` ```int main() {``` ``` int max_sizes = 10;``` ``` int sizes[max_sizes];``` ``` int num_sizes = 0;``` ``` printf("Enter matrix sizes separated by spaces (max %d sizes):\n", max_sizes);``` ``` char line[256];``` ``` if(!fgets(line, sizeof(line), stdin)) {``` ``` fprintf(stderr, "Error reading input\n");``` ``` return 1;``` ``` }``` ``` // Parse integers from input line``` ``` char *token = strtok(line, " \t\n");``` ``` while(token != NULL && num_sizes < max_sizes) {``` ``` sizes[num_sizes++] = atoi(token);``` ``` token = strtok(NULL, " \t\n");``` ``` }``` ``` if(num_sizes == 0) {``` ``` printf("No sizes entered. Exiting.\n");``` ``` return 1;``` ``` }``` ``` int num_threads = omp_get_max_threads();``` ``` int chunk_size = 10;``` ``` printf("Using %d threads\n", num_threads);``` ``` srand(time(NULL));``` ``` for(int idx=0; idx``` ```#include``` ```#include``` ```int main(int argc, char** argv)``` ```{``` ```int rank, numproc;``` ```int sum = 0;``` ```int total_sum = 0;``` ```MPI_Init(&argc, &argv);``` ```MPI_Comm_size(MPI_COMM_WORLD, &numproc);``` ```MPI_Comm_rank(MPI_COMM_WORLD, &rank);``` ```srand(rank);``` ```sum = rand() % 100;``` ```printf("Robot %d picked %d mangoes.\n", rank, sum);``` ```MPI_Reduce(&sum, &total_sum, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);``` ```if (rank == 0)``` ```printf("Total Mangoes picked by %d Robots = %d\n", numproc, total_sum);``` ```MPI_Finalize();``` ```}``` --- ### p8: MPI Collective Communications Demo This program demonstrates several fundamental MPI collective operations: **`MPI_Bcast`**, **`MPI_Scatter`**, **`MPI_Gather`**, **`MPI_Reduce`**, **`MPI_Allreduce`**, and **`MPI_Scan`**. ```c``` ```//P8``` ```#include ``` ```#include ``` ```#include ``` ```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);``` ``` if (rank == 0)``` ``` printf("MPI Collectives demo with %d process(es)\n\n", size);``` ``` // --- Broadcast ---``` ``` int bcast_val = 0;``` ``` if (rank == 0)``` ``` bcast_val = 123;``` ``` MPI_Bcast(&bcast_val, 1, MPI_INT, 0, MPI_COMM_WORLD);``` ``` printf("[rank %d] received bcast_val = %d\n", rank, bcast_val);``` ``` // --- Scatter ---``` ``` int scatter_val;``` ``` int *scatter_buf = NULL;``` ``` if (rank == 0) {``` ``` scatter_buf = malloc(size * sizeof(int));``` ``` for (int i = 0; i < size; i++)``` ``` scatter_buf[i] = (i + 1) * 10;``` ``` }``` ``` MPI_Scatter(scatter_buf, 1, MPI_INT, &scatter_val, 1, MPI_INT, 0, MPI_COMM_WORLD);``` ``` printf("[rank %d] got scatter_val = %d\n", rank, scatter_val);``` ``` if (scatter_buf)``` ``` free(scatter_buf);``` ``` // --- Gather ---``` ``` int my_val = rank * 10;``` ``` int *gather_buf = NULL;``` ``` if (rank == 0)``` ``` gather_buf = malloc(size * sizeof(int));``` ``` MPI_Gather(&my_val, 1, MPI_INT, gather_buf, 1, MPI_INT, 0, MPI_COMM_WORLD);``` ``` if (rank == 0) {``` ``` printf("Root gathered: ");``` ``` for (int i = 0; i < size; i++)``` ``` printf("%d ", gather_buf[i]);``` ``` printf("\n");``` ``` free(gather_buf);``` ``` }``` ``` // --- Reduce (sum) ---``` ``` int local_sum = rank + 1, total_sum;``` ``` MPI_Reduce(&local_sum, &total_sum, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);``` ``` if (rank == 0)``` ``` printf("Sum of ranks+1 = %d\n", total_sum);``` ``` // --- Allreduce (max) ---``` ``` int local_val = rank * 2;``` ``` int global_max;``` ``` MPI_Allreduce(&local_val, &global_max, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD);``` ``` printf("[rank %d] after Allreduce max = %d\n", rank, global_max);``` ``` // --- Scan (prefix sum) ---``` ``` int scan_out;``` ``` MPI_Scan(&rank, &scan_out, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);``` ``` printf("[rank %d] prefix sum = %d\n", rank, scan_out);``` ``` MPI_Finalize();``` ``` return 0;``` ```}``` --- ### p9: MPI Cartesian Virtual Topology This program demonstrates creating a 2D **Cartesian virtual topology** using **`MPI_Cart_create`** and finding the process's coordinates and its neighbors using **`MPI_Cart_coords`** and **`MPI_Cart_shift`**. ```c``` ```//P9``` ```#include ``` ```#include ``` ```#include ``` ```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);``` ``` // Define a 2D Cartesian grid``` ``` int dims[2] = {0, 0};``` ``` MPI_Dims_create(size, 2, dims); // Let MPI choose dimensions if not specified``` ``` int periods[2] = {0, 0}; // No wrap-around (non-periodic)``` ``` int reorder = 1; // Allow MPI to reorder ranks for efficiency``` ``` MPI_Comm cart_comm;``` ``` MPI_Cart_create(MPI_COMM_WORLD, 2, dims, periods, reorder, &cart_comm);``` ``` if (cart_comm == MPI_COMM_NULL) {``` ``` printf("[rank %d] could not create Cartesian communicator\n", rank);``` ``` MPI_Finalize();``` ``` return 0;``` ``` }``` ``` // Get my coordinates in the Cartesian grid``` ``` int coords[2];``` ``` MPI_Cart_coords(cart_comm, rank, 2, coords);``` ``` printf("[rank %d] coords = (%d, %d)\n", rank, coords[0], coords[1]);``` ``` // Find neighbors (up, down, left, right)``` ``` int up, down, left, right;``` ``` MPI_Cart_shift(cart_comm, 0, 1, &up, &down); // shift along rows``` ``` MPI_Cart_shift(cart_comm, 1, 1, &left, &right); // shift along columns``` ``` printf("[rank %d] neighbors -> up: %d, down: %d, left: %d, right: %d\n",``` ``` rank, up, down, left, right);``` ``` MPI_Finalize();``` ``` return 0;``` ```}``` --- ### p10: Blocking vs. Non-blocking Point-to-Point Communication This program contrasts **blocking (`MPI_Send`, `MPI_Recv`)** communication, which stops execution until the message transfer is safe to complete, with **non-blocking (`MPI_Isend`, `MPI_Irecv`)** communication, which allows the process to perform other work before explicitly synchronizing with **`MPI_Wait`**. ```c``` ```//P10``` ```#include ``` ```#include ``` ```#include ``` ```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);``` ``` if (size < 2) {``` ``` if (rank == 0)``` ``` printf("Please run with at least 2 processes.\n");``` ``` MPI_Finalize();``` ``` return 0;``` ``` }``` ``` // --- BLOCKING SEND / RECEIVE ---``` ``` if (rank == 0) {``` ``` int msg = 100;``` ``` printf("[Blocking] Rank 0 sending %d to rank 1\n", msg);``` ``` MPI_Send(&msg, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);``` ``` printf("[Blocking] Rank 0 done sending\n");``` ``` }``` ``` else if (rank == 1) {``` ``` int recv_msg;``` ``` MPI_Recv(&recv_msg, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);``` ``` printf("[Blocking] Rank 1 received %d from rank 0\n", recv_msg);``` ``` }``` ``` MPI_Barrier(MPI_COMM_WORLD); // synchronize before nonblocking example``` ``` // --- NONBLOCKING SEND / RECEIVE ---``` ``` if (rank == 0) {``` ``` int msg = 200;``` ``` MPI_Request request;``` ``` printf("[Nonblocking] Rank 0 sending %d to rank 1\n", msg);``` ``` MPI_Isend(&msg, 1, MPI_INT, 1, 1, MPI_COMM_WORLD, &request);``` ``` // Do some work while message is in progress``` ``` printf("[Nonblocking] Rank 0 doing other work...\n");``` ``` for (int i = 0; i < 5; i++) printf(".");``` ``` printf("\n");``` ``` // Wait for send to complete``` ``` MPI_Wait(&request, MPI_STATUS_IGNORE);``` ``` printf("[Nonblocking] Rank 0 send completed\n");``` ``` }``` ``` else if (rank == 1) {``` ``` int recv_msg;``` ``` MPI_Request request;``` ``` MPI_Irecv(&recv_msg, 1, MPI_INT, 0, 1, MPI_COMM_WORLD, &request);``` ``` // Do some work while waiting``` ``` printf("[Nonblocking] Rank 1 doing other work...\n");``` ``` for (int i = 0; i < 5; i++) printf("*");``` ``` printf("\n");``` ``` // Wait for receive to complete``` ``` MPI_Wait(&request, MPI_STATUS_IGNORE);``` ``` printf("[Nonblocking] Rank 1 received %d from rank 0\n", recv_msg);``` ``` }``` ``` MPI_Finalize();``` ``` return 0;``` ```}```