use std::marker::PhantomData;
pub trait ActiveSet: Copy + 'static {
const MASK: u32;
}
#[derive(Copy, Clone)]
pub struct All;
impl ActiveSet for All {
const MASK: u32 = 0xFFFFFFFF;
}
#[derive(Copy, Clone)]
pub struct Warp<S: ActiveSet> {
_marker: PhantomData<S>,
}
impl<S: ActiveSet> Warp<S> {
pub fn new() -> Self {
Warp {
_marker: PhantomData,
}
}
}
#[derive(Debug)]
pub struct SharedMem<T: Copy, const SIZE: usize> {
data: [T; SIZE],
}
impl<T: Copy + Default, const SIZE: usize> SharedMem<T, SIZE> {
pub fn new() -> Self {
SharedMem {
data: [T::default(); SIZE],
}
}
pub fn from_slice(values: &[T]) -> Self
where
[T; SIZE]: Default,
{
let mut data = <[T; SIZE]>::default();
for (i, v) in values.iter().take(SIZE).enumerate() {
data[i] = *v;
}
SharedMem { data }
}
}
pub mod lane_parallel {
use super::*;
pub struct SharedRef<'a, T: Copy, const SIZE: usize> {
mem: &'a SharedMem<T, SIZE>,
}
impl<'a, T: Copy, const SIZE: usize> SharedRef<'a, T, SIZE> {
pub fn read(&self, index: usize) -> T {
assert!(index < SIZE, "index out of bounds");
self.mem.data[index]
}
}
pub struct SharedMut<'a, T: Copy, const SIZE: usize> {
mem: &'a mut SharedMem<T, SIZE>,
}
impl<'a, T: Copy, const SIZE: usize> SharedMut<'a, T, SIZE> {
pub fn read(&self, index: usize) -> T {
assert!(index < SIZE, "index out of bounds");
self.mem.data[index]
}
pub fn write(&mut self, index: usize, value: T) {
assert!(index < SIZE, "index out of bounds");
self.mem.data[index] = value;
}
}
pub fn borrow_shared<'a, T: Copy, const SIZE: usize>(
_warp: &Warp<All>,
mem: &'a SharedMem<T, SIZE>,
) -> SharedRef<'a, T, SIZE> {
SharedRef { mem }
}
pub fn borrow_mut<'a, T: Copy, const SIZE: usize>(
_warp: &Warp<All>,
mem: &'a mut SharedMem<T, SIZE>,
) -> SharedMut<'a, T, SIZE> {
SharedMut { mem }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_shared_borrow() {
let warp: Warp<All> = Warp::new();
let mut mem: SharedMem<i32, 32> = SharedMem::new();
{
let mut borrow = borrow_mut(&warp, &mut mem);
borrow.write(0, 42);
}
{
let borrow = borrow_shared(&warp, &mem);
assert_eq!(borrow.read(0), 42);
}
}
#[test]
fn test_multiple_shared_borrows() {
let warp: Warp<All> = Warp::new();
let mem: SharedMem<i32, 32> = SharedMem::new();
let b1 = borrow_shared(&warp, &mem);
let b2 = borrow_shared(&warp, &mem);
assert_eq!(b1.read(0), b2.read(0));
}
}
}
pub mod split {
use super::*;
pub struct LaneSlot<'a, T: Copy, const SIZE: usize> {
mem: &'a mut SharedMem<T, SIZE>,
owned_index: usize,
}
impl<'a, T: Copy, const SIZE: usize> LaneSlot<'a, T, SIZE> {
pub fn read(&self) -> T {
self.mem.data[self.owned_index]
}
pub fn write(&mut self, value: T) {
self.mem.data[self.owned_index] = value;
}
pub fn index(&self) -> usize {
self.owned_index
}
}
pub fn split_by_lane<'a, T: Copy, const SIZE: usize>(
_warp: &Warp<All>,
mem: &'a mut SharedMem<T, SIZE>,
lane: usize,
) -> LaneSlot<'a, T, SIZE> {
assert!(lane < SIZE, "lane must be within memory size");
LaneSlot {
mem,
owned_index: lane,
}
}
pub struct DisjointView<'a, T: Copy, const SIZE: usize> {
mem: &'a SharedMem<T, SIZE>,
owned_indices: Vec<usize>,
}
impl<'a, T: Copy, const SIZE: usize> DisjointView<'a, T, SIZE> {
pub fn read(&self, local_index: usize) -> Option<T> {
self.owned_indices
.get(local_index)
.map(|&i| self.mem.data[i])
}
}
pub fn split_by_predicate<'a, T: Copy, const SIZE: usize, F>(
_warp: &Warp<All>,
mem: &'a SharedMem<T, SIZE>,
pred: F,
) -> (DisjointView<'a, T, SIZE>, DisjointView<'a, T, SIZE>)
where
F: Fn(usize) -> bool,
{
let mut true_indices = Vec::new();
let mut false_indices = Vec::new();
for i in 0..SIZE {
if pred(i) {
true_indices.push(i);
} else {
false_indices.push(i);
}
}
(
DisjointView {
mem,
owned_indices: true_indices,
},
DisjointView {
mem,
owned_indices: false_indices,
},
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lane_exclusive_access() {
let warp: Warp<All> = Warp::new();
let mut mem: SharedMem<i32, 32> = SharedMem::new();
for lane in 0..32 {
let mut slot = split_by_lane(&warp, &mut mem, lane);
slot.write(lane as i32 * 10);
}
for lane in 0..32 {
let slot = split_by_lane(&warp, &mut mem, lane);
assert_eq!(slot.read(), lane as i32 * 10);
}
}
#[test]
fn test_disjoint_views() {
let warp: Warp<All> = Warp::new();
let mem: SharedMem<i32, 8> = SharedMem::from_slice(&[0, 1, 2, 3, 4, 5, 6, 7]);
let (evens, odds) = split_by_predicate(&warp, &mem, |i| i % 2 == 0);
assert_eq!(evens.read(0), Some(0));
assert_eq!(evens.read(1), Some(2));
assert_eq!(evens.read(2), Some(4));
assert_eq!(evens.read(3), Some(6));
assert_eq!(odds.read(0), Some(1));
assert_eq!(odds.read(1), Some(3));
}
}
}
pub mod scoped {
use super::*;
pub struct ScopedMut<'a, T: Copy, const SIZE: usize> {
mem: &'a mut SharedMem<T, SIZE>,
needs_sync: bool,
}
impl<'a, T: Copy, const SIZE: usize> ScopedMut<'a, T, SIZE> {
pub fn read(&self, index: usize) -> T {
self.mem.data[index]
}
pub fn write(&mut self, index: usize, value: T) {
self.mem.data[index] = value;
self.needs_sync = true;
}
}
impl<'a, T: Copy, const SIZE: usize> Drop for ScopedMut<'a, T, SIZE> {
fn drop(&mut self) {
if self.needs_sync {
}
}
}
pub fn scoped_borrow<'a, T: Copy, const SIZE: usize>(
_warp: &Warp<All>,
mem: &'a mut SharedMem<T, SIZE>,
) -> ScopedMut<'a, T, SIZE> {
ScopedMut {
mem,
needs_sync: false,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scoped_sync() {
let warp: Warp<All> = Warp::new();
let mut mem: SharedMem<i32, 32> = SharedMem::new();
{
let mut borrow = scoped_borrow(&warp, &mut mem);
borrow.write(0, 100);
}
assert_eq!(mem.data[0], 100);
}
}
}
pub mod lease {
pub struct Lease<T: Copy, const SIZE: usize> {
data: [T; SIZE],
source_id: usize, }
impl<T: Copy, const SIZE: usize> Lease<T, SIZE> {
pub fn read(&self, index: usize) -> T {
self.data[index]
}
pub fn write(&mut self, index: usize, value: T) {
self.data[index] = value;
}
pub fn source_id(&self) -> usize {
self.source_id
}
}
pub struct LeasePool<T: Copy + Default, const SIZE: usize, const SLOTS: usize> {
slots: [[T; SIZE]; SLOTS],
leased: [bool; SLOTS],
}
impl<T: Copy + Default, const SIZE: usize, const SLOTS: usize> LeasePool<T, SIZE, SLOTS> {
pub fn new() -> Self {
LeasePool {
slots: [[T::default(); SIZE]; SLOTS],
leased: [false; SLOTS],
}
}
pub fn checkout(&mut self) -> Option<Lease<T, SIZE>> {
for (i, is_leased) in self.leased.iter_mut().enumerate() {
if !*is_leased {
*is_leased = true;
return Some(Lease {
data: self.slots[i],
source_id: i,
});
}
}
None
}
pub fn return_lease(&mut self, lease: Lease<T, SIZE>) {
let id = lease.source_id;
assert!(self.leased[id], "Returning non-leased slot");
self.slots[id] = lease.data;
self.leased[id] = false;
}
pub fn available(&self) -> usize {
self.leased.iter().filter(|&&l| !l).count()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lease_pool() {
let mut pool: LeasePool<i32, 32, 4> = LeasePool::new();
assert_eq!(pool.available(), 4);
let mut lease = pool.checkout().unwrap();
assert_eq!(pool.available(), 3);
lease.write(0, 42);
pool.return_lease(lease);
assert_eq!(pool.available(), 4);
}
#[test]
fn test_lease_exhaustion() {
let mut pool: LeasePool<i32, 8, 2> = LeasePool::new();
let _l1 = pool.checkout().unwrap();
let _l2 = pool.checkout().unwrap();
assert!(pool.checkout().is_none());
}
}
}
pub const _FINDINGS: () = ();
#[cfg(test)]
mod integration_tests {
use super::*;
#[test]
fn test_full_borrow_cycle() {
let warp: Warp<All> = Warp::new();
let mut mem: SharedMem<i32, 32> = SharedMem::new();
{
let mut borrow = lane_parallel::borrow_mut(&warp, &mut mem);
borrow.write(0, 100); }
for lane in 0..32 {
let mut slot = split::split_by_lane(&warp, &mut mem, lane);
slot.write(lane as i32);
}
for lane in 0..32 {
let slot = split::split_by_lane(&warp, &mut mem, lane);
if lane == 0 {
assert_eq!(slot.read(), 0);
} else {
assert_eq!(slot.read(), lane as i32);
}
}
}
}