-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
333 lines (277 loc) · 14 KB
/
Copy pathmain.cpp
File metadata and controls
333 lines (277 loc) · 14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/*
* Advanced Parallel Grid Processing with MPI
* ============================================
* Fayoum University – Parallel Computing Project 2026
*
* Usage:
* mpirun -np <P> ./parallel_grid [algorithm] [options]
*
* algorithm:
* heat – Heat Diffusion (stencil)
* matmul – Matrix Multiplication
* both – Run both sequentially (default)
* deadlock_demo – Show deadlock scenario and fix
* ring – Ring communication demo
* pipeline – Pipeline communication demo
*
* options (for heat):
* --grid <N> grid size (default 512)
* --iter <I> iterations (default 200)
* --nonblocking use non-blocking halo exchange
*
* options (for matmul):
* --fileA <path> matrix A file
* --fileB <path> matrix B file
* --size <N> random NxN matrix size (default 256)
* --verify verify result against sequential (only for small N)
*
* options (general):
* --generate-data generate test matrix files in ./data/
*/
#include <mpi.h>
#include <iostream>
#include <string>
#include <algorithm>
#include <cstdlib>
#include "algorithms/heat_diffusion.h"
#include "algorithms/matrix_multiplication.h"
#include "communication/blocking.h"
#include "communication/nonblocking.h"
#include "utils/file_loader.h"
#include "utils/timer.h"
// ─── argument helpers ────────────────────────────────────────────────────────
static bool hasArg(int argc, char** argv, const std::string& key) {
for (int i = 1; i < argc; i++)
if (key == argv[i]) return true;
return false;
}
static std::string getArg(int argc, char** argv, const std::string& key,
const std::string& def = "") {
for (int i = 1; i < argc - 1; i++)
if (key == argv[i]) return argv[i + 1];
return def;
}
// ─── deadlock demonstration ──────────────────────────────────────────────────
/*
* DEADLOCK SCENARIO
* -----------------
* Consider 2 ranks, both executing:
*
* MPI_Send(buf, N, MPI_DOUBLE, other, tag, comm); // BLOCKS until received
* MPI_Recv(buf, N, MPI_DOUBLE, other, tag, comm, &status);
*
* If MPI_Send uses synchronous mode (or the message is too large to buffer),
* BOTH ranks block on Send waiting for the other to call Recv — which never
* happens because both are stuck in Send. → DEADLOCK.
*
* FIX: use MPI_Sendrecv (or Isend/Irecv + Waitall, or alternating ordering).
*/
static void deadlockDemo(MPI_Comm comm) {
int rank, size;
MPI_Comm_rank(comm, &rank);
MPI_Comm_size(comm, &size);
if (size < 2) {
if (rank == 0) std::cout << "[DeadlockDemo] Need at least 2 processes.\n";
return;
}
if (rank == 0) {
std::cout << "\n╔══════════════════════════════════════════════╗\n";
std::cout << "║ DEADLOCK DEMONSTRATION ║\n";
std::cout << "╠══════════════════════════════════════════════╣\n";
std::cout << "║ Scenario: Both ranks call MPI_Send first. ║\n";
std::cout << "║ If messages exceed buffer → DEADLOCK. ║\n";
std::cout << "║ ║\n";
std::cout << "║ We demonstrate the FIXED version using ║\n";
std::cout << "║ MPI_Sendrecv which avoids the race condition. ║\n";
std::cout << "╚══════════════════════════════════════════════╝\n\n";
}
MPI_Barrier(comm);
// Only ranks 0 and 1 participate
if (rank > 1) return;
int other = 1 - rank;
const int N = 1024;
std::vector<double> send(N, (double)rank);
std::vector<double> recv(N, 0.0);
// ── FIXED version: MPI_Sendrecv ──────────────────────────────────────────
MPI_Status status;
MPI_Sendrecv(send.data(), N, MPI_DOUBLE, other, 99,
recv.data(), N, MPI_DOUBLE, other, 99,
comm, &status);
std::cout << "[Rank " << rank << "] Received " << N
<< " doubles from rank " << other
<< " (first value = " << recv[0] << ")\n";
MPI_Barrier(comm);
if (rank == 0)
std::cout << "[DeadlockDemo] Fixed version completed successfully.\n\n";
}
// ─── process group demo ──────────────────────────────────────────────────────
static void processGroupDemo(MPI_Comm worldComm) {
int rank, size;
MPI_Comm_rank(worldComm, &rank);
MPI_Comm_size(worldComm, &size);
// Split into two groups: even ranks and odd ranks
int color = rank % 2;
MPI_Comm subComm;
MPI_Comm_split(worldComm, color, rank, &subComm);
int subRank, subSize;
MPI_Comm_rank(subComm, &subRank);
MPI_Comm_size(subComm, &subSize);
// Each sub-group does its own reduction
double myVal = (double)(rank + 1);
double subSum = 0.0;
MPI_Reduce(&myVal, &subSum, 1, MPI_DOUBLE, MPI_SUM, 0, subComm);
if (subRank == 0) {
std::cout << "[ProcessGroup] " << (color == 0 ? "Even" : "Odd ")
<< " group (" << subSize << " ranks): sum = " << subSum << "\n";
}
MPI_Comm_free(&subComm);
}
// ─── ring demo ───────────────────────────────────────────────────────────────
static void ringDemo(MPI_Comm comm) {
int rank;
MPI_Comm_rank(comm, &rank);
double myToken = (double)(rank + 1) * 10.0;
double total = ringCommunication(myToken, comm);
if (rank == 0)
std::cout << "[RingDemo] Sum of all tokens = " << total << "\n";
}
// ─── pipeline demo ───────────────────────────────────────────────────────────
static void pipelineDemo(MPI_Comm comm) {
int rank, size;
MPI_Comm_rank(comm, &rank);
MPI_Comm_size(comm, &size);
double result = pipelineCommunication(100.0, comm);
if (rank == 0)
std::cout << "[PipelineDemo] Final value after pipeline = " << result
<< " (expected " << 100.0 + size*(size-1)/2.0 << ")\n";
}
// ─── interactive menu (rank 0 only, then broadcast choices) ─────────────────
struct UserChoices {
int algo; // 1=heat 2=matmul 3=both 4=deadlock 5=ring 6=pipeline
int gridSize;
int iterations;
int useNonBlocking;
int matSize;
int verify;
};
static UserChoices askUser() {
UserChoices c{};
std::cout << "\n";
std::cout << "╔══════════════════════════════════════════════════════╗\n";
std::cout << "║ Advanced Parallel Grid Processing with MPI ║\n";
std::cout << "║ Fayoum University – Parallel Computing 2026 ║\n";
std::cout << "╠══════════════════════════════════════════════════════╣\n";
std::cout << "║ Select an option: ║\n";
std::cout << "║ ║\n";
std::cout << "║ [1] Heat Diffusion (Category A – Grid) ║\n";
std::cout << "║ [2] Matrix Multiplication (Category B – Data) ║\n";
std::cout << "║ [3] Both Algorithms ║\n";
std::cout << "║ [4] Deadlock Demo ║\n";
std::cout << "║ [5] Ring Communication Demo ║\n";
std::cout << "║ [6] Pipeline Communication Demo ║\n";
std::cout << "╚══════════════════════════════════════════════════════╝\n";
std::cout << " Your choice: ";
std::cin >> c.algo;
// ── Heat options ─────────────────────────────────────────────────────────
if (c.algo == 1 || c.algo == 3) {
std::cout << "\n--- Heat Diffusion Settings ---\n";
std::cout << " Grid size N (NxN grid, e.g. 256 or 512): ";
std::cin >> c.gridSize;
std::cout << " Number of iterations (e.g. 100 or 200): ";
std::cin >> c.iterations;
int nb = 0;
std::cout << " Use non-blocking communication? (0=No 1=Yes): ";
std::cin >> nb;
c.useNonBlocking = nb;
} else {
c.gridSize = 256;
c.iterations = 100;
c.useNonBlocking = 0;
}
// ── MatMul options ───────────────────────────────────────────────────────
if (c.algo == 2 || c.algo == 3) {
std::cout << "\n--- Matrix Multiplication Settings ---\n";
std::cout << " Matrix size N (NxN random matrix, e.g. 128 or 256): ";
std::cin >> c.matSize;
int v = 0;
std::cout << " Verify result against sequential? (0=No 1=Yes, slow for N>256): ";
std::cin >> v;
c.verify = v;
} else {
c.matSize = 256;
c.verify = 0;
}
return c;
}
// ─── main ────────────────────────────────────────────────────────────────────
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);
// ── Only rank 0 shows the menu and reads input ───────────────────────────
UserChoices choices{};
if (rank == 0) {
std::cout << "\n Running with " << size << " MPI process(es)\n";
choices = askUser();
}
// ── Broadcast all choices to every rank so they agree ────────────────────
MPI_Bcast(&choices, sizeof(UserChoices), MPI_BYTE, 0, MPI_COMM_WORLD);
MPI_Barrier(MPI_COMM_WORLD);
Timer timer;
// ── Process groups (always shown) ────────────────────────────────────────
if (rank == 0) std::cout << "\n--- Process Group Demo (MPI_Comm_split) ---\n";
processGroupDemo(MPI_COMM_WORLD);
MPI_Barrier(MPI_COMM_WORLD);
// ── Algorithm dispatch ───────────────────────────────────────────────────
if (choices.algo == 4) {
deadlockDemo(MPI_COMM_WORLD);
} else if (choices.algo == 5) {
if (rank == 0) std::cout << "\n--- Ring Communication Demo ---\n";
ringDemo(MPI_COMM_WORLD);
} else if (choices.algo == 6) {
if (rank == 0) std::cout << "\n--- Pipeline Communication Demo ---\n";
pipelineDemo(MPI_COMM_WORLD);
} else {
// ── Heat Diffusion ───────────────────────────────────────────────────
if (choices.algo == 1 || choices.algo == 3) {
HeatConfig hcfg;
hcfg.gridSize = choices.gridSize;
hcfg.iterations = choices.iterations;
hcfg.useNonBlocking = (choices.useNonBlocking == 1);
if (rank == 0) std::cout << "\n--- Running Heat Diffusion ---\n";
MPI_Barrier(MPI_COMM_WORLD);
timer.start("HeatDiffusion");
runHeatDiffusion(hcfg, MPI_COMM_WORLD);
double heatMs = timer.stop("HeatDiffusion");
MPI_Barrier(MPI_COMM_WORLD);
if (rank == 0)
std::cout << "[Main] Heat Diffusion total wall time: " << heatMs << " ms\n";
}
// ── Matrix Multiplication ────────────────────────────────────────────
if (choices.algo == 2 || choices.algo == 3) {
MatMulConfig mcfg;
mcfg.randomN = choices.matSize;
mcfg.verify = (choices.verify == 1);
if (rank == 0) std::cout << "\n--- Running Matrix Multiplication ---\n";
MPI_Barrier(MPI_COMM_WORLD);
timer.start("MatMul");
runMatrixMultiplication(mcfg, MPI_COMM_WORLD);
double matMs = timer.stop("MatMul");
MPI_Barrier(MPI_COMM_WORLD);
if (rank == 0)
std::cout << "[Main] Matrix Multiplication total wall time: " << matMs << " ms\n";
}
// ── Always show comm demos after main algorithms ─────────────────────
MPI_Barrier(MPI_COMM_WORLD);
if (rank == 0) std::cout << "\n--- Communication Pattern Demos ---\n";
ringDemo(MPI_COMM_WORLD);
pipelineDemo(MPI_COMM_WORLD);
if (rank == 0) std::cout << "\n";
deadlockDemo(MPI_COMM_WORLD);
}
// ── Timing summary ───────────────────────────────────────────────────────
if (rank == 0) timer.report();
MPI_Finalize();
return 0;
}