1#![allow(unused_imports, unused_variables, dead_code)]
8
9use crate::{
10 dataloader::{BatchResult, DistributedSampler, Sampler},
11 DataLoader, DataLoaderConfig, Dataset,
12};
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15use std::io::{BufReader, BufWriter, Read, Write};
16use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, TcpStream};
17use std::sync::{Arc, Mutex, RwLock};
18use std::thread;
19use std::time::{Duration, Instant};
20use tenflowers_core::{Device, Result, Tensor, TensorError};
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct DistributedLoadingConfig {
25 pub world_size: usize,
27 pub rank: usize,
29 pub master_addr: String,
31 pub master_port: u16,
33 pub enable_rdma: bool,
35 pub rdma_device: Option<String>,
37 pub network_timeout: Duration,
39 pub enable_compression: bool,
41 pub collective_batch_size: usize,
43 pub network_workers: usize,
45 pub enable_remote_prefetch: bool,
47 pub remote_prefetch_size: usize,
49}
50
51impl Default for DistributedLoadingConfig {
52 fn default() -> Self {
53 Self {
54 world_size: 1,
55 rank: 0,
56 master_addr: "127.0.0.1".to_string(),
57 master_port: 29500,
58 enable_rdma: false,
59 rdma_device: None,
60 network_timeout: Duration::from_secs(30),
61 enable_compression: false,
62 collective_batch_size: 32,
63 network_workers: 4,
64 enable_remote_prefetch: true,
65 remote_prefetch_size: 64,
66 }
67 }
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct NodeInfo {
73 pub rank: usize,
74 pub addr: SocketAddr,
75 pub device_capabilities: Vec<String>, pub rdma_enabled: bool,
77 pub rdma_device: Option<String>,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82pub enum DistributedMessage {
83 Handshake { node_info: NodeInfo },
85 DataRequest {
87 indices: Vec<usize>,
88 requestor_rank: usize,
89 request_id: u64,
90 },
91 DataResponse {
93 data: Vec<u8>, request_id: u64,
95 compressed: bool,
96 },
97 CollectiveOp {
99 op_type: CollectiveOpType,
100 op_id: u64,
101 data: Option<Vec<u8>>,
102 },
103 Heartbeat { timestamp: u64 },
105 Error { message: String },
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
111pub enum CollectiveOpType {
112 EpochSync { epoch: usize },
114 ShuffleSync { seed: u64 },
116 StatisticsGather,
118 ConfigBroadcast,
120 Barrier,
122 Broadcast,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct DistributedLoadingStats {
129 pub local_samples_loaded: u64,
130 pub remote_samples_loaded: u64,
131 pub network_bytes_sent: u64,
132 pub network_bytes_received: u64,
133 pub average_network_latency_ms: u64, pub cache_hit_rate: f64,
135 pub rdma_transfers: u64,
136 pub collective_operations: u64,
137}
138
139impl Default for DistributedLoadingStats {
140 fn default() -> Self {
141 Self {
142 local_samples_loaded: 0,
143 remote_samples_loaded: 0,
144 network_bytes_sent: 0,
145 network_bytes_received: 0,
146 average_network_latency_ms: 0,
147 cache_hit_rate: 0.0,
148 rdma_transfers: 0,
149 collective_operations: 0,
150 }
151 }
152}
153
154pub struct EnhancedDistributedSampler {
156 base_sampler: DistributedSampler,
158 config: DistributedLoadingConfig,
160 comm_manager: Arc<Mutex<CommunicationManager>>,
162 stats: Arc<RwLock<DistributedLoadingStats>>,
164 sample_cache: Arc<Mutex<HashMap<usize, CachedSample>>>,
166 rdma_context: Option<Arc<Mutex<RdmaContext>>>,
168}
169
170#[derive(Debug, Clone)]
172struct CachedSample {
173 data: Vec<u8>,
174 timestamp: Instant,
175 access_count: u64,
176}
177
178#[derive(Debug)]
180struct RdmaContext {
181 device_name: String,
182 initialized: bool,
185 memory_regions: HashMap<String, RdmaMemoryRegion>,
186}
187
188#[derive(Debug)]
190struct RdmaMemoryRegion {
191 addr: usize, size: usize,
193 }
195
196pub struct CommunicationManager {
198 node_info: NodeInfo,
199 cluster_nodes: HashMap<usize, NodeInfo>,
200 connections: HashMap<usize, TcpStream>,
201 listener: Option<TcpListener>,
202 config: DistributedLoadingConfig,
203 #[allow(clippy::type_complexity)]
204 message_handlers: HashMap<
205 String,
206 Box<dyn Fn(&DistributedMessage) -> Result<Option<DistributedMessage>> + Send + Sync>,
207 >,
208}
209
210impl EnhancedDistributedSampler {
211 pub fn new(num_replicas: usize, rank: usize, config: DistributedLoadingConfig) -> Result<Self> {
213 let base_sampler = DistributedSampler::new(num_replicas, rank)?;
214
215 let node_info = NodeInfo {
217 rank,
218 addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0), device_capabilities: Self::detect_devices_as_strings(),
220 rdma_enabled: config.enable_rdma,
221 rdma_device: config.rdma_device.clone(),
222 };
223
224 let comm_manager = Arc::new(Mutex::new(CommunicationManager::new(
225 node_info,
226 config.clone(),
227 )?));
228
229 let rdma_context = if config.enable_rdma {
231 Some(Arc::new(Mutex::new(RdmaContext::new(
232 config.rdma_device.as_ref(),
233 )?)))
234 } else {
235 None
236 };
237
238 Ok(Self {
239 base_sampler,
240 config,
241 comm_manager,
242 stats: Arc::new(RwLock::new(DistributedLoadingStats::default())),
243 sample_cache: Arc::new(Mutex::new(HashMap::new())),
244 rdma_context,
245 })
246 }
247
248 pub fn initialize(&mut self) -> Result<()> {
250 self.register_with_master()?;
252
253 self.discover_cluster_nodes()?;
255
256 self.establish_connections()?;
258
259 if let Some(rdma_context) = &self.rdma_context {
261 let mut ctx = rdma_context.lock().map_err(|_| {
262 TensorError::invalid_operation_simple("rdma context lock poisoned".to_string())
263 })?;
264 ctx.initialize()?;
265 }
266
267 self.start_network_workers()?;
269
270 Ok(())
271 }
272
273 pub fn sample_indices_distributed(
275 &self,
276 dataset_len: usize,
277 ) -> Result<Box<dyn Iterator<Item = usize> + Send>> {
278 let mut base_indices: Vec<usize> = self.base_sampler.sample_indices(dataset_len).collect();
280
281 if self.base_sampler.is_random() {
283 self.coordinate_shuffle(&mut base_indices)?;
284 }
285
286 let enhanced_indices = self.apply_load_balancing(base_indices)?;
288
289 Ok(Box::new(enhanced_indices.into_iter()))
290 }
291
292 pub fn load_batch_distributed<T, D>(
294 &self,
295 dataset: &D,
296 indices: &[usize],
297 ) -> Result<BatchResult<T>>
298 where
299 T: Clone
300 + Default
301 + Send
302 + Sync
303 + 'static
304 + bytemuck::Pod
305 + bytemuck::Zeroable
306 + serde::Serialize
307 + for<'de> serde::Deserialize<'de>
308 + scirs2_core::numeric::Zero,
309 D: Dataset<T> + Send + Sync,
310 {
311 let mut local_indices = Vec::new();
312 let mut remote_requests = HashMap::new();
313
314 for &index in indices {
316 if self.is_local_index(index, dataset.len()) {
317 local_indices.push(index);
318 } else {
319 let owner_rank = self.get_index_owner(index, dataset.len());
320 remote_requests
321 .entry(owner_rank)
322 .or_insert_with(Vec::new)
323 .push(index);
324 }
325 }
326
327 let mut batch_data = Vec::new();
329 for &index in &local_indices {
330 let (features, labels) = dataset.get(index)?;
331 batch_data.push((features, labels));
332 }
333
334 for (remote_rank, remote_indices) in remote_requests {
336 let remote_data = self.fetch_remote_data_sync::<T>(remote_rank, &remote_indices)?;
339 batch_data.extend(remote_data);
340 }
341
342 {
344 let mut stats = self.stats.write().map_err(|_| {
345 TensorError::invalid_operation_simple("stats lock poisoned".to_string())
346 })?;
347 stats.local_samples_loaded += local_indices.len() as u64;
348 stats.remote_samples_loaded += (indices.len() - local_indices.len()) as u64;
349 }
350
351 Ok(BatchResult::Samples(batch_data))
352 }
353
354 pub fn collective_operation(
356 &self,
357 op_type: CollectiveOpType,
358 data: Option<Vec<u8>>,
359 ) -> Result<Option<Vec<u8>>> {
360 let op_id = self.generate_operation_id();
361 let message = DistributedMessage::CollectiveOp {
362 op_type: op_type.clone(),
363 op_id,
364 data,
365 };
366
367 let comm_manager = self.comm_manager.lock().map_err(|_| {
369 TensorError::invalid_operation_simple("comm manager lock poisoned".to_string())
370 })?;
371 let results = comm_manager.broadcast_message(&message)?;
372
373 match op_type {
375 CollectiveOpType::EpochSync { epoch } => {
376 self.synchronize_epoch(epoch)?;
378 Ok(None)
379 }
380 CollectiveOpType::ShuffleSync { seed } => {
381 self.coordinate_shuffle_seed(seed)?;
383 Ok(None)
384 }
385 CollectiveOpType::StatisticsGather => {
386 let aggregated_stats = self.aggregate_statistics(results)?;
388 let serialized =
389 oxicode::serde::encode_to_vec(&aggregated_stats, oxicode::config::standard())
390 .map_err(|e| {
391 TensorError::invalid_argument(format!("Serialization error: {e}"))
392 })?;
393 Ok(Some(serialized))
394 }
395 CollectiveOpType::ConfigBroadcast => {
396 Ok(None)
398 }
399 CollectiveOpType::Barrier => {
400 Ok(None)
402 }
403 CollectiveOpType::Broadcast => {
404 Ok(None)
406 }
407 }
408 }
409
410 pub fn get_statistics(&self) -> DistributedLoadingStats {
412 self.stats
413 .read()
414 .unwrap_or_else(|poisoned| poisoned.into_inner())
415 .clone()
416 }
417
418 pub fn shutdown(&mut self) -> Result<()> {
420 {
422 let mut comm_manager = self.comm_manager.lock().map_err(|_| {
423 TensorError::invalid_operation_simple("comm manager lock poisoned".to_string())
424 })?;
425 comm_manager.shutdown()?;
426 }
427
428 if let Some(rdma_context) = &self.rdma_context {
430 let mut ctx = rdma_context.lock().map_err(|_| {
431 TensorError::invalid_operation_simple("rdma context lock poisoned".to_string())
432 })?;
433 ctx.cleanup()?;
434 }
435
436 {
438 let mut cache = self.sample_cache.lock().map_err(|_| {
439 TensorError::invalid_operation_simple("sample cache lock poisoned".to_string())
440 })?;
441 cache.clear();
442 }
443
444 Ok(())
445 }
446
447 fn detect_devices() -> Vec<Device> {
450 #[cfg_attr(not(feature = "gpu"), allow(unused_mut))]
451 let mut devices = vec![Device::Cpu];
452
453 #[cfg(feature = "gpu")]
454 {
455 #[cfg(feature = "gpu")]
458 if std::env::var("CUDA_VISIBLE_DEVICES").is_ok() {
459 for i in 0..4 {
460 if let Ok(gpu_device) = Device::from_str(&format!("gpu:{i}")) {
462 devices.push(gpu_device);
463 }
464 }
465 }
466 }
467
468 devices
469 }
470
471 fn detect_devices_as_strings() -> Vec<String> {
472 Self::detect_devices()
473 .iter()
474 .map(|d| format!("{d:?}"))
475 .collect()
476 }
477
478 fn register_with_master(&self) -> Result<()> {
479 let master_addr = format!("{}:{}", self.config.master_addr, self.config.master_port);
481
482 println!("Registering with master at {master_addr}");
484
485 Ok(())
486 }
487
488 fn discover_cluster_nodes(&self) -> Result<()> {
489 Ok(())
492 }
493
494 fn establish_connections(&self) -> Result<()> {
495 Ok(())
498 }
499
500 fn start_network_workers(&self) -> Result<()> {
501 Ok(())
504 }
505
506 fn coordinate_shuffle(&self, indices: &mut [usize]) -> Result<()> {
507 let seed = if self.config.rank == 0 {
509 std::time::SystemTime::now()
511 .duration_since(std::time::UNIX_EPOCH)
512 .map(|d| d.as_secs())
513 .unwrap_or(0)
514 } else {
515 let collective_msg = DistributedMessage::CollectiveOp {
517 op_type: CollectiveOpType::Broadcast,
518 op_id: std::time::SystemTime::now()
519 .duration_since(std::time::UNIX_EPOCH)
520 .map(|d| d.as_nanos() as u64)
521 .unwrap_or(0),
522 data: None,
523 };
524
525 let res = {
527 let comm_manager = self.comm_manager.lock().map_err(|_| {
528 TensorError::invalid_operation_simple("comm manager lock poisoned".to_string())
529 })?;
530 comm_manager.send_request(0, &collective_msg)
531 };
532 match res {
533 Ok(DistributedMessage::CollectiveOp {
534 data: Some(seed_data),
535 ..
536 }) => {
537 match oxicode::serde::decode_owned_from_slice::<u64, _>(
539 &seed_data,
540 oxicode::config::standard(),
541 )
542 .map(|(v, _)| v)
543 {
544 Ok(received_seed) => received_seed,
545 Err(_) => {
546 std::time::SystemTime::now()
548 .duration_since(std::time::UNIX_EPOCH)
549 .map(|d| d.as_secs())
550 .unwrap_or(0)
551 }
552 }
553 }
554 _ => {
555 std::time::SystemTime::now()
557 .duration_since(std::time::UNIX_EPOCH)
558 .map(|d| d.as_secs())
559 .unwrap_or(0)
560 }
561 }
562 };
563
564 self.coordinate_shuffle_seed(seed)?;
565
566 let mut rng_state = seed;
568 for i in (1..indices.len()).rev() {
569 rng_state = rng_state.wrapping_mul(1103515245).wrapping_add(12345);
570 let j = (rng_state as usize) % (i + 1);
571 indices.swap(i, j);
572 }
573
574 Ok(())
575 }
576
577 fn apply_load_balancing(&self, indices: Vec<usize>) -> Result<Vec<usize>> {
578 Ok(indices)
581 }
582
583 fn is_local_index(&self, index: usize, dataset_len: usize) -> bool {
584 let samples_per_replica =
586 (dataset_len + self.config.world_size - 1) / self.config.world_size;
587 let start_idx = self.config.rank * samples_per_replica;
588 let end_idx = ((self.config.rank + 1) * samples_per_replica).min(dataset_len);
589
590 index >= start_idx && index < end_idx
591 }
592
593 fn get_index_owner(&self, index: usize, dataset_len: usize) -> usize {
594 let samples_per_replica =
596 (dataset_len + self.config.world_size - 1) / self.config.world_size;
597 index / samples_per_replica
598 }
599
600 fn fetch_remote_data_sync<T>(
601 &self,
602 remote_rank: usize,
603 indices: &[usize],
604 ) -> Result<Vec<(Tensor<T>, Tensor<T>)>>
605 where
606 T: Clone
607 + Default
608 + Send
609 + Sync
610 + 'static
611 + bytemuck::Pod
612 + bytemuck::Zeroable
613 + serde::Serialize
614 + for<'de> serde::Deserialize<'de>
615 + scirs2_core::numeric::Zero,
616 {
617 let cached_data = self.check_cache::<T>(indices);
619 if !cached_data.is_empty() {
620 return Ok(cached_data);
621 }
622
623 let request_id = self.generate_request_id();
625 let request = DistributedMessage::DataRequest {
626 indices: indices.to_vec(),
627 requestor_rank: self.config.rank,
628 request_id,
629 };
630
631 let comm_manager = self.comm_manager.lock().map_err(|_| {
632 TensorError::invalid_operation_simple("comm manager lock poisoned".to_string())
633 })?;
634 let response = comm_manager.send_request(remote_rank, &request)?;
635
636 match response {
637 DistributedMessage::DataResponse {
638 data, compressed, ..
639 } => {
640 let data_len = data.len(); let decompressed_data = if compressed {
642 self.decompress_data(&data)?
643 } else {
644 data
645 };
646
647 let samples: Vec<(Tensor<T>, Tensor<T>)> =
649 match oxicode::serde::decode_owned_from_slice::<
650 Vec<(Vec<T>, Vec<usize>, Vec<T>, Vec<usize>)>,
651 _,
652 >(&decompressed_data, oxicode::config::standard())
653 .map(|(v, _)| v)
654 {
655 Ok(tensor_data) => {
656 tensor_data
658 .into_iter()
659 .map(|(input_data, input_shape, target_data, target_shape)| {
660 let input_tensor =
662 match Tensor::from_vec(input_data, &input_shape) {
663 Ok(tensor) => tensor,
664 Err(_) => {
665 Tensor::zeros(&[1])
667 }
668 };
669
670 let target_tensor =
672 match Tensor::from_vec(target_data, &target_shape) {
673 Ok(tensor) => tensor,
674 Err(_) => {
675 Tensor::zeros(&[1])
677 }
678 };
679
680 (input_tensor, target_tensor)
681 })
682 .collect()
683 }
684 Err(_) => {
685 indices
687 .iter()
688 .map(|_| {
689 let input_data = vec![T::default(); 1];
690 let target_data = vec![T::default(); 1];
691 let input_tensor = Tensor::from_vec(input_data, &[1])
692 .unwrap_or_else(|_| Tensor::zeros(&[1]));
693 let target_tensor = Tensor::from_vec(target_data, &[1])
694 .unwrap_or_else(|_| Tensor::zeros(&[1]));
695 (input_tensor, target_tensor)
696 })
697 .collect()
698 }
699 };
700
701 self.cache_samples(indices, &decompressed_data);
703
704 {
706 let mut stats = self.stats.write().map_err(|_| {
707 TensorError::invalid_operation_simple("stats lock poisoned".to_string())
708 })?;
709 stats.network_bytes_received += data_len as u64;
710 }
711
712 Ok(samples)
713 }
714 _ => Err(TensorError::invalid_argument(
715 "Invalid response from remote node".to_string(),
716 )),
717 }
718 }
719
720 fn check_cache<T>(&self, indices: &[usize]) -> Vec<(Tensor<T>, Tensor<T>)>
721 where
722 T: Clone + Default + Send + Sync + 'static + bytemuck::Pod + bytemuck::Zeroable,
723 {
724 Vec::new()
727 }
728
729 fn cache_samples(&self, indices: &[usize], data: &[u8]) {
730 let mut cache = self
731 .sample_cache
732 .lock()
733 .unwrap_or_else(|poisoned| poisoned.into_inner());
734 let timestamp = Instant::now();
735
736 for &index in indices {
737 let cached_sample = CachedSample {
738 data: data.to_vec(),
739 timestamp,
740 access_count: 1,
741 };
742 cache.insert(index, cached_sample);
743 }
744
745 if cache.len() > 1000 {
747 self.evict_old_cache_entries(&mut cache);
749 }
750 }
751
752 fn evict_old_cache_entries(&self, cache: &mut HashMap<usize, CachedSample>) {
753 let cutoff_time = Instant::now() - Duration::from_secs(300); cache.retain(|_, sample| sample.timestamp > cutoff_time);
756 }
757
758 fn decompress_data(&self, data: &[u8]) -> Result<Vec<u8>> {
759 Ok(data.to_vec())
762 }
763
764 fn generate_operation_id(&self) -> u64 {
765 std::time::SystemTime::now()
766 .duration_since(std::time::UNIX_EPOCH)
767 .map(|d| d.as_nanos() as u64)
768 .unwrap_or(0)
769 }
770
771 fn generate_request_id(&self) -> u64 {
772 self.generate_operation_id()
773 }
774
775 fn synchronize_epoch(&self, epoch: usize) -> Result<()> {
776 Ok(())
779 }
780
781 fn coordinate_shuffle_seed(&self, seed: u64) -> Result<()> {
782 if self.config.rank == 0 {
784 let seed_data = oxicode::serde::encode_to_vec(&seed, oxicode::config::standard())
786 .map_err(|e| {
787 TensorError::invalid_operation_simple(format!("Seed serialization error: {e}"))
788 })?;
789
790 let broadcast_msg = DistributedMessage::CollectiveOp {
791 op_type: CollectiveOpType::Broadcast,
792 op_id: std::time::SystemTime::now()
793 .duration_since(std::time::UNIX_EPOCH)
794 .map(|d| d.as_nanos() as u64)
795 .unwrap_or(0),
796 data: Some(seed_data),
797 };
798
799 for rank in 1..self.config.world_size {
801 if let Err(e) = {
802 let comm_manager = self.comm_manager.lock().map_err(|_| {
803 TensorError::invalid_operation_simple(
804 "comm manager lock poisoned".to_string(),
805 )
806 })?;
807 comm_manager.send_request(rank, &broadcast_msg)
808 } {
809 return Err(TensorError::invalid_operation_simple(format!(
810 "Failed to send seed to rank {rank}: {e}"
811 )));
812 }
813 }
814 }
815 Ok(())
817 }
818
819 fn aggregate_statistics(
820 &self,
821 results: Vec<DistributedMessage>,
822 ) -> Result<DistributedLoadingStats> {
823 Ok(DistributedLoadingStats::default())
826 }
827}
828
829impl Sampler for EnhancedDistributedSampler {
831 fn sample_indices(&self, len: usize) -> Box<dyn Iterator<Item = usize> + Send> {
832 self.base_sampler.sample_indices(len)
834 }
835
836 fn is_random(&self) -> bool {
837 self.base_sampler.is_random()
838 }
839
840 fn set_seed(&mut self, seed: Option<u64>) {
841 }
844}
845
846impl CommunicationManager {
847 fn new(node_info: NodeInfo, config: DistributedLoadingConfig) -> Result<Self> {
848 Ok(Self {
849 node_info,
850 cluster_nodes: HashMap::new(),
851 connections: HashMap::new(),
852 listener: None,
853 config,
854 message_handlers: HashMap::new(),
855 })
856 }
857
858 fn broadcast_message(&self, message: &DistributedMessage) -> Result<Vec<DistributedMessage>> {
859 Ok(Vec::new())
862 }
863
864 fn send_request(
865 &self,
866 dest_rank: usize,
867 message: &DistributedMessage,
868 ) -> Result<DistributedMessage> {
869 if dest_rank >= self.config.world_size {
871 return Ok(DistributedMessage::Error {
872 message: format!("Invalid destination rank: {dest_rank}"),
873 });
874 }
875
876 let connections = &self.connections;
878 if let Some(connection) = connections.get(&dest_rank) {
879 let serialized_message =
881 oxicode::serde::encode_to_vec(message, oxicode::config::standard()).map_err(
882 |e| TensorError::invalid_operation_simple(format!("Serialization error: {e}")),
883 )?;
884
885 let mut stream = connection;
887 let msg_len = serialized_message.len() as u32;
888 let len_bytes = msg_len.to_be_bytes();
889
890 if stream.write_all(&len_bytes).is_err() {
891 return Ok(DistributedMessage::Error {
892 message: format!("Failed to send to rank {dest_rank}"),
893 });
894 }
895
896 if stream.write_all(&serialized_message).is_err() {
897 return Ok(DistributedMessage::Error {
898 message: format!("Failed to send message to rank {dest_rank}"),
899 });
900 }
901
902 let mut response_len_bytes = [0u8; 4];
904 if stream.read_exact(&mut response_len_bytes).is_err() {
905 return Ok(DistributedMessage::Error {
906 message: format!("Failed to read response length from rank {dest_rank}"),
907 });
908 }
909
910 let response_len = u32::from_be_bytes(response_len_bytes) as usize;
911 let mut response_data = vec![0u8; response_len];
912
913 if stream.read_exact(&mut response_data).is_err() {
914 return Ok(DistributedMessage::Error {
915 message: format!("Failed to read response from rank {dest_rank}"),
916 });
917 }
918
919 match oxicode::serde::decode_owned_from_slice::<DistributedMessage, _>(
921 &response_data,
922 oxicode::config::standard(),
923 )
924 .map(|(v, _)| v)
925 {
926 Ok(response) => Ok(response),
927 Err(e) => Ok(DistributedMessage::Error {
928 message: format!("Deserialization error: {e}"),
929 }),
930 }
931 } else {
932 Ok(DistributedMessage::Error {
933 message: format!("No connection to rank {dest_rank}"),
934 })
935 }
936 }
937
938 fn shutdown(&mut self) -> Result<()> {
939 self.connections.clear();
941
942 if let Some(listener) = self.listener.take() {
943 drop(listener);
944 }
945
946 Ok(())
947 }
948}
949
950impl RdmaContext {
951 fn new(device_name: Option<&String>) -> Result<Self> {
952 Ok(Self {
953 device_name: device_name.cloned().unwrap_or_else(|| "mlx5_0".to_string()),
954 initialized: false,
955 memory_regions: HashMap::new(),
956 })
957 }
958
959 fn initialize(&mut self) -> Result<()> {
960 self.initialized = true;
968 Ok(())
969 }
970
971 fn cleanup(&mut self) -> Result<()> {
972 self.memory_regions.clear();
974 self.initialized = false;
975 Ok(())
976 }
977
978 fn register_memory_region(&mut self, key: String, size: usize) -> Result<()> {
979 let mr = RdmaMemoryRegion {
983 addr: 0, size,
985 };
986
987 self.memory_regions.insert(key, mr);
988 Ok(())
989 }
990}
991
992pub fn create_distributed_dataloader<T, D>(
994 dataset: D,
995 config: DistributedLoadingConfig,
996 dataloader_config: DataLoaderConfig,
997) -> Result<DataLoader<T, D, EnhancedDistributedSampler>>
998where
999 T: Clone
1000 + Default
1001 + scirs2_core::numeric::Zero
1002 + Send
1003 + Sync
1004 + 'static
1005 + bytemuck::Pod
1006 + bytemuck::Zeroable,
1007 D: Dataset<T> + Send + Sync + 'static,
1008{
1009 let sampler = EnhancedDistributedSampler::new(config.world_size, config.rank, config)?;
1010
1011 Ok(DataLoader::new(dataset, sampler, dataloader_config))
1012}
1013
1014#[cfg(test)]
1015mod tests {
1016 use super::*;
1017 use crate::TensorDataset;
1018
1019 #[test]
1020 fn test_distributed_loading_config() {
1021 let config = DistributedLoadingConfig::default();
1022 assert_eq!(config.world_size, 1);
1023 assert_eq!(config.rank, 0);
1024 assert!(!config.enable_rdma);
1025 }
1026
1027 #[test]
1028 fn test_enhanced_distributed_sampler_creation() {
1029 let config = DistributedLoadingConfig::default();
1030 let sampler = EnhancedDistributedSampler::new(2, 0, config);
1031 assert!(sampler.is_ok());
1032 }
1033
1034 #[test]
1035 fn test_communication_manager_creation() {
1036 let node_info = NodeInfo {
1037 rank: 0,
1038 addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080),
1039 device_capabilities: vec!["Cpu".to_string()],
1040 rdma_enabled: false,
1041 rdma_device: None,
1042 };
1043
1044 let config = DistributedLoadingConfig::default();
1045 let comm_manager = CommunicationManager::new(node_info, config);
1046 assert!(comm_manager.is_ok());
1047 }
1048
1049 #[test]
1050 fn test_index_ownership() {
1051 let config = DistributedLoadingConfig {
1052 world_size: 4,
1053 rank: 1,
1054 ..Default::default()
1055 };
1056
1057 let sampler =
1058 EnhancedDistributedSampler::new(4, 1, config).expect("test: operation should succeed");
1059
1060 let dataset_len = 100;
1062 assert!(sampler.is_local_index(25, dataset_len)); assert!(!sampler.is_local_index(5, dataset_len)); assert_eq!(sampler.get_index_owner(5, dataset_len), 0);
1065 assert_eq!(sampler.get_index_owner(75, dataset_len), 3);
1066 }
1067
1068 #[test]
1069 fn test_rdma_context_initialization() {
1070 let rdma_ctx = RdmaContext::new(Some(&"mlx5_0".to_string()));
1071 assert!(rdma_ctx.is_ok());
1072
1073 let mut ctx = rdma_ctx.expect("test: operation should succeed");
1074 assert!(ctx.initialize().is_ok());
1075 assert!(ctx.initialized);
1076 }
1077}