use crate::BlockId;
use super::{
FullyContiguousLayout, InnerShape, LayerSeparateLayout, Layout, MemoryRegion,
builder::{PhysicalLayoutBuilder, PhysicalLayoutBuilderDefault},
serialize::{LayoutDescriptor, LayoutTypeDetails},
};
use anyhow::{Result, anyhow};
use dynamo_memory::{
Buffer, MemoryDescriptor, StorageKind,
nixl::{MemType, NixlAgent, NixlDescriptor},
};
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct PhysicalLayout {
layout: Arc<dyn Layout>,
location: StorageKind,
nixl_metadata: NixlMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NixlMetadata {
agent_name: String,
mem_type: MemType,
device_id: u64,
}
impl NixlMetadata {
pub fn new(agent_name: String, mem_type: MemType, device_id: u64) -> Self {
Self {
agent_name,
mem_type,
device_id,
}
}
pub fn agent_name(&self) -> &str {
&self.agent_name
}
#[inline(always)]
pub fn mem_type(&self) -> MemType {
self.mem_type
}
#[inline(always)]
pub fn device_id(&self) -> u64 {
self.device_id
}
}
impl PhysicalLayout {
pub fn builder(agent: NixlAgent) -> PhysicalLayoutBuilderDefault {
PhysicalLayoutBuilder::new(agent)
}
pub(crate) fn new_local(
layout: Arc<dyn Layout>,
location: StorageKind,
nixl_metadata: NixlMetadata,
) -> Self {
Self {
layout,
location,
nixl_metadata,
}
}
pub fn layout(&self) -> &Arc<dyn Layout> {
&self.layout
}
pub(crate) fn location(&self) -> StorageKind {
self.location
}
pub(crate) fn nixl_metadata(&self) -> &NixlMetadata {
&self.nixl_metadata
}
pub fn memory_region(
&self,
block_id: BlockId,
layer_id: usize,
outer_id: usize,
) -> Result<MemoryRegion> {
self.layout.memory_region(block_id, layer_id, outer_id)
}
pub(crate) fn to_descriptor(&self) -> Result<LayoutDescriptor> {
let memory_descriptors = self
.layout
.memory_regions()
.iter()
.map(|region| MemoryRegion {
addr: region.addr(),
size: region.size(),
})
.collect();
let layout_type_details = self.layout.serialization_details();
Ok(LayoutDescriptor {
version: LayoutDescriptor::CURRENT_VERSION,
layout_config: self.layout.config().clone(),
location: self.location,
nixl_metadata: self.nixl_metadata.clone(),
memory_descriptors,
layout_type_details,
})
}
pub(crate) fn from_descriptor(serialized: LayoutDescriptor) -> Result<Self> {
if serialized.version > LayoutDescriptor::CURRENT_VERSION {
return Err(anyhow!(
"Unsupported serialization version: {}. Maximum supported: {}",
serialized.version,
LayoutDescriptor::CURRENT_VERSION
));
}
let remote_regions: Vec<Arc<dyn MemoryDescriptor>> = serialized
.memory_descriptors
.iter()
.map(|desc| {
Arc::new(RemoteMemoryDescriptor {
addr: desc.addr,
size: desc.size,
storage_kind: serialized.location,
nixl_metadata: serialized.nixl_metadata.clone(),
}) as Arc<dyn MemoryDescriptor>
})
.collect();
let layout: Arc<dyn Layout> = match serialized.layout_type_details {
LayoutTypeDetails::FullyContiguous(details) => {
if remote_regions.len() != 1 {
return Err(anyhow!(
"FullyContiguous layout requires exactly 1 memory region, got {}",
remote_regions.len()
));
}
let layout = FullyContiguousLayout::new_with_format(
serialized.layout_config.clone(),
Buffer::from_arc(remote_regions[0].clone()),
details.block_format,
details.kv_block_layout,
)?;
Arc::new(layout)
}
LayoutTypeDetails::LayerSeparate(details) => {
if remote_regions.len() != serialized.layout_config.num_layers {
return Err(anyhow!(
"LayerSeparate layout requires {} memory regions (one per layer), got {}",
serialized.layout_config.num_layers,
remote_regions.len()
));
}
let inner_shape = details
.kv_block_layout
.to_inner_shape()
.unwrap_or(InnerShape::Unknown);
let layout = LayerSeparateLayout::builder()
.config(serialized.layout_config.clone())
.memory(remote_regions.into_iter().map(Buffer::from_arc).collect())
.block_dim(details.block_dim)
.inner_shape(inner_shape)
.build()?;
Arc::new(layout)
}
};
Ok(Self {
layout,
location: serialized.location,
nixl_metadata: serialized.nixl_metadata,
})
}
}
#[derive(Debug)]
struct RemoteMemoryDescriptor {
addr: usize,
size: usize,
storage_kind: StorageKind,
nixl_metadata: NixlMetadata,
}
impl MemoryDescriptor for RemoteMemoryDescriptor {
fn addr(&self) -> usize {
self.addr
}
fn size(&self) -> usize {
self.size
}
fn storage_kind(&self) -> StorageKind {
self.storage_kind
}
fn as_any(&self) -> &dyn Any {
self
}
fn nixl_descriptor(&self) -> Option<NixlDescriptor> {
Some(NixlDescriptor {
addr: self.addr as u64,
size: self.size,
mem_type: self.nixl_metadata.mem_type(),
device_id: self.nixl_metadata.device_id(),
})
}
}