1+ /**
2+ * Copyright (C) 2020 TU Dresden
3+ *
4+ * This benchmark implements a parallel all pairs shortest path algorithm. In
5+ * order to split the workload, the large input matrix of size graphSize x
6+ * graphSize is split into smaller blocks of size blockSize x blockSize. Each of
7+ * the worker reactors (ApspFloydWarshallBlock) processes one of these blocks.
8+ * The worker reactors are organized in the same matrix pattern, replication the
9+ * structure of the blocks within the large input matrix. Each of the workers
10+ * operates on its local block data, and sends results to all other workers in
11+ * the same column or in the same row. The data from the neighbors is then used
12+ * to compute the next intermediate result and to update the local state
13+ * accordingly.
14+ *
15+ * @author Christian Menard
16+ * @author Hannes Klein
17+ * @author Johannes Hayeß
18+ */
19+
20+ target Rust {
21+ build-type : Release,
22+ cargo-features: [ "cli" ],
23+ rust-include: [ "../lib/matrix.rs", "../lib/pseudo_random.rs"],
24+ };
25+
26+ import BenchmarkRunner from "../lib/BenchmarkRunner.lf";
27+
28+ reactor ApspFloydWarshallBlock(
29+ bank_index: usize(0),
30+ row_index: usize(0),
31+ graphSize: usize(300),
32+ blockSize: usize(50),
33+ dimension: usize(6),
34+ verbose: bool(false)
35+ ) {
36+ state bank_index(bank_index);
37+ state row_index(row_index);
38+ state graph_size(graphSize);
39+ state block_size(blockSize);
40+ state dimension(dimension);
41+ state verbose(verbose);
42+
43+ state num_neighbors: usize({=2 * (dimension - 1)=});
44+ state row_offset: usize({=row_index * blockSize=}); // row offset of the block of this reactor
45+ state col_offset: usize({=bank_index * blockSize=}); // column offset of the block of this reactor
46+
47+ state k: usize(0); // iteration counter
48+ state reportedFinish: bool(false);
49+
50+ input start: Matrix<u64>;
51+
52+ input[dimension] fromRow: Arc<Matrix<u64>>;
53+ input[dimension] fromCol: Arc<Matrix<u64>>;
54+
55+ output toNeighbors: Arc<Matrix<u64>>;
56+ output finished: unit;
57+
58+ logical action notifyNeighbors: Arc<Matrix<u64>>;
59+
60+ preamble {=
61+ use std::sync::Arc;
62+ use crate::matrix::Matrix;
63+
64+ fn get_element_at(
65+ row: usize,
66+ col: usize,
67+ row_ports: &ReadablePortBank<Arc<Matrix<u64>>>,
68+ col_ports: &ReadablePortBank<Arc<Matrix<u64>>>,
69+ ctx: &ReactionCtx,
70+ block_size: usize,
71+ row_index: usize,
72+ bank_index: usize,
73+ ) -> u64 {
74+ let dest_row = row / block_size;
75+ let dest_col = col / block_size;
76+ let local_row = row % block_size;
77+ let local_col = col % block_size;
78+
79+ if dest_row == row_index {
80+ ctx.use_ref_opt(&row_ports.get(dest_col), |r| {
81+ *r.get(local_row, local_col)
82+ }).unwrap()
83+ } else if dest_col == bank_index {
84+ ctx.use_ref_opt(&col_ports.get(dest_row), |c| {
85+ *c.get(local_row, local_col)
86+ }).unwrap()
87+ } else {
88+ eprintln!("Error: unexpected target location ({},{})", dest_col, dest_row);
89+ std::process::exit(2);
90+ }
91+
92+ }
93+ =}
94+
95+ reaction(start) -> notifyNeighbors {=
96+ // reset local state
97+ self.k = 0;
98+ self.reportedFinish = false;
99+
100+ // start execution
101+ let matrix = ctx.use_ref_opt(start, Clone::clone).unwrap();
102+ ctx.schedule_with_v(notifyNeighbors, Some(Arc::new(matrix)), Asap);
103+ =}
104+
105+ reaction(notifyNeighbors) -> toNeighbors {=
106+ //notify all neighbors
107+ ctx.set(toNeighbors, ctx.use_ref_opt(notifyNeighbors, Clone::clone).unwrap());
108+ =}
109+
110+ reaction(fromRow, fromCol) -> notifyNeighbors, finished {=
111+ // do nothing if complete
112+ if self.k == self.graph_size {
113+ return;
114+ }
115+
116+ // perform computation
117+ let mut matrix: Matrix<u64> = Matrix::new(self.block_size, self.block_size);
118+ let bs = self.block_size;
119+ let ri = self.row_index;
120+ let bi = self.bank_index;
121+
122+ for i in 0..self.block_size {
123+ for j in 0..self.block_size {
124+ let gi = self.row_offset + i;
125+ let gj = self.col_offset + j;
126+
127+ let result = get_element_at(gi, self.k, &fromRow, &fromCol, &ctx, bs, ri, bi) + get_element_at(self.k, gj, &fromRow, &fromCol, &ctx, bs, ri, bi);
128+ matrix.set(i, j, result.min(get_element_at(gi, gj, &fromRow, &fromCol, &ctx, bs, ri, bi)));
129+ }
130+ }
131+
132+ // increment iteration count
133+ self.k += 1;
134+
135+ if self.k == self.graph_size {
136+ if self.verbose && self.bank_index == 0 && self.row_index == 0 {
137+ // debugging and result checking
138+ for i in 0..self.block_size {
139+ let mut result = "".to_string();
140+ for j in 0..self.block_size {
141+ result = format!("{} {}", result, matrix.get(i, j));
142+ }
143+ info!("{}", result);
144+ }
145+ }
146+ ctx.set(finished, ());
147+ }
148+
149+ // send the result to all neighbors in the next iteration
150+ ctx.schedule_with_v(notifyNeighbors, Some(Arc::new(matrix)), Asap);
151+ =}
152+ }
153+
154+ reactor ApspRow(
155+ bank_index: usize(0),
156+ blockSize: usize(50),
157+ numNodes: usize(300),
158+ dimension: usize(6),
159+ dimension_sq: usize(36),
160+ verbose: bool(false)
161+ ) {
162+
163+ input start: Matrix<u64>;
164+ output[dimension] finished: unit;
165+
166+ input[dimension_sq] fromCol: Matrix<u64>;
167+ output[dimension] toCol: Matrix<u64>;
168+
169+ blocks = new[dimension] ApspFloydWarshallBlock(
170+ row_index=bank_index,
171+ blockSize=blockSize,
172+ graphSize=numNodes,
173+ dimension=dimension,
174+ verbose=verbose
175+ );
176+
177+ // connect all blocks within the row
178+ (blocks.toNeighbors)+ -> blocks.fromRow;
179+
180+ // block output to all column neighbours
181+ blocks.toNeighbors -> toCol;
182+ // block input from all column neighbours
183+ fromCol -> interleaved(blocks.fromCol);
184+
185+ // broadcast the incoming matrix to all blocks
186+ (start)+ -> blocks.start;
187+ // collect and forward finished signals from all blocks
188+ blocks.finished -> finished;
189+
190+ preamble {=
191+ use crate::matrix::Matrix;
192+ =}
193+ }
194+
195+ reactor ApspMatrix(
196+ blockSize: usize(50),
197+ numNodes: usize(300),
198+ dimension: usize(6),
199+ dimension_sq: usize(36),
200+ verbose: bool(false)
201+ ) {
202+ input start: Matrix<u64>;
203+ output[dimension_sq] finished: unit;
204+
205+ rows = new[dimension] ApspRow(blockSize=blockSize, numNodes=numNodes, dimension=dimension, dimension_sq=dimension_sq, verbose=verbose);
206+
207+ // broadcast the incoming matrix to all rows
208+ (start)+ -> rows.start;
209+ // collect and forward finished signals from all blocks
210+ rows.finished -> finished;
211+
212+ (rows.toCol)+ -> rows.fromCol;
213+
214+ preamble {=
215+ use crate::matrix::Matrix;
216+ =}
217+ }
218+
219+ main reactor (
220+ numIterations: usize(12),
221+ maxEdgeWeight: usize(100),
222+ blockSize: usize(50),
223+ numNodes: usize(300),
224+ verbose: bool(false)
225+ ) {
226+ state num_iterations(numIterations);
227+ state max_edge_weight(maxEdgeWeight);
228+ state block_size(blockSize);
229+ state num_nodes(numNodes);
230+ state verbose(verbose);
231+
232+ state graph_data: Matrix<u64>;
233+ state num_blocks_finished: usize(0);
234+
235+ runner = new BenchmarkRunner(num_iterations=numIterations);
236+ matrix = new ApspMatrix(
237+ blockSize=blockSize,
238+ numNodes=numNodes,
239+ dimension={=numNodes / blockSize=},
240+ dimension_sq={=(numNodes / blockSize)*(numNodes / blockSize)=},
241+ verbose=verbose
242+ );
243+
244+ reaction(startup) {=
245+ print_benchmark_info("ApspBenchmark");
246+ print_args!(
247+ "numIterations",
248+ self.num_iterations,
249+ "maxEdgeWeight",
250+ self.max_edge_weight,
251+ "numNodes",
252+ self.num_nodes,
253+ "blockSize",
254+ self.block_size
255+ );
256+ print_system_info();
257+
258+ self.graph_data = generate_graph(self.num_nodes as i64, self.max_edge_weight as i64);
259+ =}
260+
261+ reaction(runner.start) -> matrix.start {=
262+ // reset local state
263+ self.num_blocks_finished = 0;
264+
265+ // start execution
266+ ctx.set(matrix__start, self.graph_data);
267+ =}
268+
269+ reaction (matrix.finished) -> runner.finished {=
270+ for f in matrix__finished {
271+ if ctx.is_present(&f) {
272+ self.num_blocks_finished += 1;
273+ }
274+ }
275+ let dimension = self.num_nodes / self.block_size;
276+ if self.num_blocks_finished == dimension * dimension {
277+ ctx.set(runner__finished, ());
278+ }
279+ =}
280+
281+ preamble {=
282+ use crate::matrix::Matrix;
283+ use crate::{print_args,reactors::benchmark_runner::{print_system_info, print_benchmark_info}};
284+ use crate::pseudo_random::PseudoRandomGenerator;
285+
286+ fn generate_graph(n: i64, w: i64) -> Matrix<u64> {
287+ let random = PseudoRandomGenerator::from(n);
288+ let nu = n as usize;
289+ let mut local_data: Matrix<u64> = Matrix::new(nu, nu);
290+
291+ for i in 0..nu {
292+ for j in (i+1)..nu {
293+ let r = random.next_in_range(0..w).into() + 1;
294+ local_data.set(i, j, r);
295+ local_data.set(j, i, r);
296+ }
297+ }
298+
299+ local_data
300+ }
301+ =}
302+ }
0 commit comments