#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <omp.h>
#include <string.h>
#include <math.h>
// ===== 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 <stdio.h>
#include <stdlib.h>
#include <omp.h>
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 <stdio.h>
#include <stdlib.h>
#include <omp.h>
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 <stdio.h>
#include <omp.h>
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 <stdio.h>
#include <omp.h>
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 <stdio.h>
#include <omp.h>
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 <stdio.h>
#include <omp.h>
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 <stdio.h>
#include <omp.h>
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 <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <omp.h>
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 <stdio.h>
#include <omp.h>
#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 <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <omp.h>
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 <stdio.h>
#include <stdlib.h>
#include <omp.h>
#include <time.h>
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 <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <omp.h>
#include <string.h>
#include <math.h>
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;
}
}
}
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 {
fprintf(stderr,"Unknown schedule type '%s', defaulting to static\n", schedule_type);
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;
}
}
// You can put other parallel work here without waiting for above loop to finish
}
// implicit barrier at the end of parallel region ensures completion before continuing
}
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 {
fprintf(stderr,"Unknown schedule type '%s', defaulting to static\n", schedule_type);
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;
}
}
// Other parallel work without waiting for above loop
}
// implicit barrier at parallel region end
}
int compare_matrices(double **M1, double **M2, int N) {
const double EPS = 1e-6;
for(int i=0; i<N; i++) {
for(int j=0; j<N; j++) {
if(fabs(M1[i][j] - M2[i][j]) > 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<num_sizes; idx++) {
int N = sizes[idx];
printf("\n=== Matrix size: %d x %d ===\n", N, N);
double **A = allocate_matrix(N);
double **B = allocate_matrix(N);
double **C_seq = allocate_matrix(N);
double **C_par_nc = allocate_matrix(N);
double **C_par_c = allocate_matrix(N);
initialize_matrix(A, N);
initialize_matrix(B, N);
double start, end;
// Sequential
printf("Running 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);
// Parallel no collapse (static schedule)
printf("Running parallel multiplication without collapse (static schedule)...\n");
start = omp_get_wtime();
matmul_parallel_no_collapse(A, B, C_par_nc, N, "static", chunk_size);
end = omp_get_wtime();
printf("Parallel no collapse time: %f seconds\n", end - start);
printf("Correctness check: %s\n", compare_matrices(C_seq, C_par_nc, N) ? "PASS" : "FAIL");
// Parallel with collapse(2) (static schedule)
printf("Running parallel multiplication with collapse(2) (static schedule)...\n");
start = omp_get_wtime();
matmul_parallel_collapse(A, B, C_par_c, N, "static", chunk_size);
end = omp_get_wtime();
printf("Parallel collapse(2) time: %f seconds\n", end - start);
printf("Correctness check: %s\n", compare_matrices(C_seq, C_par_c, N) ? "PASS" : "FAIL");
free_matrix(A, N);
free_matrix(B, N);
free_matrix(C_seq, N);
free_matrix(C_par_nc, N);
free_matrix(C_par_c, N);
}
return 0;
}
//export OMP_NUM_THREADS=8
🤖 MPI Distributed Memory Code Snippets
p7: Total Sum using MPI_Reduce
This program demonstrates the MPI_Reduce collective operation to sum the randomly generated "mangoes picked" by each MPI process (robot) on the root process (rank 0).
c
//p7
#include<stdio.h>
#include<stdlib.h>
#include<mpi.h>
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 <mpi.h>
#include <stdio.h>
#include <stdlib.h>
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 <mpi.h>
#include <stdio.h>
#include <stdlib.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);
// 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 <mpi.h>
#include <stdio.h>
#include <stdlib.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);
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;
}