Skip to main content

burn_core/module/param/
running.rs

1use super::ParamId;
2use crate::module::{
3    AutodiffModule, Content, Module, ModuleDisplay, ModuleDisplayDefault, ModuleMapper,
4    ModuleVisitor, Param,
5};
6
7use alloc::string::ToString;
8use alloc::vec::Vec;
9
10#[cfg(target_has_atomic = "ptr")]
11use alloc::sync::Arc;
12
13#[cfg(not(target_has_atomic = "ptr"))]
14use portable_atomic_util::Arc;
15
16use burn_std::stub::Mutex;
17use burn_tensor::{Device, Tensor};
18
19#[cfg(feature = "std")]
20mod threading {
21    pub(super) use std::collections::HashMap;
22    pub(super) use std::thread::ThreadId;
23
24    #[inline(always)]
25    pub(super) fn get_thread_current_id() -> ThreadId {
26        std::thread::current().id()
27    }
28}
29
30#[cfg(not(feature = "std"))]
31mod threading {
32    pub(super) use burn_std::stub::ThreadId;
33    pub(super) use hashbrown::HashMap;
34
35    #[inline(always)]
36    pub(super) fn get_thread_current_id() -> ThreadId {
37        panic!("Current thread id is not available")
38    }
39}
40
41// Re-export items from the disabled/enabled blocks
42use threading::*;
43
44/// A state that can be updated during the forward pass while being thread safe.
45///
46/// # Note
47///
48/// The state value is the average of all updates on all threads.
49#[derive(Clone, Debug)]
50pub struct RunningState<V> {
51    id: ParamId,
52    values: Arc<Mutex<HashMap<ThreadId, V>>>,
53    value: Arc<Mutex<V>>,
54}
55
56// Implement display for the module
57
58impl<V> core::fmt::Display for RunningState<V> {
59    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
60        write!(f, "RunningState(id={})", self.id)
61    }
62}
63
64impl<V> ModuleDisplayDefault for RunningState<V> {
65    fn content(&self, content: Content) -> Option<Content> {
66        content
67            .add_formatted(&"RunningState".to_string())
68            .optional()
69    }
70}
71
72impl<V> ModuleDisplay for RunningState<V> {}
73
74impl<const D: usize> Module for RunningState<Tensor<D>> {
75    fn visit<V: ModuleVisitor>(&self, visitor: &mut V) {
76        let tensor = self.value.lock().unwrap();
77        let param = Param::initialized(self.id, tensor.clone());
78        visitor.visit_float(&param)
79    }
80
81    fn map<M: ModuleMapper>(self, mapper: &mut M) -> Self {
82        let mut tensor = self.value.lock().unwrap();
83        let param = Param::initialized(self.id, tensor.clone());
84        let param_out = mapper.map_float(param);
85        let (_, tensor_out, _) = param_out.consume();
86
87        *tensor = tensor_out;
88        core::mem::drop(tensor);
89
90        self
91    }
92
93    fn to_device(self, device: &Device) -> Self {
94        let mut tensor = self.value.lock().unwrap();
95        let tensor_out = tensor.clone().to_device(device);
96
97        *tensor = tensor_out;
98        core::mem::drop(tensor);
99
100        self
101    }
102
103    fn fork(self, device: &Device) -> Self {
104        self.to_device(device) // Same thing here since no grad.
105    }
106
107    fn collect_devices(&self, mut devices: Vec<Device>) -> Vec<Device> {
108        let device = self.value.lock().unwrap().device();
109
110        if !devices.contains(&device) {
111            devices.push(device)
112        }
113
114        devices
115    }
116}
117
118impl<const D: usize> RunningState<Tensor<D>> {
119    /// Create a new running state.
120    pub fn new(value: Tensor<D>) -> Self {
121        Self {
122            id: ParamId::new(),
123            values: Arc::new(Mutex::new(HashMap::new())),
124            value: Arc::new(Mutex::new(value)),
125        }
126    }
127
128    /// Create a new running state.
129    pub fn with_id(id: ParamId, value: Tensor<D>) -> Self {
130        Self {
131            id,
132            values: Arc::new(Mutex::new(HashMap::new())),
133            value: Arc::new(Mutex::new(value)),
134        }
135    }
136
137    /// Create a new running state from a record.
138    pub fn from_record(record: Param<Tensor<D>>) -> Self {
139        let tensor = record.val();
140        Self {
141            id: record.id,
142            values: Arc::new(Mutex::new(HashMap::new())),
143            value: Arc::new(Mutex::new(tensor)),
144        }
145    }
146
147    /// Update the value on the current thread.
148    pub fn update(&self, value: Tensor<D>) {
149        let thread_id = get_thread_current_id();
150        let mut map = self.values.lock().unwrap();
151
152        if map.contains_key(&thread_id) {
153            self.update_value(&mut map);
154        }
155
156        map.insert(thread_id, value);
157    }
158
159    /// Get the current value,
160    ///
161    /// # Note
162    ///
163    /// The current value might be outdated by one update.
164    pub fn value(&self) -> Tensor<D> {
165        let value = self.value.lock().unwrap();
166        value.clone()
167    }
168
169    /// Get the current value and make sure it is sync.
170    ///
171    /// # Note
172    ///
173    /// Don't use this function after an update on the same thread where other threads might have to
174    /// register their update before the actual synchronization needs to happen.
175    pub fn value_sync(&self) -> Tensor<D> {
176        let thread_id = get_thread_current_id();
177        let mut map = self.values.lock().unwrap();
178
179        if map.contains_key(&thread_id) {
180            self.update_value(&mut map);
181        }
182
183        let value = self.value.lock().unwrap();
184        value.clone()
185    }
186
187    fn sync(&self) {
188        let mut map = self.values.lock().unwrap();
189
190        if !map.is_empty() {
191            self.update_value(&mut map);
192        }
193    }
194
195    fn update_value(&self, map: &mut HashMap<ThreadId, Tensor<D>>) {
196        let mut value_updated: Option<Tensor<D>> = None;
197        let mut counter = 0;
198
199        for (_key, tensor) in map.drain() {
200            counter += 1;
201
202            value_updated = match value_updated {
203                Some(current) => {
204                    let device = current.device();
205                    Some(tensor.to_device(&device).add(current))
206                }
207                None => Some(tensor),
208            };
209        }
210
211        if let Some(value) = value_updated {
212            let value = value.div_scalar(counter);
213            let mut value_old = self.value.lock().unwrap();
214            *value_old = value;
215        }
216    }
217}
218
219impl<const D: usize> AutodiffModule for RunningState<Tensor<D>> {
220    fn valid(&self) -> Self {
221        self.sync();
222        let value = self.value();
223
224        RunningState::with_id(self.id, value.inner())
225    }
226
227    fn from_inner(module: Self) -> Self {
228        module.sync();
229        let value = module.value();
230
231        RunningState::with_id(module.id, Tensor::from_inner(value))
232    }
233}