Skip to content

Commit 2d448c3

Browse files
committed
Use atomics to allow for concurrent updates.
1 parent c3a01bb commit 2d448c3

File tree

3 files changed

+64
-7
lines changed

3 files changed

+64
-7
lines changed

src/resource.rs

+23-4
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ use std::any::{Any, TypeId};
22

33
use bevy::utils::HashMap;
44

5-
use crate::MemoryStats;
5+
use crate::stats::{MemoryStats, MemoryStatsInternal};
66

77
/// Stores memory usage statistics for registered data types.
88
#[derive(Debug, Default)]
99
pub struct MemoryUsage {
10-
pub(crate) datasizes: HashMap<TypeId, MemoryStats>,
10+
pub(crate) datasizes: HashMap<TypeId, MemoryStatsInternal>,
1111
}
1212

1313
impl MemoryUsage {
@@ -20,7 +20,7 @@ impl MemoryUsage {
2020
{
2121
let type_id = TypeId::of::<T>();
2222

23-
self.datasizes.get(&type_id).copied()
23+
self.datasizes.get(&type_id).map(MemoryStatsInternal::get)
2424
}
2525

2626
/// Updates the [`MemoryStats`] for the given type.
@@ -35,7 +35,26 @@ impl MemoryUsage {
3535
.get_mut(&type_id)
3636
.expect("Memory usage not tracked for this type. Did you forget to register the type?");
3737

38-
*entry = stats;
38+
*entry = MemoryStatsInternal::from(stats);
39+
}
40+
41+
/// Like [`update_stats`][Self::update_stats] but operates on a shared reference.
42+
///
43+
/// In exchange for the slight possibility that individual fields in
44+
/// [`MemoryStats`] will be inconsistent with each other, this allows making
45+
/// multiple updates to the resource concurrently.
46+
pub fn update_stats_fast<T>(&self, stats: MemoryStats)
47+
where
48+
T: Any,
49+
{
50+
let type_id = TypeId::of::<T>();
51+
52+
let entry = self
53+
.datasizes
54+
.get(&type_id)
55+
.expect("Memory usage not tracked for this type. Did you forget to register the type?");
56+
57+
entry.set(stats);
3958
}
4059

4160
/// Registers the given type with the usage tracker.

src/stats.rs

+39-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::any::Any;
1+
use std::{
2+
any::Any,
3+
sync::atomic::{AtomicUsize, Ordering},
4+
};
25

36
use crate::DataSize;
47

@@ -107,3 +110,38 @@ impl MemoryStats {
107110
Self::stack_size_of(value) + Self::heap_size_of(value)
108111
}
109112
}
113+
114+
#[derive(Debug, Default)]
115+
pub(crate) struct MemoryStatsInternal {
116+
count: AtomicUsize,
117+
total_stack_bytes: AtomicUsize,
118+
total_heap_bytes: AtomicUsize,
119+
}
120+
121+
impl MemoryStatsInternal {
122+
pub(crate) fn get(&self) -> MemoryStats {
123+
MemoryStats {
124+
count: self.count.load(Ordering::Relaxed),
125+
total_stack_bytes: self.total_stack_bytes.load(Ordering::Relaxed),
126+
total_heap_bytes: self.total_heap_bytes.load(Ordering::Relaxed),
127+
}
128+
}
129+
130+
pub(crate) fn set(&self, stats: MemoryStats) {
131+
self.count.store(stats.count, Ordering::Relaxed);
132+
self.total_stack_bytes
133+
.store(stats.total_stack_bytes, Ordering::Relaxed);
134+
self.total_heap_bytes
135+
.store(stats.total_heap_bytes, Ordering::Relaxed);
136+
}
137+
}
138+
139+
impl From<MemoryStats> for MemoryStatsInternal {
140+
fn from(stats: MemoryStats) -> Self {
141+
Self {
142+
count: AtomicUsize::new(stats.count),
143+
total_stack_bytes: AtomicUsize::new(stats.total_stack_bytes),
144+
total_heap_bytes: AtomicUsize::new(stats.total_heap_bytes),
145+
}
146+
}
147+
}

src/systems.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::{MemoryConfig, MemoryStats, MemoryUsage};
1212
pub fn update_stats_for_component<T>(
1313
query: Query<&T>,
1414
memory_config: Res<MemoryConfig>,
15-
mut memory_usage: ResMut<MemoryUsage>,
15+
memory_usage: Res<MemoryUsage>,
1616
) where
1717
T: Any + Component + DataSize,
1818
{
@@ -22,5 +22,5 @@ pub fn update_stats_for_component<T>(
2222

2323
let stats = MemoryStats::from_values(query.iter());
2424

25-
memory_usage.update_stats::<T>(stats);
25+
memory_usage.update_stats_fast::<T>(stats);
2626
}

0 commit comments

Comments
 (0)