cubecl_environment/bytes/
file_reads.rs1use std::{
5 path::{Path, PathBuf},
6 sync::{
7 Arc, Mutex, Weak,
8 atomic::{AtomicU64, AtomicUsize, Ordering},
9 },
10 vec::Vec,
11};
12
13static REGISTRY: CounterRegistry = CounterRegistry {
15 counters: Mutex::new(Vec::new()),
16 alive: AtomicUsize::new(0),
17};
18
19pub struct FileReadCounter {
32 counted: Arc<Counted>,
33}
34
35impl FileReadCounter {
36 pub fn new(path: impl Into<PathBuf>) -> Self {
38 let counted = Arc::new(Counted {
39 path: path.into(),
40 read: AtomicU64::new(0),
41 });
42 REGISTRY.register(&counted);
43 Self { counted }
44 }
45
46 pub fn read(&self) -> u64 {
48 self.counted.read.load(Ordering::Relaxed)
49 }
50}
51
52impl Drop for FileReadCounter {
53 fn drop(&mut self) {
54 REGISTRY.unregister();
55 }
56}
57
58pub(crate) fn count(path: &Path, size: u64) {
60 REGISTRY.count(path, size);
61}
62
63struct Counted {
65 path: PathBuf,
66 read: AtomicU64,
67}
68
69struct CounterRegistry {
77 counters: Mutex<Vec<Weak<Counted>>>,
78 alive: AtomicUsize,
79}
80
81impl CounterRegistry {
82 fn register(&self, counted: &Arc<Counted>) {
83 let mut counters = self.lock();
84 counters.retain(|counter| counter.strong_count() > 0);
85 counters.push(Arc::downgrade(counted));
86 self.alive.fetch_add(1, Ordering::Relaxed);
87 }
88
89 fn unregister(&self) {
90 self.alive.fetch_sub(1, Ordering::Relaxed);
91 }
92
93 fn count(&self, path: &Path, size: u64) {
94 if self.alive.load(Ordering::Relaxed) == 0 {
95 return;
96 }
97 self.lock()
98 .iter()
99 .filter_map(Weak::upgrade)
100 .filter(|counted| counted.path == path)
101 .for_each(|counted| {
102 counted.read.fetch_add(size, Ordering::Relaxed);
103 });
104 }
105
106 fn lock(&self) -> std::sync::MutexGuard<'_, Vec<Weak<Counted>>> {
107 self.counters
108 .lock()
109 .unwrap_or_else(|poison| poison.into_inner())
110 }
111}
112
113#[cfg(test)]
114#[cfg(not(miri))]
115mod tests {
116 use super::super::{Bytes, Reader};
117 use super::*;
118 use std::io::Write;
119
120 #[test]
124 fn reads_are_counted_when_the_file_is_accessed() {
125 let dir = tempfile::tempdir().unwrap();
126 let path = dir.path().join("weights");
127 std::fs::File::create(&path)
128 .unwrap()
129 .write_all(&[7u8; 64])
130 .unwrap();
131
132 let counter = FileReadCounter::new(&path);
133 let bytes = Bytes::from_file(&path, 64, 0);
134 let view = bytes.view(0, 16).unwrap();
135 assert_eq!(counter.read(), 0);
136
137 let mut copied = Bytes::from_bytes_vec(std::vec![0u8; 16]);
138 view.copy_into(&mut copied);
139 assert_eq!(counter.read(), 16);
140
141 bytes.read(Reader::new()).unwrap();
142 bytes.read(Reader::new()).unwrap();
143 assert_eq!(counter.read(), 16 + 64);
144
145 let other = dir.path().join("other");
146 std::fs::File::create(&other)
147 .unwrap()
148 .write_all(&[1])
149 .unwrap();
150 Bytes::from_file(&other, 1, 0).read(Reader::new()).unwrap();
151 assert_eq!(counter.read(), 16 + 64);
152 }
153}