burn_backend/backend/distributed/ops.rs
1use alloc::vec::Vec;
2
3use crate::{
4 Backend, DeviceId, TensorMetadata,
5 distributed::CollectiveTensor,
6 tensor::{Device, FloatTensor},
7};
8
9use crate::distributed::{DistributedConfig, DistributedParams, ReduceOperation};
10
11#[cfg(feature = "std")]
12use crate::distributed::{
13 close_distributed_sync_server, get_distributed_sync_client, start_distributed_sync_server,
14};
15
16/// Mutable reference to a float tensor.
17#[derive(Clone)]
18pub struct TensorRef<B: Backend>(pub *mut FloatTensor<B>);
19unsafe impl<B> Sync for TensorRef<B> where B: Backend {}
20unsafe impl<B> Send for TensorRef<B> where B: Backend {}
21
22// TODO : The following functions should be moved in the `burn-autodiff` crate. The difficulty is in not discriminating between
23// the dispatch backend and its inner dispatched backend when calling the communication server API. This implementation makes
24// it easy by implementing `DistributedOps` for `DispatchBackend`.
25// The functions in question :
26// * `start_communication_server`
27// * `close_communication_server`
28// * `register_sync_parameters`
29// * `submit_sync_collective`
30// * `submit_gradient_sync`
31
32/// Operations on communication tensors.
33pub trait DistributedOps<B: Backend> {
34 /// Start the communication server used to orchestrate tensor syncing between devices.
35 ///
36 /// # Arguments
37 ///
38 /// * `devices` - The devices to orchestrate.
39 fn start_communication_server(devices: &[B::Device], config: DistributedConfig) {
40 #[cfg(feature = "std")]
41 start_distributed_sync_server::<B>(devices, config);
42 #[cfg(not(feature = "std"))]
43 let _ = (devices, config);
44 }
45
46 /// Close the communication server used to orchestrate syncing between devices.
47 ///
48 /// # Arguments
49 ///
50 /// * `device` - A device on the backend.
51 fn close_communication_server(_device: &B::Device) {
52 #[cfg(feature = "std")]
53 close_distributed_sync_server::<B>();
54 }
55
56 /// Register the parameters that will require gradient synchronization for the upcoming backward pass.
57 ///
58 /// This must be called before the backward pass on each device so the gradient sync server
59 /// can coordinate collective operations across all devices.
60 ///
61 /// # Arguments
62 ///
63 /// * `device` - The device calling the initialization.
64 /// * `distributed_params` - A list of [`DistributedParams`] of the tensors to sync.
65 fn register_sync_parameters(_device: &B::Device, distributed_params: Vec<DistributedParams>) {
66 #[cfg(feature = "std")]
67 if let Some(sync_client) = get_distributed_sync_client::<B>() {
68 sync_client.register_sync_parameters(distributed_params);
69 };
70 #[cfg(not(feature = "std"))]
71 let _ = distributed_params;
72 }
73
74 /// Tell the gradient sync server that this device has submitted all its sync operations and is ready to be synchronized.
75 ///
76 /// # Arguments
77 ///
78 /// * `device` - The device on which to sync.
79 fn submit_sync_collective(device: &B::Device) {
80 #[cfg(feature = "std")]
81 if let Some(sync_client) = get_distributed_sync_client::<B>() {
82 sync_client.submit_sync_collective(device.clone());
83 };
84 #[cfg(not(feature = "std"))]
85 let _ = device;
86 }
87
88 /// Submit a gradient tensor for synchronization across all devices.
89 ///
90 /// The gradient is sent to the gradient sync server, which will launch the all-reduce
91 /// operation once all devices have submitted their gradient for this parameter.
92 ///
93 /// # Arguments
94 ///
95 /// * `tensor` - The tensor to synchronize.
96 /// * `distributed_params` - The [`DistributedParams`] for the parameter.
97 fn submit_gradient_sync(tensor: TensorRef<B>, distributed_params: DistributedParams) {
98 #[cfg(feature = "std")]
99 if let Some(sync_client) = get_distributed_sync_client::<B>() {
100 sync_client.submit_gradient_sync(tensor, distributed_params);
101 };
102 #[cfg(not(feature = "std"))]
103 let _ = (tensor, distributed_params);
104 }
105
106 /// all_reduce operation.
107 ///
108 /// # Arguments
109 ///
110 /// * `tensors` - The tensors on which to perform all_reduce.
111 /// * `op` - The [`ReduceOperation`].
112 ///
113 /// # Returns
114 ///
115 /// The corresponding [CollectiveTensor].
116 fn all_reduce(
117 _tensor: FloatTensor<B>,
118 _op: ReduceOperation,
119 _device_ids: Vec<DeviceId>,
120 ) -> CollectiveTensor<B> {
121 unimplemented!()
122 }
123
124 /// Sync the collective operations.
125 ///
126 /// # Arguments
127 ///
128 /// * `device` - The device to sync.
129 fn sync_collective(_device: &B::Device) {
130 unimplemented!()
131 }
132
133 /// Get the device of the tensor reference.
134 ///
135 /// # Arguments
136 ///
137 /// * `tensor` - The tensor reference.
138 ///
139 /// # Returns
140 ///
141 /// The device of the tensor.
142 ///
143 /// # Safety
144 ///
145 /// Ensure that the tensors are not accessed/modified when calling.
146 unsafe fn comm_device(tensor: &TensorRef<B>) -> Device<B> {
147 unsafe { &(*tensor.0) }.device()
148 }
149
150 /// Returns a clone of the float tensor from the tensor reference.
151 ///
152 /// # Arguments
153 ///
154 /// * `tensor` - The tensor.
155 ///
156 /// # Returns
157 ///
158 /// A float tensor containing a copy of the data of the given tensor.
159 ///
160 /// # Safety
161 ///
162 /// Ensure that the tensors are not accessed/modified when calling.
163 unsafe fn float_from_ref(tensor: &TensorRef<B>) -> FloatTensor<B> {
164 unsafe { (*tensor.0).clone() }
165 }
166}