use futures_util::stream::StreamExt;
use lamellar::array::prelude::*;
use matrixmultiply::sgemm;
use std::sync::Arc;
#[lamellar::AmData(Dist, Debug, ArrayOps, Copy, Clone, Default)]
struct Block {
j: usize,
k: usize,
}
#[lamellar::main]
fn main() {
let args: Vec<String> = std::env::args().collect();
let elem_per_pe = args
.get(1)
.and_then(|s| s.parse::<usize>().ok())
.unwrap_or_else(|| 200);
let world = lamellar::LamellarWorldBuilder::new().build();
let my_pe = world.my_pe();
let num_pes = world.num_pes();
let dim = elem_per_pe * num_pes;
let m = dim; let n = dim; let p = dim;
let a = LocalLockArray::<f32>::new(&world, m * n, Distribution::Block).block(); let b = LocalLockArray::<f32>::new(&world, n * p, Distribution::Block).block(); let c = AtomicArray::<f32>::new(&world, m * p, Distribution::Block).block(); a.dist_iter_mut()
.enumerate()
.for_each(|(i, x)| *x = i as f32)
.block();
b.dist_iter_mut()
.enumerate()
.for_each(move |(i, x)| {
let row = i / dim;
let col = i % dim;
if row == col {
*x = 1 as f32
} else {
*x = 0 as f32;
}
})
.block();
c.dist_iter_mut().for_each(|x| x.store(0.0)).block();
world.barrier();
let a = a.into_read_only().block();
let b = b.into_read_only().block();
let num_gops = ((2 * dim * dim * dim) - dim * dim) as f64 / 1_000_000_000.0; let blocksize = dim / num_pes;
let m_blks = m / blocksize; let m_blks_pe = m_blks / num_pes;
let n_blks = n / blocksize; let p_blks = p / blocksize;
println! {"n: {}, m: {}, p: {}, blocksize: {}, m_blks: {}, n_blks: {}, p_blks: {}, m_blks_pe: {}, num_gops: {}", n, m, p, blocksize, m_blks, n_blks, p_blks, m_blks_pe, num_gops};
let nblks_array =
LocalLockArray::<Block>::new(&world, (n_blks * n_blks) * num_pes, Distribution::Block)
.block();
nblks_array
.dist_iter_mut()
.enumerate()
.for_each(move |(g_i, x)| {
let i = g_i % (n_blks * n_blks);
x.j = i / n_blks;
x.k = i % n_blks
})
.block();
let nblks_array = nblks_array.into_read_only().block();
let start = std::time::Instant::now();
let a_clone = a.clone();
let b_clone = b.clone();
let c_clone = c.clone();
nblks_array
.dist_iter()
.for_each_async(move |block| {
let b = b_clone.clone();
let a: ReadOnlyArray<f32> = a_clone.clone();
let c = c_clone.clone();
async move {
let j_blk = block.j;
let k_blk = block.k;
let b_block = b
.onesided_iter() .chunks(blocksize) .skip(k_blk * n_blks * blocksize + j_blk) .step_by(n_blks) .into_stream() .take(blocksize) .collect::<Vec<_>>()
.await; let mut b_block_vec = vec![0.0; blocksize * blocksize];
for (j, col) in b_block.iter().enumerate() {
let b_block_col = &mut b_block_vec[j * blocksize..(j + 1) * blocksize];
b_block_col.copy_from_slice(col.as_slice());
}
let b_block_vec = Arc::new(b_block_vec);
for i_blk in 0..m_blks_pe {
let b_block_vec = b_block_vec.clone();
let a_vec = a
.local_as_slice()
.chunks(blocksize)
.skip(i_blk * m_blks * blocksize + k_blk) .step_by(m_blks) .take(blocksize) .flatten()
.copied() .collect::<Vec<f32>>();
let mut c_vec = vec![0.0; blocksize * blocksize]; unsafe {
sgemm(
blocksize,
blocksize,
blocksize,
1.0,
a_vec.as_ptr(),
blocksize as isize,
1,
b_block_vec.as_ptr(),
1,
blocksize as isize,
0.0,
c_vec.as_mut_ptr(),
blocksize as isize,
1,
);
}
let c_slice = c.mut_local_data();
for row in 0..blocksize {
let row_offset = (i_blk * blocksize + row) * n;
for col in 0..blocksize {
let col_offset = j_blk * blocksize + col;
c_slice
.at(row_offset + col_offset)
.fetch_add(c_vec[row * blocksize + col]);
}
}
}
}
})
.block();
world.wait_all();
world.barrier();
let elapsed = start.elapsed().as_secs_f64();
if my_pe == 0 {
println!("Elapsed: {:?}", elapsed);
println!(
"blksize: {:?} elapsed {:?} Gflops: {:?} MBsend: {:?}",
blocksize,
elapsed,
num_gops / elapsed,
world.MB_sent()
);
}
}