use super::ParamId;
use crate::module::{
AutodiffModule, Content, Module, ModuleDisplay, ModuleDisplayDefault, ModuleMapper,
ModuleVisitor, Param,
};
use alloc::string::ToString;
use alloc::vec::Vec;
#[cfg(target_has_atomic = "ptr")]
use alloc::sync::Arc;
#[cfg(not(target_has_atomic = "ptr"))]
use portable_atomic_util::Arc;
use burn_std::stub::Mutex;
use burn_tensor::{Device, Tensor};
#[cfg(feature = "std")]
mod threading {
pub(super) use std::collections::HashMap;
pub(super) use std::thread::ThreadId;
#[inline(always)]
pub(super) fn get_thread_current_id() -> ThreadId {
std::thread::current().id()
}
}
#[cfg(not(feature = "std"))]
mod threading {
pub(super) use burn_std::stub::ThreadId;
pub(super) use hashbrown::HashMap;
#[inline(always)]
pub(super) fn get_thread_current_id() -> ThreadId {
panic!("Current thread id is not available")
}
}
use threading::*;
#[derive(Clone, Debug)]
pub struct RunningState<V> {
id: ParamId,
values: Arc<Mutex<HashMap<ThreadId, V>>>,
value: Arc<Mutex<V>>,
}
impl<V> core::fmt::Display for RunningState<V> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "RunningState(id={})", self.id)
}
}
impl<V> ModuleDisplayDefault for RunningState<V> {
fn content(&self, content: Content) -> Option<Content> {
content
.add_formatted(&"RunningState".to_string())
.optional()
}
}
impl<V> ModuleDisplay for RunningState<V> {}
impl<const D: usize> Module for RunningState<Tensor<D>> {
fn visit<V: ModuleVisitor>(&self, visitor: &mut V) {
let tensor = self.value.lock().unwrap();
let param = Param::initialized(self.id, tensor.clone());
visitor.visit_float(¶m)
}
fn map<M: ModuleMapper>(self, mapper: &mut M) -> Self {
let mut tensor = self.value.lock().unwrap();
let param = Param::initialized(self.id, tensor.clone());
let param_out = mapper.map_float(param);
let (_, tensor_out, _) = param_out.consume();
*tensor = tensor_out;
core::mem::drop(tensor);
self
}
fn to_device(self, device: &Device) -> Self {
let mut tensor = self.value.lock().unwrap();
let tensor_out = tensor.clone().to_device(device);
*tensor = tensor_out;
core::mem::drop(tensor);
self
}
fn fork(self, device: &Device) -> Self {
self.to_device(device) }
fn collect_devices(&self, mut devices: Vec<Device>) -> Vec<Device> {
let device = self.value.lock().unwrap().device();
if !devices.contains(&device) {
devices.push(device)
}
devices
}
}
impl<const D: usize> RunningState<Tensor<D>> {
pub fn new(value: Tensor<D>) -> Self {
Self {
id: ParamId::new(),
values: Arc::new(Mutex::new(HashMap::new())),
value: Arc::new(Mutex::new(value)),
}
}
pub fn with_id(id: ParamId, value: Tensor<D>) -> Self {
Self {
id,
values: Arc::new(Mutex::new(HashMap::new())),
value: Arc::new(Mutex::new(value)),
}
}
pub fn from_record(record: Param<Tensor<D>>) -> Self {
let tensor = record.val();
Self {
id: record.id,
values: Arc::new(Mutex::new(HashMap::new())),
value: Arc::new(Mutex::new(tensor)),
}
}
pub fn update(&self, value: Tensor<D>) {
let thread_id = get_thread_current_id();
let mut map = self.values.lock().unwrap();
if map.contains_key(&thread_id) {
self.update_value(&mut map);
}
map.insert(thread_id, value);
}
pub fn value(&self) -> Tensor<D> {
let value = self.value.lock().unwrap();
value.clone()
}
pub fn value_sync(&self) -> Tensor<D> {
let thread_id = get_thread_current_id();
let mut map = self.values.lock().unwrap();
if map.contains_key(&thread_id) {
self.update_value(&mut map);
}
let value = self.value.lock().unwrap();
value.clone()
}
fn sync(&self) {
let mut map = self.values.lock().unwrap();
if !map.is_empty() {
self.update_value(&mut map);
}
}
fn update_value(&self, map: &mut HashMap<ThreadId, Tensor<D>>) {
let mut value_updated: Option<Tensor<D>> = None;
let mut counter = 0;
for (_key, tensor) in map.drain() {
counter += 1;
value_updated = match value_updated {
Some(current) => {
let device = current.device();
Some(tensor.to_device(&device).add(current))
}
None => Some(tensor),
};
}
if let Some(value) = value_updated {
let value = value.div_scalar(counter);
let mut value_old = self.value.lock().unwrap();
*value_old = value;
}
}
}
impl<const D: usize> AutodiffModule for RunningState<Tensor<D>> {
fn valid(&self) -> Self {
self.sync();
let value = self.value();
RunningState::with_id(self.id, value.inner())
}
fn from_inner(module: Self) -> Self {
module.sync();
let value = module.value();
RunningState::with_id(module.id, Tensor::from_inner(value))
}
}