#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GroupLayout {
pub packing: usize,
pub block_bytes: usize,
pub shard_count: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HostLayout {
pub groups: Vec<GroupLayout>,
pub lcm_block_bytes: usize,
pub num_lcm_blocks: usize,
pub owner_rank: usize,
pub tp_rank: usize,
}
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum LayoutError {
#[error("cache transfer names unknown group {0}")]
Group(u32),
#[error("a cache transfer cannot name the null block 0")]
Null,
#[error("Host block {block} of group {group} is out of range")]
Block {
group: u32,
block: i64,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Range {
pub offset: usize,
pub size: usize,
}
impl HostLayout {
pub fn bytes(&self) -> usize {
self.num_lcm_blocks * self.lcm_block_bytes
}
fn group(&self, group: u32) -> Result<&GroupLayout, LayoutError> {
self.groups
.get(group as usize)
.ok_or(LayoutError::Group(group))
}
pub fn owned_range(&self, group: u32, block: i32) -> Result<Option<Range>, LayoutError> {
let layout = self.group(group)?;
if block == 0 {
return Err(LayoutError::Null);
}
let virtual_count = 1 + self.num_lcm_blocks * layout.packing * layout.shard_count;
let Ok(virtual_block) = usize::try_from(block) else {
return Err(LayoutError::Block {
group,
block: i64::from(block),
});
};
if virtual_block >= virtual_count {
return Err(LayoutError::Block {
group,
block: i64::from(block),
});
}
let dealt = virtual_block - 1;
if layout.shard_count > 1 && dealt % layout.shard_count != self.owner_rank {
return Ok(None);
}
let local = dealt / layout.shard_count + 1;
Ok(Some(self.local_range(group, layout, local)?))
}
fn local_range(
&self,
group: u32,
layout: &GroupLayout,
local: usize,
) -> Result<Range, LayoutError> {
if local == 0 || local > self.num_lcm_blocks * layout.packing {
return Err(LayoutError::Block {
group,
block: local as i64,
});
}
let zero_based = local - 1;
let (parent, child) = (zero_based / layout.packing, zero_based % layout.packing);
Ok(Range {
offset: parent * self.lcm_block_bytes + child * layout.block_bytes,
size: layout.block_bytes,
})
}
pub fn block_bytes(&self, group: u32) -> Result<usize, LayoutError> {
Ok(self.group(group)?.block_bytes)
}
pub fn key_rank(&self, group: u32) -> Result<i64, LayoutError> {
Ok((self.tp_rank / self.group(group)?.shard_count) as i64)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn layout(shard_count: usize, owner_rank: usize) -> HostLayout {
HostLayout {
groups: vec![
GroupLayout {
packing: 2,
block_bytes: 10,
shard_count: 1,
},
GroupLayout {
packing: 1,
block_bytes: 4,
shard_count,
},
],
lcm_block_bytes: 20,
num_lcm_blocks: 3,
owner_rank,
tp_rank: 5,
}
}
#[test]
fn packed_blocks_sit_inside_their_lcm_block() {
let host = layout(1, 0);
assert_eq!(host.bytes(), 60);
let range = |block| host.owned_range(0, block).unwrap().unwrap();
assert_eq!(
range(1),
Range {
offset: 0,
size: 10
}
);
assert_eq!(
range(2),
Range {
offset: 10,
size: 10
}
);
assert_eq!(
range(3),
Range {
offset: 20,
size: 10
}
);
assert_eq!(
range(6),
Range {
offset: 50,
size: 10
}
);
assert_eq!(
host.owned_range(0, 7),
Err(LayoutError::Block { group: 0, block: 7 })
);
assert_eq!(host.owned_range(0, 0), Err(LayoutError::Null));
assert_eq!(host.owned_range(2, 1), Err(LayoutError::Group(2)));
}
#[test]
fn a_sharded_group_deals_its_blocks_to_the_owners() {
let rank0 = layout(2, 0);
let rank1 = layout(2, 1);
assert_eq!(
rank0.owned_range(1, 3).unwrap(),
Some(Range {
offset: 20,
size: 4
})
);
assert_eq!(rank0.owned_range(1, 4).unwrap(), None);
assert_eq!(
rank1.owned_range(1, 4).unwrap(),
Some(Range {
offset: 20,
size: 4
})
);
assert!(rank1.owned_range(1, 6).unwrap().is_some());
assert!(rank1.owned_range(1, 7).is_err());
assert_eq!(rank0.key_rank(1).unwrap(), 2);
assert_eq!(rank0.key_rank(0).unwrap(), 5);
}
}