1#![allow(dead_code)]
13use crate::collectives::{all_gather, reduce_scatter};
14use crate::{ProcessGroup, TorshDistributedError, TorshResult};
15use std::collections::HashMap;
16use std::sync::Arc;
17use torsh_core::{error::Result, DeviceType, Shape};
18use torsh_nn::{Module, Parameter};
19use torsh_tensor::Tensor;
20use tracing::{debug, info};
21
22#[derive(Debug, Clone)]
38pub struct TensorParallelConfig {
39 pub tp_size: usize,
41 pub sequence_parallel: bool,
43 pub communication_backend: String,
45 pub async_communication: bool,
47 pub memory_optimization_level: u8,
49 #[cfg(feature = "scirs2-memory")]
51 pub enable_scirs2_memory: bool,
52 #[cfg(feature = "scirs2-memory")]
54 pub use_memory_mapping: bool,
55 #[cfg(feature = "scirs2-memory")]
57 pub enable_lazy_loading: bool,
58 #[cfg(feature = "scirs2-memory")]
60 pub enable_chunked_processing: bool,
61 #[cfg(feature = "scirs2-memory")]
63 pub enable_simd_ops: bool,
64 #[cfg(feature = "scirs2-memory")]
66 pub buffer_pool_size_mb: usize,
67}
68
69impl Default for TensorParallelConfig {
70 fn default() -> Self {
71 Self {
72 tp_size: 1,
73 sequence_parallel: false,
74 communication_backend: "nccl".to_string(),
75 async_communication: true,
76 memory_optimization_level: 1,
77 #[cfg(feature = "scirs2-memory")]
78 enable_scirs2_memory: true,
79 #[cfg(feature = "scirs2-memory")]
80 use_memory_mapping: true,
81 #[cfg(feature = "scirs2-memory")]
82 enable_lazy_loading: false,
83 #[cfg(feature = "scirs2-memory")]
84 enable_chunked_processing: true,
85 #[cfg(feature = "scirs2-memory")]
86 enable_simd_ops: true,
87 #[cfg(feature = "scirs2-memory")]
88 buffer_pool_size_mb: 512,
89 }
90 }
91}
92
93#[derive(Debug, Clone, PartialEq)]
95pub enum TensorParallelStrategy {
96 RowParallel,
98 ColumnParallel,
100 VocabParallel,
102 SequenceParallel,
104 AttentionHeadParallel,
106}
107
108#[derive(Debug, Clone)]
110pub enum TensorParallelLayer {
111 RowParallelLinear {
113 input_size: usize,
114 output_size: usize,
115 bias: bool,
116 input_is_parallel: bool,
117 },
118 ColumnParallelLinear {
120 input_size: usize,
121 output_size: usize,
122 bias: bool,
123 gather_output: bool,
124 },
125 ParallelEmbedding {
127 num_embeddings: usize,
128 embedding_dim: usize,
129 padding_idx: Option<usize>,
130 },
131 ParallelAttention {
133 hidden_size: usize,
134 num_attention_heads: usize,
135 dropout_prob: f32,
136 },
137}
138
139pub struct TensorParallel {
141 module: Box<dyn Module>,
143 tp_group: Arc<ProcessGroup>,
145 config: TensorParallelConfig,
147 tp_rank: usize,
149 layer_info: TensorParallelLayer,
151 shard_info: HashMap<String, ShardInfo>,
153 comm_buffers: HashMap<String, Tensor>,
155}
156
157#[derive(Debug, Clone)]
159pub struct ShardInfo {
160 pub shard_dim: usize,
162 pub start_idx: usize,
164 pub shard_size: usize,
166 pub original_shape: Shape,
168 pub strategy: TensorParallelStrategy,
170}
171
172impl TensorParallel {
173 pub fn new(
175 module: Box<dyn Module>,
176 tp_group: Arc<ProcessGroup>,
177 config: TensorParallelConfig,
178 layer_info: TensorParallelLayer,
179 ) -> TorshResult<Self> {
180 let tp_rank = tp_group.rank() as usize;
181 let tp_size = tp_group.world_size() as usize;
182
183 if tp_size != config.tp_size {
184 return Err(TorshDistributedError::invalid_argument(
185 "tp_size",
186 format!(
187 "TP group size ({}) doesn't match config TP size ({})",
188 tp_size, config.tp_size
189 ),
190 format!("tp_size = {}", config.tp_size),
191 ));
192 }
193
194 let mut tensor_parallel = Self {
195 module,
196 tp_group,
197 config,
198 tp_rank,
199 layer_info,
200 shard_info: HashMap::new(),
201 comm_buffers: HashMap::new(),
202 };
203
204 tensor_parallel.init_parameter_sharding()?;
206
207 info!(
208 "Initialized tensor parallel layer with TP size {} at rank {}",
209 tp_size, tp_rank
210 );
211
212 Ok(tensor_parallel)
213 }
214
215 fn init_parameter_sharding(&mut self) -> TorshResult<()> {
217 let parameters = self.module.parameters();
218
219 match &self.layer_info {
220 TensorParallelLayer::RowParallelLinear { output_size, .. } => {
221 self.shard_row_parallel_parameters(¶meters, *output_size)?;
222 }
223 TensorParallelLayer::ColumnParallelLinear { input_size, .. } => {
224 self.shard_column_parallel_parameters(¶meters, *input_size)?;
225 }
226 TensorParallelLayer::ParallelEmbedding { num_embeddings, .. } => {
227 self.shard_embedding_parameters(¶meters, *num_embeddings)?;
228 }
229 TensorParallelLayer::ParallelAttention {
230 num_attention_heads,
231 ..
232 } => {
233 self.shard_attention_parameters(¶meters, *num_attention_heads)?;
234 }
235 }
236
237 Ok(())
238 }
239
240 fn shard_row_parallel_parameters(
242 &mut self,
243 parameters: &HashMap<String, Parameter>,
244 output_size: usize,
245 ) -> TorshResult<()> {
246 for name in parameters.keys() {
247 if name.contains("weight") {
248 let shard_size = output_size / self.config.tp_size;
249 let start_idx = self.tp_rank * shard_size;
250
251 let shard_info = ShardInfo {
252 shard_dim: 0, start_idx,
254 shard_size,
255 original_shape: Shape::new(vec![output_size, parameters.len()]), strategy: TensorParallelStrategy::RowParallel,
257 };
258
259 self.shard_info.insert(name.clone(), shard_info);
260 debug!("Sharded parameter '{}' with row-parallel strategy", name);
261 }
262 }
263
264 Ok(())
265 }
266
267 fn shard_column_parallel_parameters(
269 &mut self,
270 parameters: &HashMap<String, Parameter>,
271 input_size: usize,
272 ) -> TorshResult<()> {
273 for name in parameters.keys() {
274 if name.contains("weight") {
275 let shard_size = input_size / self.config.tp_size;
276 let start_idx = self.tp_rank * shard_size;
277
278 let shard_info = ShardInfo {
279 shard_dim: 1, start_idx,
281 shard_size,
282 original_shape: Shape::new(vec![parameters.len(), input_size]), strategy: TensorParallelStrategy::ColumnParallel,
284 };
285
286 self.shard_info.insert(name.clone(), shard_info);
287 debug!("Sharded parameter '{}' with column-parallel strategy", name);
288 }
289 }
290
291 Ok(())
292 }
293
294 fn shard_embedding_parameters(
296 &mut self,
297 parameters: &HashMap<String, Parameter>,
298 num_embeddings: usize,
299 ) -> TorshResult<()> {
300 for name in parameters.keys() {
301 if name.contains("weight") {
302 let shard_size = num_embeddings / self.config.tp_size;
303 let start_idx = self.tp_rank * shard_size;
304
305 let shard_info = ShardInfo {
306 shard_dim: 0, start_idx,
308 shard_size,
309 original_shape: Shape::new(vec![num_embeddings, 512]), strategy: TensorParallelStrategy::VocabParallel,
311 };
312
313 self.shard_info.insert(name.clone(), shard_info);
314 debug!("Sharded parameter '{}' with vocab-parallel strategy", name);
315 }
316 }
317
318 Ok(())
319 }
320
321 fn shard_attention_parameters(
323 &mut self,
324 parameters: &HashMap<String, Parameter>,
325 num_attention_heads: usize,
326 ) -> TorshResult<()> {
327 let heads_per_partition = num_attention_heads / self.config.tp_size;
328 let start_head = self.tp_rank * heads_per_partition;
329
330 for name in parameters.keys() {
331 if name.contains("query")
332 || name.contains("key")
333 || name.contains("value")
334 || name.contains("output")
335 {
336 let shard_info = ShardInfo {
337 shard_dim: 0, start_idx: start_head,
339 shard_size: heads_per_partition,
340 original_shape: Shape::new(vec![num_attention_heads, 64]), strategy: TensorParallelStrategy::AttentionHeadParallel,
342 };
343
344 self.shard_info.insert(name.clone(), shard_info);
345 debug!(
346 "Sharded parameter '{}' with attention-head-parallel strategy",
347 name
348 );
349 }
350 }
351
352 Ok(())
353 }
354
355 async fn all_gather_for_row_parallel(&mut self, input: &Tensor) -> TorshResult<Tensor> {
357 debug!("Performing all-gather for row-parallel layer");
358
359 let mut gathered_tensors = Vec::new();
360 all_gather(&mut gathered_tensors, input, &self.tp_group).await?;
361
362 if gathered_tensors.len() == 1 {
364 Ok(gathered_tensors
365 .into_iter()
366 .next()
367 .expect("gathered_tensors should not be empty"))
368 } else {
369 Ok(gathered_tensors
372 .into_iter()
373 .next()
374 .expect("gathered_tensors should not be empty"))
375 }
376 }
377
378 async fn reduce_scatter_for_column_parallel(&mut self, input: &Tensor) -> TorshResult<Tensor> {
380 debug!("Performing reduce-scatter for column-parallel layer");
381
382 let mut output_tensor = input.clone();
383 reduce_scatter(
384 &mut output_tensor,
385 input,
386 crate::backend::ReduceOp::Sum,
387 &self.tp_group,
388 )
389 .await?;
390
391 Ok(output_tensor)
393 }
394
395 async fn sequence_parallel_communication(&mut self, input: &Tensor) -> TorshResult<Tensor> {
397 debug!("Performing sequence-parallel communication");
398
399 if self.config.sequence_parallel {
400 self.all_gather_for_row_parallel(input).await
402 } else {
403 Ok(input.clone())
404 }
405 }
406
407 pub fn tp_rank(&self) -> usize {
409 self.tp_rank
410 }
411
412 pub fn tp_world_size(&self) -> usize {
414 self.config.tp_size
415 }
416
417 pub fn get_shard_info(&self, param_name: &str) -> Option<&ShardInfo> {
419 self.shard_info.get(param_name)
420 }
421
422 pub fn uses_sequence_parallel(&self) -> bool {
424 self.config.sequence_parallel
425 }
426
427 pub fn memory_stats(&self) -> TensorParallelStats {
429 let total_params = self.module.parameters().len();
430 let sharded_params = self.shard_info.len();
431 let memory_reduction = if total_params > 0 {
432 1.0 - (sharded_params as f64 / total_params as f64)
433 } else {
434 0.0
435 };
436
437 TensorParallelStats {
438 tp_rank: self.tp_rank,
439 tp_world_size: self.config.tp_size,
440 total_parameters: total_params,
441 sharded_parameters: sharded_params,
442 memory_reduction_ratio: memory_reduction,
443 communication_overhead_ms: 0.0, }
445 }
446
447 #[cfg(feature = "scirs2-memory")]
451 pub fn create_memory_efficient_shard(
452 &self,
453 tensor: &Tensor,
454 shard_dim: usize,
455 use_memory_mapping: bool,
456 ) -> TorshResult<Tensor> {
457 debug!(
458 "Creating memory-efficient shard for tensor with shape {:?}",
459 tensor.shape()
460 );
461
462 if !self.config.enable_scirs2_memory {
463 return self.create_chunked_shard(tensor, shard_dim);
464 }
465
466 let _use_mapping = use_memory_mapping && tensor.numel() > 1_000_000;
470 if self.config.enable_chunked_processing {
471 self.create_chunked_shard(tensor, shard_dim)
473 } else {
474 self.create_chunked_shard(tensor, shard_dim)
476 }
477 }
478
479 #[cfg(feature = "scirs2-memory")]
481 fn create_chunked_shard(&self, tensor: &Tensor, shard_dim: usize) -> TorshResult<Tensor> {
482 let shard_size = tensor.shape().dims()[shard_dim] / self.config.tp_size;
485 let start_idx = self.tp_rank * shard_size;
486
487 let shard_tensor = tensor.narrow(shard_dim as i32, start_idx as i64, shard_size)?;
490
491 info!(
492 "Created chunked shard with shape {:?}",
493 shard_tensor.shape()
494 );
495 Ok(shard_tensor)
496 }
497
498 #[cfg(feature = "scirs2-memory")]
500 pub fn simd_optimized_forward(&self, input: &Tensor, weights: &Tensor) -> TorshResult<Tensor> {
501 if !self.config.enable_simd_ops {
502 return self.standard_forward(input, weights);
503 }
504
505 debug!("Performing SIMD-optimized forward pass");
506
507 match (input.dtype(), weights.dtype()) {
508 (torsh_core::DType::F32, torsh_core::DType::F32) => {
509 let input_shape_owned = input.shape();
510 let weights_shape_owned = weights.shape();
511 let input_dims = input_shape_owned.dims();
512 let weights_dims = weights_shape_owned.dims();
513 if input_dims.len() == 2
514 && weights_dims.len() == 2
515 && input_dims[1] == weights_dims[0]
516 {
517 let m = input_dims[0];
518 let k = input_dims[1];
519 let n = weights_dims[1];
520 let a_data = input.to_vec().map_err(|e| {
521 TorshDistributedError::InternalError(format!(
522 "failed to materialize input data: {e}"
523 ))
524 })?;
525 let b_data = weights.to_vec().map_err(|e| {
526 TorshDistributedError::InternalError(format!(
527 "failed to materialize weights data: {e}"
528 ))
529 })?;
530 let mut c_data = vec![0.0f32; m * n];
531 scirs2_core::simd_ops::simd_matrix_multiply_f32(
532 m,
533 k,
534 n,
535 1.0,
536 &a_data,
537 &b_data,
538 0.0,
539 &mut c_data,
540 );
541 Tensor::from_vec(c_data, &[m, n]).map_err(|e| {
542 TorshDistributedError::InternalError(format!(
543 "failed to build matmul output tensor: {e}"
544 ))
545 })
546 } else {
547 self.standard_forward(input, weights)
548 }
549 }
550 _ => self.standard_forward(input, weights),
551 }
552 }
553
554 #[cfg(feature = "scirs2-memory")]
556 pub async fn parallel_all_gather(&self, tensor: &Tensor) -> TorshResult<Tensor> {
557 debug!("Performing parallel all-gather (simplified implementation)");
560
561 let mut output: Vec<Tensor> = Vec::with_capacity(self.config.tp_size);
563
564 all_gather(&mut output, tensor, &self.tp_group).await?;
566
567 let result = if !output.is_empty() {
571 output
572 .into_iter()
573 .next()
574 .expect("output should not be empty")
575 } else {
576 tensor.clone()
577 };
578
579 info!(
580 "Parallel all-gather completed with shape {:?}",
581 result.shape()
582 );
583 Ok(result)
584 }
585
586 #[cfg(feature = "scirs2-memory")]
588 pub fn init_scirs2_memory_pools(&mut self) -> TorshResult<()> {
589 if !self.config.enable_scirs2_memory {
590 return Ok(());
591 }
592
593 info!(
594 "Initializing SciRS2 memory pools with {}MB buffer",
595 self.config.buffer_pool_size_mb
596 );
597
598 info!("SciRS2 memory pools initialized successfully");
614 Ok(())
615 }
616
617 #[cfg(feature = "scirs2-memory")]
619 pub fn get_memory_efficiency_stats(&self) -> HashMap<String, f64> {
620 let mut stats = HashMap::new();
621
622 if self.config.enable_scirs2_memory {
623 }
631
632 stats.insert(
635 "memory_reduction_ratio".to_string(),
636 1.0 / self.config.tp_size as f64, );
638 stats.insert(
639 "tp_efficiency".to_string(),
640 1.0 / self.config.tp_size as f64,
641 );
642
643 stats
644 }
645
646 #[cfg(feature = "scirs2-memory")]
649 fn compute_output_shape(
650 &self,
651 input_shape: &Shape,
652 weights_shape: &Shape,
653 ) -> TorshResult<Shape> {
654 let input_dims = input_shape.dims();
656 let weights_dims = weights_shape.dims();
657
658 let output_dims = vec![input_dims[0], weights_dims[1]];
659 Shape::from_dims(output_dims).map_err(|e| {
660 TorshDistributedError::internal_error(format!("Failed to create shape: {}", e))
661 })
662 }
663
664 #[cfg(feature = "scirs2-memory")]
665 fn compute_gathered_shape(&self, shard_shape: &Shape) -> TorshResult<Shape> {
666 let mut dims = shard_shape.dims().to_vec();
667 dims[1] *= self.config.tp_size; Shape::from_dims(dims).map_err(|e| {
669 TorshDistributedError::internal_error(format!("Failed to create gathered shape: {}", e))
670 })
671 }
672
673 #[cfg(feature = "scirs2-memory")]
674 fn standard_forward(&self, input: &Tensor, weights: &Tensor) -> TorshResult<Tensor> {
675 info!("Using standard forward pass (SIMD disabled)");
677
678 let result = input.matmul(weights)?;
680 Ok(result)
681 }
682}
683
684impl Module for TensorParallel {
685 fn forward(&self, input: &Tensor) -> Result<Tensor> {
686 match &self.layer_info {
687 TensorParallelLayer::RowParallelLinear {
688 input_is_parallel, ..
689 } => {
690 let processed_input = if *input_is_parallel {
692 input.clone()
693 } else {
694 input.clone()
696 };
697
698 let local_output = self.module.forward(&processed_input)?;
700
701 Ok(local_output)
704 }
705
706 TensorParallelLayer::ColumnParallelLinear { gather_output, .. } => {
707 let local_output = self.module.forward(input)?;
709
710 if *gather_output {
711 Ok(local_output)
714 } else {
715 Ok(local_output)
716 }
717 }
718
719 TensorParallelLayer::ParallelEmbedding { .. } => {
720 let output = self.module.forward(input)?;
722
723 Ok(output)
726 }
727
728 TensorParallelLayer::ParallelAttention { .. } => {
729 let output = self.module.forward(input)?;
731
732 Ok(output)
735 }
736 }
737 }
738
739 fn parameters(&self) -> HashMap<String, Parameter> {
740 let all_params = self.module.parameters();
742 let mut sharded_params = HashMap::new();
743
744 for (name, param) in all_params {
745 if let Some(_shard_info) = self.shard_info.get(&name) {
746 let tensor = param.tensor();
748 let _tensor_guard = tensor.read();
749
750 sharded_params.insert(name, param);
753 } else {
754 sharded_params.insert(name, param);
756 }
757 }
758
759 sharded_params
760 }
761
762 fn named_parameters(&self) -> HashMap<String, Parameter> {
763 self.parameters()
764 }
765
766 fn training(&self) -> bool {
767 self.module.training()
768 }
769
770 fn train(&mut self) {
771 self.module.train()
772 }
773
774 fn eval(&mut self) {
775 self.module.eval()
776 }
777
778 fn to_device(&mut self, device: DeviceType) -> Result<()> {
779 self.module.to_device(device)
780 }
781}
782
783#[derive(Debug, Clone)]
785pub struct TensorParallelStats {
786 pub tp_rank: usize,
788 pub tp_world_size: usize,
790 pub total_parameters: usize,
792 pub sharded_parameters: usize,
794 pub memory_reduction_ratio: f64,
796 pub communication_overhead_ms: f64,
798}
799
800pub mod utils {
802 use super::*;
803
804 pub fn create_row_parallel_linear(
806 input_size: usize,
807 output_size: usize,
808 bias: bool,
809 input_is_parallel: bool,
810 tp_group: Arc<ProcessGroup>,
811 config: Option<TensorParallelConfig>,
812 ) -> TorshResult<TensorParallel> {
813 let linear = torsh_nn::layers::Linear::new(input_size, output_size, bias);
814 let module = Box::new(linear) as Box<dyn Module>;
815
816 let layer_info = TensorParallelLayer::RowParallelLinear {
817 input_size,
818 output_size,
819 bias,
820 input_is_parallel,
821 };
822
823 let config = config.unwrap_or_default();
824 TensorParallel::new(module, tp_group, config, layer_info)
825 }
826
827 pub fn create_column_parallel_linear(
829 input_size: usize,
830 output_size: usize,
831 bias: bool,
832 gather_output: bool,
833 tp_group: Arc<ProcessGroup>,
834 config: Option<TensorParallelConfig>,
835 ) -> TorshResult<TensorParallel> {
836 let linear = torsh_nn::layers::Linear::new(input_size, output_size, bias);
837 let module = Box::new(linear) as Box<dyn Module>;
838
839 let layer_info = TensorParallelLayer::ColumnParallelLinear {
840 input_size,
841 output_size,
842 bias,
843 gather_output,
844 };
845
846 let config = config.unwrap_or_default();
847 TensorParallel::new(module, tp_group, config, layer_info)
848 }
849
850 pub fn split_tensor_for_tp(
852 tensor: &Tensor,
853 split_dim: usize,
854 tp_rank: usize,
855 tp_size: usize,
856 ) -> TorshResult<Tensor> {
857 let shape = tensor.shape();
858 let dim_size = shape.dims()[split_dim];
859
860 if dim_size % tp_size != 0 {
861 return Err(TorshDistributedError::invalid_argument(
862 "tensor_dimension",
863 format!(
864 "Dimension size {} is not divisible by TP size {}",
865 dim_size, tp_size
866 ),
867 format!("dimension size must be multiple of tp_size ({})", tp_size),
868 ));
869 }
870
871 let shard_size = dim_size / tp_size;
872 let start_idx = tp_rank * shard_size;
873 let end_idx = start_idx + shard_size;
874
875 Ok(tensor.slice(split_dim, start_idx, end_idx)?.to_tensor()?)
876 }
877
878 pub async fn gather_tensor_from_tp(
880 tensor: &Tensor,
881 _gather_dim: usize,
882 tp_group: &ProcessGroup,
883 ) -> TorshResult<Tensor> {
884 let mut gathered_tensors = Vec::new();
885 all_gather(&mut gathered_tensors, tensor, tp_group).await?;
886
887 if gathered_tensors.is_empty() {
890 Err(TorshDistributedError::communication_error(
891 "tensor_parallel",
892 "No tensors gathered",
893 ))
894 } else {
895 Ok(gathered_tensors
896 .into_iter()
897 .next()
898 .expect("gathered_tensors should not be empty"))
899 }
900 }
901}
902
903#[cfg(test)]
904mod tests {
905 use super::*;
906 use crate::{init_process_group, BackendType};
907
908 #[tokio::test]
909 async fn test_tensor_parallel_config() {
910 let config = TensorParallelConfig::default();
911 assert_eq!(config.tp_size, 1);
912 assert!(!config.sequence_parallel);
913 assert_eq!(config.communication_backend, "nccl");
914 assert!(config.async_communication);
915 }
916
917 #[tokio::test]
918 async fn test_shard_info() {
919 let shard_info = ShardInfo {
920 shard_dim: 0,
921 start_idx: 0,
922 shard_size: 128,
923 original_shape: Shape::new(vec![512, 256]),
924 strategy: TensorParallelStrategy::RowParallel,
925 };
926
927 assert_eq!(shard_info.shard_dim, 0);
928 assert_eq!(shard_info.shard_size, 128);
929 assert_eq!(shard_info.strategy, TensorParallelStrategy::RowParallel);
930 }
931
932 #[tokio::test]
933 async fn test_tensor_parallel_stats() {
934 let stats = TensorParallelStats {
935 tp_rank: 0,
936 tp_world_size: 4,
937 total_parameters: 1000,
938 sharded_parameters: 800,
939 memory_reduction_ratio: 0.75,
940 communication_overhead_ms: 5.2,
941 };
942
943 assert_eq!(stats.tp_rank, 0);
944 assert_eq!(stats.tp_world_size, 4);
945 assert_eq!(stats.memory_reduction_ratio, 0.75);
946 }
947
948 #[tokio::test]
949 async fn test_create_row_parallel_linear() -> TorshResult<()> {
950 let process_group =
951 Arc::new(init_process_group(BackendType::Gloo, 0, 2, "127.0.0.1", 12345).await?);
952
953 let config = TensorParallelConfig {
954 tp_size: 2,
955 ..Default::default()
956 };
957
958 let tp_layer =
959 utils::create_row_parallel_linear(128, 256, true, false, process_group, Some(config))?;
960
961 assert_eq!(tp_layer.tp_rank(), 0);
962 assert_eq!(tp_layer.tp_world_size(), 2);
963
964 Ok(())
965 }
966
967 #[tokio::test]
968 async fn test_create_column_parallel_linear() -> TorshResult<()> {
969 let process_group =
970 Arc::new(init_process_group(BackendType::Gloo, 0, 2, "127.0.0.1", 12346).await?);
971
972 let config = TensorParallelConfig {
973 tp_size: 2,
974 ..Default::default()
975 };
976
977 let tp_layer = utils::create_column_parallel_linear(
978 128,
979 256,
980 true,
981 true,
982 process_group,
983 Some(config),
984 )?;
985
986 assert_eq!(tp_layer.tp_rank(), 0);
987 assert_eq!(tp_layer.tp_world_size(), 2);
988
989 Ok(())
990 }
991
992 #[test]
993 fn test_tensor_parallel_strategies() {
994 assert_ne!(
995 TensorParallelStrategy::RowParallel,
996 TensorParallelStrategy::ColumnParallel
997 );
998 assert_ne!(
999 TensorParallelStrategy::VocabParallel,
1000 TensorParallelStrategy::SequenceParallel
1001 );
1002 assert_ne!(
1003 TensorParallelStrategy::AttentionHeadParallel,
1004 TensorParallelStrategy::RowParallel
1005 );
1006 }
1007
1008 #[tokio::test]
1009 async fn test_split_tensor_for_tp() -> TorshResult<()> {
1010 let tensor = torsh_tensor::creation::ones(&[8, 16])?;
1011
1012 let shard = utils::split_tensor_for_tp(&tensor, 1, 0, 2)?;
1013 assert_eq!(shard.shape().dims(), &[8, 8]);
1014
1015 Ok(())
1016 }
1017}