use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Condvar, Mutex};
use std::time::{Duration, Instant};
use thiserror::Error;
use crate::coordination::{PodCoordinator, TpuDeviceId};
#[derive(Debug)]
pub struct SynchronizationManager {
device_id: TpuDeviceId,
all_devices: Vec<TpuDeviceId>,
active_barriers: Arc<Mutex<HashMap<String, Barrier>>>,
collective_handlers: HashMap<CollectiveOpType, Box<dyn CollectiveHandler>>,
topology: CommunicationTopology,
stats: Arc<Mutex<SynchronizationStats>>,
}
#[derive(Debug)]
pub struct Barrier {
pub id: String,
pub required_devices: Vec<TpuDeviceId>,
pub arrived_devices: Vec<TpuDeviceId>,
pub condition: Arc<(Mutex<bool>, Condvar)>,
pub timeout: Duration,
pub created_at: Instant,
pub state: BarrierState,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BarrierState {
Active,
Complete,
TimedOut,
Cancelled,
}
#[derive(Debug, Clone)]
pub struct CommunicationTopology {
pub topology_type: TopologyType,
pub connections: HashMap<TpuDeviceId, Vec<TpuDeviceId>>,
pub rings: Vec<Vec<TpuDeviceId>>,
pub tree: Option<CommunicationTree>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TopologyType {
Linear,
Ring,
Tree,
Mesh,
Torus,
Custom,
}
#[derive(Debug, Clone)]
pub struct CommunicationTree {
pub root: TpuDeviceId,
pub parent_child: HashMap<TpuDeviceId, Vec<TpuDeviceId>>,
pub child_parent: HashMap<TpuDeviceId, TpuDeviceId>,
pub depth: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CollectiveOpType {
AllReduce,
AllGather,
ReduceScatter,
Broadcast,
AllToAll,
Barrier,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReductionOp {
Sum,
Mean,
Max,
Min,
Product,
LogicalAnd,
LogicalOr,
}
#[derive(Debug, Clone)]
pub struct CollectiveOpRequest {
pub id: String,
pub op_type: CollectiveOpType,
pub devices: Vec<TpuDeviceId>,
pub root_device: Option<TpuDeviceId>,
pub reduction_op: Option<ReductionOp>,
pub data_size: usize,
pub timeout: Duration,
pub priority: OperationPriority,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum OperationPriority {
Low,
Normal,
High,
Critical,
}
#[derive(Debug)]
pub struct CollectiveOpResult {
pub id: String,
pub status: OperationStatus,
pub duration: Duration,
pub bandwidth_gb_s: f64,
pub error_message: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OperationStatus {
Success,
Failed,
TimedOut,
Cancelled,
}
pub trait CollectiveHandler: Send + Sync + std::fmt::Debug {
fn execute(
&self,
request: &CollectiveOpRequest,
topology: &CommunicationTopology,
) -> Result<CollectiveOpResult, SynchronizationError>;
fn estimate_time(&self, request: &CollectiveOpRequest) -> Duration;
fn supports_operation(&self, op_type: CollectiveOpType) -> bool;
}
#[derive(Debug, Default, Clone)]
pub struct SynchronizationStats {
pub barriers_created: u64,
pub barriers_completed: u64,
pub barriers_timed_out: u64,
pub collective_ops_total: u64,
pub collective_ops_success: u64,
pub collective_ops_failed: u64,
pub total_sync_time_seconds: f64,
pub avg_barrier_wait_time: f64,
pub total_data_transferred: u64,
pub avg_bandwidth_gb_s: f64,
}
#[derive(Debug, Error)]
pub enum SynchronizationError {
#[error("Barrier timeout: {barrier_id}")]
BarrierTimeout { barrier_id: String },
#[error("Device not found: {device_id:?}")]
DeviceNotFound { device_id: TpuDeviceId },
#[error("Collective operation failed: {reason}")]
CollectiveOpFailed { reason: String },
#[error("Communication error: {reason}")]
CommunicationError { reason: String },
#[error("Topology error: {reason}")]
TopologyError { reason: String },
#[error("Operation cancelled: {operation_id}")]
OperationCancelled { operation_id: String },
#[error("Invalid operation: {reason}")]
InvalidOperation { reason: String },
}
impl SynchronizationManager {
pub fn new(
device_id: TpuDeviceId,
all_devices: Vec<TpuDeviceId>,
topology: CommunicationTopology,
) -> Self {
let mut collective_handlers: HashMap<CollectiveOpType, Box<dyn CollectiveHandler>> =
HashMap::new();
collective_handlers.insert(
CollectiveOpType::AllReduce,
Box::new(AllReduceHandler::new()),
);
collective_handlers.insert(
CollectiveOpType::AllGather,
Box::new(AllGatherHandler::new()),
);
collective_handlers.insert(
CollectiveOpType::Broadcast,
Box::new(BroadcastHandler::new()),
);
collective_handlers.insert(CollectiveOpType::Barrier, Box::new(BarrierHandler::new()));
Self {
device_id,
all_devices,
active_barriers: Arc::new(Mutex::new(HashMap::new())),
collective_handlers,
topology,
stats: Arc::new(Mutex::new(SynchronizationStats::default())),
}
}
pub fn create_barrier(
&self,
barrier_id: String,
devices: Vec<TpuDeviceId>,
timeout: Duration,
) -> Result<(), SynchronizationError> {
let barrier = Barrier {
id: barrier_id.clone(),
required_devices: devices,
arrived_devices: Vec::new(),
condition: Arc::new((Mutex::new(false), Condvar::new())),
timeout,
created_at: Instant::now(),
state: BarrierState::Active,
};
let mut barriers = self.active_barriers.lock().expect("lock poisoned");
barriers.insert(barrier_id, barrier);
let mut stats = self.stats.lock().expect("lock poisoned");
stats.barriers_created += 1;
Ok(())
}
pub fn wait_barrier(&self, barrier_id: &str) -> Result<(), SynchronizationError> {
let start_time = Instant::now();
{
let mut barriers = self.active_barriers.lock().expect("lock poisoned");
if let Some(barrier) = barriers.get_mut(barrier_id) {
if !barrier.arrived_devices.contains(&self.device_id) {
barrier.arrived_devices.push(self.device_id);
}
if barrier.arrived_devices.len() == barrier.required_devices.len() {
barrier.state = BarrierState::Complete;
let (_, condvar) = &*barrier.condition;
condvar.notify_all();
let mut stats = self.stats.lock().expect("lock poisoned");
stats.barriers_completed += 1;
return Ok(());
}
} else {
return Err(SynchronizationError::InvalidOperation {
reason: format!("Barrier {} not found", barrier_id),
});
}
}
let barrier_condition = {
let barriers = self.active_barriers.lock().expect("lock poisoned");
barriers
.get(barrier_id)
.expect("unwrap failed")
.condition
.clone()
};
let (lock, condvar) = &*barrier_condition;
let completed = lock.lock().expect("lock poisoned");
let timeout_result = condvar
.wait_timeout_while(
completed,
Duration::from_secs(30), |&mut completed| !completed,
)
.expect("unwrap failed");
if timeout_result.1.timed_out() {
let mut barriers = self.active_barriers.lock().expect("lock poisoned");
if let Some(barrier) = barriers.get_mut(barrier_id) {
barrier.state = BarrierState::TimedOut;
}
let mut stats = self.stats.lock().expect("lock poisoned");
stats.barriers_timed_out += 1;
return Err(SynchronizationError::BarrierTimeout {
barrier_id: barrier_id.to_string(),
});
}
let wait_time = start_time.elapsed().as_secs_f64();
let mut stats = self.stats.lock().expect("lock poisoned");
stats.total_sync_time_seconds += wait_time;
stats.avg_barrier_wait_time =
stats.total_sync_time_seconds / stats.barriers_completed as f64;
Ok(())
}
pub fn execute_collective_op(
&self,
request: CollectiveOpRequest,
) -> Result<CollectiveOpResult, SynchronizationError> {
let start_time = Instant::now();
{
let mut stats = self.stats.lock().expect("lock poisoned");
stats.collective_ops_total += 1;
}
let handler = self
.collective_handlers
.get(&request.op_type)
.ok_or_else(|| SynchronizationError::InvalidOperation {
reason: format!("No handler for operation {:?}", request.op_type),
})?;
let result = handler.execute(&request, &self.topology);
let mut stats = self.stats.lock().expect("lock poisoned");
match &result {
Ok(op_result) => {
stats.collective_ops_success += 1;
stats.total_data_transferred += request.data_size as u64;
let total_ops = stats.collective_ops_success;
let new_bandwidth = op_result.bandwidth_gb_s;
stats.avg_bandwidth_gb_s = (stats.avg_bandwidth_gb_s * (total_ops - 1) as f64
+ new_bandwidth)
/ total_ops as f64;
}
Err(_) => {
stats.collective_ops_failed += 1;
}
}
result
}
pub fn get_statistics(&self) -> SynchronizationStats {
let stats = self.stats.lock().expect("lock poisoned");
(*stats).clone()
}
pub fn cancel_all_barriers(&self) {
let mut barriers = self.active_barriers.lock().expect("lock poisoned");
for (_, barrier) in barriers.iter_mut() {
barrier.state = BarrierState::Cancelled;
let (_, condvar) = &*barrier.condition;
condvar.notify_all();
}
barriers.clear();
}
pub fn get_topology(&self) -> &CommunicationTopology {
&self.topology
}
}
#[derive(Debug)]
pub struct AllReduceHandler;
impl Default for AllReduceHandler {
fn default() -> Self {
Self::new()
}
}
impl AllReduceHandler {
pub fn new() -> Self {
Self
}
}
impl CollectiveHandler for AllReduceHandler {
fn execute(
&self,
request: &CollectiveOpRequest,
topology: &CommunicationTopology,
) -> Result<CollectiveOpResult, SynchronizationError> {
let start_time = Instant::now();
std::thread::sleep(Duration::from_millis(10));
let duration = start_time.elapsed();
let bandwidth_gb_s =
(request.data_size as f64) / duration.as_secs_f64() / (1024.0 * 1024.0 * 1024.0);
Ok(CollectiveOpResult {
id: request.id.clone(),
status: OperationStatus::Success,
duration,
bandwidth_gb_s,
error_message: None,
})
}
fn estimate_time(&self, request: &CollectiveOpRequest) -> Duration {
let base_latency = Duration::from_micros(10);
let transfer_time = Duration::from_nanos(
(request.data_size as u64 * request.devices.len() as u64) / 100, );
base_latency + transfer_time
}
fn supports_operation(&self, op_type: CollectiveOpType) -> bool {
matches!(op_type, CollectiveOpType::AllReduce)
}
}
#[derive(Debug)]
pub struct AllGatherHandler;
impl Default for AllGatherHandler {
fn default() -> Self {
Self::new()
}
}
impl AllGatherHandler {
pub fn new() -> Self {
Self
}
}
impl CollectiveHandler for AllGatherHandler {
fn execute(
&self,
request: &CollectiveOpRequest,
_topology: &CommunicationTopology,
) -> Result<CollectiveOpResult, SynchronizationError> {
let start_time = Instant::now();
std::thread::sleep(Duration::from_millis(5));
let duration = start_time.elapsed();
let bandwidth_gb_s = (request.data_size as f64 * request.devices.len() as f64)
/ duration.as_secs_f64()
/ (1024.0 * 1024.0 * 1024.0);
Ok(CollectiveOpResult {
id: request.id.clone(),
status: OperationStatus::Success,
duration,
bandwidth_gb_s,
error_message: None,
})
}
fn estimate_time(&self, request: &CollectiveOpRequest) -> Duration {
Duration::from_micros(5 + (request.data_size / 1024) as u64)
}
fn supports_operation(&self, op_type: CollectiveOpType) -> bool {
matches!(op_type, CollectiveOpType::AllGather)
}
}
#[derive(Debug)]
pub struct BroadcastHandler;
impl Default for BroadcastHandler {
fn default() -> Self {
Self::new()
}
}
impl BroadcastHandler {
pub fn new() -> Self {
Self
}
}
impl CollectiveHandler for BroadcastHandler {
fn execute(
&self,
request: &CollectiveOpRequest,
_topology: &CommunicationTopology,
) -> Result<CollectiveOpResult, SynchronizationError> {
let start_time = Instant::now();
if request.root_device.is_none() {
return Err(SynchronizationError::InvalidOperation {
reason: "Broadcast requires a root device".to_string(),
});
}
std::thread::sleep(Duration::from_millis(3));
let duration = start_time.elapsed();
let bandwidth_gb_s =
(request.data_size as f64) / duration.as_secs_f64() / (1024.0 * 1024.0 * 1024.0);
Ok(CollectiveOpResult {
id: request.id.clone(),
status: OperationStatus::Success,
duration,
bandwidth_gb_s,
error_message: None,
})
}
fn estimate_time(&self, request: &CollectiveOpRequest) -> Duration {
Duration::from_micros(3 + (request.data_size / 2048) as u64)
}
fn supports_operation(&self, op_type: CollectiveOpType) -> bool {
matches!(op_type, CollectiveOpType::Broadcast)
}
}
#[derive(Debug)]
pub struct BarrierHandler;
impl Default for BarrierHandler {
fn default() -> Self {
Self::new()
}
}
impl BarrierHandler {
pub fn new() -> Self {
Self
}
}
impl CollectiveHandler for BarrierHandler {
fn execute(
&self,
request: &CollectiveOpRequest,
_topology: &CommunicationTopology,
) -> Result<CollectiveOpResult, SynchronizationError> {
let start_time = Instant::now();
std::thread::sleep(Duration::from_millis(1));
let duration = start_time.elapsed();
Ok(CollectiveOpResult {
id: request.id.clone(),
status: OperationStatus::Success,
duration,
bandwidth_gb_s: 0.0, error_message: None,
})
}
fn estimate_time(&self, _request: &CollectiveOpRequest) -> Duration {
Duration::from_micros(100) }
fn supports_operation(&self, op_type: CollectiveOpType) -> bool {
matches!(op_type, CollectiveOpType::Barrier)
}
}
impl CommunicationTopology {
pub fn create_ring(devices: Vec<TpuDeviceId>) -> Self {
let mut connections = HashMap::new();
let mut rings = Vec::new();
if !devices.is_empty() {
for (i, &device) in devices.iter().enumerate() {
let next_device = devices[(i + 1) % devices.len()];
connections.insert(device, vec![next_device]);
}
rings.push(devices.clone());
}
Self {
topology_type: TopologyType::Ring,
connections,
rings,
tree: None,
}
}
pub fn create_tree(devices: Vec<TpuDeviceId>) -> Self {
let mut connections = HashMap::new();
let mut parent_child = HashMap::new();
let mut child_parent = HashMap::new();
if !devices.is_empty() {
let root = devices[0];
for (i, &device) in devices.iter().enumerate() {
let mut children = Vec::new();
let left_child_idx = 2 * i + 1;
let right_child_idx = 2 * i + 2;
if left_child_idx < devices.len() {
children.push(devices[left_child_idx]);
child_parent.insert(devices[left_child_idx], device);
}
if right_child_idx < devices.len() {
children.push(devices[right_child_idx]);
child_parent.insert(devices[right_child_idx], device);
}
if !children.is_empty() {
connections.insert(device, children.clone());
parent_child.insert(device, children);
}
}
}
let tree = if !devices.is_empty() {
Some(CommunicationTree {
root: devices[0],
parent_child,
child_parent,
depth: (devices.len() as f64).log2().ceil() as u32,
})
} else {
None
};
Self {
topology_type: TopologyType::Tree,
connections,
rings: Vec::new(),
tree,
}
}
pub fn create_mesh(devices: Vec<TpuDeviceId>) -> Self {
let mut connections = HashMap::new();
for &device in &devices {
let neighbors: Vec<TpuDeviceId> = devices
.iter()
.filter(|&&other| other != device)
.cloned()
.collect();
connections.insert(device, neighbors);
}
Self {
topology_type: TopologyType::Mesh,
connections,
rings: Vec::new(),
tree: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_synchronization_manager_creation() {
let devices = vec![
TpuDeviceId(0),
TpuDeviceId(1),
TpuDeviceId(2),
TpuDeviceId(3),
];
let topology = CommunicationTopology::create_ring(devices.clone());
let sync_manager = SynchronizationManager::new(TpuDeviceId(0), devices, topology);
assert_eq!(sync_manager.device_id, TpuDeviceId(0));
assert_eq!(sync_manager.all_devices.len(), 4);
}
#[test]
fn test_barrier_creation() {
let devices = vec![TpuDeviceId(0), TpuDeviceId(1)];
let topology = CommunicationTopology::create_ring(devices.clone());
let sync_manager = SynchronizationManager::new(TpuDeviceId(0), devices.clone(), topology);
let result = sync_manager.create_barrier(
"test_barrier".to_string(),
devices,
Duration::from_secs(10),
);
assert!(result.is_ok());
let barriers = sync_manager.active_barriers.lock().expect("lock poisoned");
assert!(barriers.contains_key("test_barrier"));
}
#[test]
fn test_collective_operation() {
let devices = vec![
TpuDeviceId(0),
TpuDeviceId(1),
TpuDeviceId(2),
TpuDeviceId(3),
];
let topology = CommunicationTopology::create_ring(devices.clone());
let sync_manager = SynchronizationManager::new(TpuDeviceId(0), devices.clone(), topology);
let request = CollectiveOpRequest {
id: "test_allreduce".to_string(),
op_type: CollectiveOpType::AllReduce,
devices,
root_device: None,
reduction_op: Some(ReductionOp::Sum),
data_size: 1024,
timeout: Duration::from_secs(10),
priority: OperationPriority::Normal,
};
let result = sync_manager.execute_collective_op(request);
assert!(result.is_ok());
let op_result = result.expect("unwrap failed");
assert_eq!(op_result.status, OperationStatus::Success);
}
#[test]
fn test_topology_creation() {
let devices = vec![
TpuDeviceId(0),
TpuDeviceId(1),
TpuDeviceId(2),
TpuDeviceId(3),
];
let ring_topology = CommunicationTopology::create_ring(devices.clone());
assert_eq!(ring_topology.topology_type, TopologyType::Ring);
assert_eq!(ring_topology.rings.len(), 1);
assert_eq!(ring_topology.rings[0].len(), 4);
let tree_topology = CommunicationTopology::create_tree(devices.clone());
assert_eq!(tree_topology.topology_type, TopologyType::Tree);
assert!(tree_topology.tree.is_some());
let mesh_topology = CommunicationTopology::create_mesh(devices.clone());
assert_eq!(mesh_topology.topology_type, TopologyType::Mesh);
assert_eq!(mesh_topology.connections.len(), 4);
for neighbors in mesh_topology.connections.values() {
assert_eq!(neighbors.len(), 3); }
}
}