ruda_model/data/dataloader/
batch.rs1use super::{BatchStrategy, DataLoader, DataLoaderIterator, Progress, batcher::Batcher};
2use ruda_dataset::{
3 Dataset,
4 transform::{PartialDataset, ShuffledDataset},
5};
6use ruda_tensor::api::backend::Backend;
7use rand::SeedableRng;
8use std::ops::DerefMut;
9use std::sync::Arc;
10
11pub struct BatchDataLoader<B: Backend, I, O> {
13 strategy: Box<dyn BatchStrategy<I>>,
14 dataset: Arc<dyn Dataset<I>>,
15 batcher: Arc<dyn Batcher<B, I, O>>,
16 device: B::Device,
17 rng: Option<Arc<spin::Mutex<rand::rngs::StdRng>>>,
18 batch_reading: bool,
19}
20
21impl<B: Backend, I, O> Clone for BatchDataLoader<B, I, O> {
22 fn clone(&self) -> Self {
23 Self {
24 strategy: self.strategy.clone_dyn(),
25 dataset: self.dataset.clone(),
26 batcher: self.batcher.clone(),
27 device: self.device.clone(),
28 rng: self.rng.clone(),
29 batch_reading: self.batch_reading,
30 }
31 }
32}
33
34impl<B: Backend, I, O> BatchDataLoader<B, I, O> {
35 pub fn new(
50 strategy: Box<dyn BatchStrategy<I>>,
51 dataset: Arc<dyn Dataset<I>>,
52 batcher: Arc<dyn Batcher<B, I, O>>,
53 device: B::Device,
54 rng: Option<rand::rngs::StdRng>,
55 ) -> Self {
56 Self {
57 strategy,
58 dataset,
59 batcher,
60 device,
61 rng: rng.map(|rng| Arc::new(spin::Mutex::new(rng))),
62 batch_reading: false,
63 }
64 }
65
66 pub fn batch_reading(mut self, enabled: bool) -> Self {
72 self.set_batch_reading(enabled);
73 self
74 }
75
76 pub(super) fn set_batch_reading(&mut self, enabled: bool) {
77 self.batch_reading = enabled;
78 }
79}
80
81struct BatchDataloaderIterator<B: Backend, I, O> {
83 current_index: usize,
84 strategy: Box<dyn BatchStrategy<I>>,
85 dataset: Arc<dyn Dataset<I>>,
86 batcher: Arc<dyn Batcher<B, I, O>>,
87 device: B::Device,
88 batch_reading: bool,
89 pending: std::vec::IntoIter<I>,
90 read_indices: Vec<usize>,
91 read_failed: bool,
92}
93
94impl<B, I, O> DataLoader<B, O> for BatchDataLoader<B, I, O>
95where
96 B: Backend,
97 I: Send + Sync + Clone + 'static,
98 O: Send + 'static,
99{
100 fn iter<'a>(&'a self) -> Box<dyn DataLoaderIterator<O> + 'a> {
101 let dataset = match &self.rng {
105 Some(rng) => Arc::new(ShuffledDataset::new(
106 self.dataset.clone(),
107 rng.lock().deref_mut(),
108 )),
109 None => self.dataset.clone(),
110 };
111 Box::new(BatchDataloaderIterator::new(
112 self.strategy.clone_dyn(),
113 dataset,
114 self.batcher.clone(),
115 self.device.clone(),
116 self.batch_reading,
117 ))
118 }
119
120 fn num_items(&self) -> usize {
121 self.dataset.len()
122 }
123
124 fn to_device(&self, device: &B::Device) -> Arc<dyn DataLoader<B, O>> {
125 let rng = self.rng.as_ref().map(|rng| {
126 let mut rng = rng.lock();
127 rng.fork()
128 });
129 Arc::new(
130 Self::new(
131 self.strategy.clone_dyn(),
132 self.dataset.clone(),
133 self.batcher.clone(),
134 device.clone(),
135 rng,
136 )
137 .batch_reading(self.batch_reading),
138 )
139 }
140
141 fn slice(&self, start: usize, end: usize) -> Arc<dyn DataLoader<B, O>> {
142 let rng = self.rng.as_ref().map(|rng| {
143 let mut rng = rng.lock();
144 rng.fork()
145 });
146 let dataloader = Self::new(
147 self.strategy.clone_dyn(),
148 Arc::new(PartialDataset::new(self.dataset.clone(), start, end)),
149 self.batcher.clone(),
150 self.device.clone(),
151 rng,
152 )
153 .batch_reading(self.batch_reading);
154 Arc::new(dataloader)
155 }
156}
157
158impl<B: Backend, I, O> BatchDataloaderIterator<B, I, O> {
159 pub fn new(
172 strategy: Box<dyn BatchStrategy<I>>,
173 dataset: Arc<dyn Dataset<I>>,
174 batcher: Arc<dyn Batcher<B, I, O>>,
175 device: B::Device,
176 batch_reading: bool,
177 ) -> Self {
178 BatchDataloaderIterator {
179 current_index: 0,
180 strategy,
181 dataset,
182 batcher,
183 device,
184 batch_reading,
185 pending: Vec::new().into_iter(),
186 read_indices: Vec::new(),
187 read_failed: false,
188 }
189 }
190
191 fn next_item(&mut self) -> Option<I> {
192 if !self.batch_reading {
193 return self.dataset.get(self.current_index);
194 }
195
196 assert!(
197 !self.read_failed,
198 "cannot resume a failed batch dataset read"
199 );
200 if let Some(item) = self.pending.next() {
201 return Some(item);
202 }
203
204 let len = self.dataset.len();
205 if self.current_index >= len {
206 return None;
207 }
208 let count = self.strategy.batch_size().unwrap_or(1).max(1)
209 .min(len - self.current_index);
210 self.read_indices.clear();
211 self.read_indices
212 .extend(self.current_index..self.current_index + count);
213
214 self.read_failed = true;
215 let items = self.dataset.get_many(&self.read_indices).unwrap_or_else(|| {
216 panic!(
217 "batch dataset read is missing an item in indices {:?}",
218 self.read_indices
219 )
220 });
221 assert_eq!(
222 items.len(),
223 count,
224 "batch dataset read returned the wrong number of items"
225 );
226 self.pending = items.into_iter();
227 self.read_failed = false;
228 self.pending.next()
229 }
230}
231
232impl<B: Backend, I, O> Iterator for BatchDataloaderIterator<B, I, O> {
233 type Item = O;
234
235 fn next(&mut self) -> Option<O> {
236 while let Some(item) = self.next_item() {
237 self.current_index += 1;
238 self.strategy.add(item);
239
240 if let Some(items) = self.strategy.batch(false) {
241 return Some(self.batcher.batch(items, &self.device));
242 }
243 }
244
245 if let Some(items) = self.strategy.batch(true) {
246 return Some(self.batcher.batch(items, &self.device));
247 }
248
249 None
250 }
251}
252
253impl<B: Backend, I, O> DataLoaderIterator<O> for BatchDataloaderIterator<B, I, O> {
254 fn progress(&self) -> Progress {
255 Progress::new(self.current_index, self.dataset.len())
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use std::collections::HashSet;
262
263 use super::*;
264 use crate::data::dataloader::FixBatchStrategy;
265 use crate::data::dataloader::batcher::TestBatcher;
266 use crate::data::dataset::FakeDataset;
267
268 #[test]
269 fn test_batch_dataloader() {
270 let batcher = Arc::new(TestBatcher::new());
271 let dataset = Arc::new(FakeDataset::<String>::new(27));
272 let dataloader = BatchDataLoader::new(
273 Box::new(FixBatchStrategy::new(5)),
274 dataset.clone(),
275 batcher,
276 Default::default(),
277 None,
278 );
279
280 let mut items_dataset = HashSet::new();
281 let mut items_dataloader = HashSet::new();
282
283 for item in dataset.iter() {
284 items_dataset.insert(item);
285 }
286
287 for items in dataloader.iter() {
288 for item in items {
289 items_dataloader.insert(item);
290 }
291 }
292
293 assert_eq!(items_dataset, items_dataloader);
294 }
295
296 #[test]
297 fn test_batch_dataloader_slice() {
298 let batcher = Arc::new(TestBatcher::new());
299 let dataset = Arc::new(FakeDataset::<String>::new(27));
300 let dataloader = BatchDataLoader::new(
301 Box::new(FixBatchStrategy::new(5)),
302 dataset.clone(),
303 batcher,
304 Default::default(),
305 None,
306 );
307 let dataloader_slice = dataloader.slice(5, 15);
308
309 let mut items_dataloader = HashSet::new();
310 let mut items_dataloader_slice = HashSet::new();
311
312 let mut idx = 0;
313 for items in dataloader.iter() {
314 for item in items {
315 if (5..15).contains(&idx) {
316 items_dataloader.insert(item);
317 }
318 idx += 1;
319 }
320 }
321
322 for items in dataloader_slice.iter() {
323 for item in items {
324 items_dataloader_slice.insert(item);
325 }
326 }
327
328 assert_eq!(items_dataloader, items_dataloader_slice);
329 }
330}