|
10 | 10 | //! you need specific behavior, it may be best to defensively copy, paste, and maintain the
|
11 | 11 | //! specific behavior you require.
|
12 | 12 |
|
| 13 | +use std::collections::VecDeque; |
| 14 | +use timely::container::{ContainerBuilder, PushContainer, PushInto}; |
| 15 | +use crate::Data; |
13 | 16 | use crate::difference::Semigroup;
|
14 | 17 |
|
15 | 18 | /// Sorts and consolidates `vec`.
|
@@ -145,6 +148,84 @@ pub fn consolidate_updates_slice<D: Ord, T: Ord, R: Semigroup>(slice: &mut [(D,
|
145 | 148 | offset
|
146 | 149 | }
|
147 | 150 |
|
| 151 | +/// A container builder that consolidates data in-places into fixed-sized containers. Does not |
| 152 | +/// maintain FIFO ordering. |
| 153 | +#[derive(Default)] |
| 154 | +pub struct ConsolidatingContainerBuilder<C>{ |
| 155 | + current: C, |
| 156 | + empty: Vec<C>, |
| 157 | + outbound: VecDeque<C>, |
| 158 | +} |
| 159 | + |
| 160 | +impl<D,T,R> ConsolidatingContainerBuilder<Vec<(D, T, R)>> |
| 161 | +where |
| 162 | + D: Data, |
| 163 | + T: Data, |
| 164 | + R: Semigroup, |
| 165 | +{ |
| 166 | + /// Flush `self.current` up to the biggest `multiple` of elements. Pass 1 to flush all elements. |
| 167 | + // TODO: Can we replace `multiple` by a bool? |
| 168 | + fn consolidate_and_flush_through(&mut self, multiple: usize) { |
| 169 | + let preferred_capacity = <Vec<(D,T,R)>>::preferred_capacity(); |
| 170 | + consolidate_updates(&mut self.current); |
| 171 | + let mut drain = self.current.drain(..(self.current.len()/multiple)*multiple).peekable(); |
| 172 | + while drain.peek().is_some() { |
| 173 | + let mut container = self.empty.pop().unwrap_or_else(|| Vec::with_capacity(preferred_capacity)); |
| 174 | + container.extend((&mut drain).take(preferred_capacity)); |
| 175 | + self.outbound.push_back(container); |
| 176 | + } |
| 177 | + } |
| 178 | +} |
| 179 | + |
| 180 | +impl<D,T,R> ContainerBuilder for ConsolidatingContainerBuilder<Vec<(D, T, R)>> |
| 181 | +where |
| 182 | + D: Data, |
| 183 | + T: Data, |
| 184 | + R: Semigroup, |
| 185 | +{ |
| 186 | + type Container = Vec<(D,T,R)>; |
| 187 | + |
| 188 | + /// Push an element. |
| 189 | + /// |
| 190 | + /// Precondition: `current` is not allocated or has space for at least one element. |
| 191 | + #[inline] |
| 192 | + fn push<P: PushInto<Self::Container>>(&mut self, item: P) { |
| 193 | + let preferred_capacity = <Vec<(D,T,R)>>::preferred_capacity(); |
| 194 | + if self.current.capacity() < preferred_capacity * 2 { |
| 195 | + self.current.reserve(preferred_capacity * 2 - self.current.capacity()); |
| 196 | + } |
| 197 | + item.push_into(&mut self.current); |
| 198 | + if self.current.len() == self.current.capacity() { |
| 199 | + // Flush complete containers. |
| 200 | + self.consolidate_and_flush_through(preferred_capacity); |
| 201 | + } |
| 202 | + } |
| 203 | + |
| 204 | + fn push_container(&mut self, container: &mut Self::Container) { |
| 205 | + for item in container.drain(..) { |
| 206 | + self.push(item); |
| 207 | + } |
| 208 | + } |
| 209 | + |
| 210 | + fn extract(&mut self) -> Option<&mut Vec<(D,T,R)>> { |
| 211 | + if let Some(container) = self.outbound.pop_front() { |
| 212 | + self.empty.push(container); |
| 213 | + self.empty.last_mut() |
| 214 | + } else { |
| 215 | + None |
| 216 | + } |
| 217 | + } |
| 218 | + |
| 219 | + fn finish(&mut self) -> Option<&mut Vec<(D,T,R)>> { |
| 220 | + // Flush all |
| 221 | + self.consolidate_and_flush_through(1); |
| 222 | + // Remove all but two elements from the stash of empty to avoid memory leaks. We retain |
| 223 | + // two to match `current` capacity. |
| 224 | + self.empty.truncate(2); |
| 225 | + self.extract() |
| 226 | + } |
| 227 | +} |
| 228 | + |
148 | 229 | #[cfg(test)]
|
149 | 230 | mod tests {
|
150 | 231 | use super::*;
|
@@ -211,4 +292,46 @@ mod tests {
|
211 | 292 | assert_eq!(input, output);
|
212 | 293 | }
|
213 | 294 | }
|
| 295 | + |
| 296 | + #[test] |
| 297 | + fn test_consolidating_container_builder() { |
| 298 | + let mut ccb = <ConsolidatingContainerBuilder<Vec<(usize, usize, usize)>>>::default(); |
| 299 | + for _ in 0..1024 { |
| 300 | + ccb.push((0, 0, 0)); |
| 301 | + } |
| 302 | + assert_eq!(ccb.extract(), None); |
| 303 | + assert_eq!(ccb.finish(), None); |
| 304 | + |
| 305 | + for i in 0..1024 { |
| 306 | + ccb.push((i, 0, 1)); |
| 307 | + } |
| 308 | + |
| 309 | + let mut collected = Vec::default(); |
| 310 | + while let Some(container) = ccb.finish() { |
| 311 | + collected.append(container); |
| 312 | + } |
| 313 | + // The output happens to be sorted, but it's not guaranteed. |
| 314 | + collected.sort(); |
| 315 | + for i in 0..1024 { |
| 316 | + assert_eq!((i, 0, 1), collected[i]); |
| 317 | + } |
| 318 | + |
| 319 | + ccb = Default::default(); |
| 320 | + ccb.push_container(&mut Vec::default()); |
| 321 | + assert_eq!(ccb.extract(), None); |
| 322 | + assert_eq!(ccb.finish(), None); |
| 323 | + |
| 324 | + ccb.push_container(&mut Vec::from_iter((0..1024).map(|i| (i, 0, 1)))); |
| 325 | + ccb.push_container(&mut Vec::from_iter((0..1024).map(|i| (i, 0, 1)))); |
| 326 | + collected.clear(); |
| 327 | + while let Some(container) = ccb.finish() { |
| 328 | + collected.append(container); |
| 329 | + } |
| 330 | + // The output happens to be sorted, but it's not guaranteed. |
| 331 | + consolidate_updates(&mut collected); |
| 332 | + for i in 0..1024 { |
| 333 | + assert_eq!((i, 0, 2), collected[i]); |
| 334 | + } |
| 335 | + |
| 336 | + } |
214 | 337 | }
|
0 commit comments