Skip to content

Commit b9c9605

Browse files
committed
Add currently broken Apsp implementation
Why it's broken is explained here: lf-lang/lingua-franca#1228 (comment)
1 parent 9cbd28c commit b9c9605

File tree

1 file changed

+302
-0
lines changed

1 file changed

+302
-0
lines changed
Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
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

Comments
 (0)