Skip to content

Commit 04b1c60

Browse files
committed
Introduce entry-like vector API in order to reduce interned data copies even more
1 parent 06fe8b5 commit 04b1c60

File tree

2 files changed

+37
-17
lines changed

2 files changed

+37
-17
lines changed

webrender/src/intern.rs

+6-16
Original file line numberDiff line numberDiff line change
@@ -142,16 +142,10 @@ impl<S, T, M> DataStore<S, T, M> where S: Debug, T: From<S>, M: Debug {
142142
for update in update_list.updates {
143143
match update.kind {
144144
UpdateKind::Insert => {
145-
let data = data_iter.next().unwrap();
146-
let item = Item {
147-
data: T::from(data),
145+
self.items.entry(update.index).set(Item {
146+
data: T::from(data_iter.next().unwrap()),
148147
epoch: update_list.epoch,
149-
};
150-
if self.items.len() == update.index {
151-
self.items.push(item)
152-
} else {
153-
self.items[update.index] = item;
154-
}
148+
});
155149
}
156150
UpdateKind::Remove => {
157151
self.items[update.index].epoch = Epoch::INVALID;
@@ -161,6 +155,7 @@ impl<S, T, M> DataStore<S, T, M> where S: Debug, T: From<S>, M: Debug {
161155
}
162156
}
163157
}
158+
debug_assert!(data_iter.next().is_none());
164159
}
165160
}
166161

@@ -283,15 +278,10 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
283278

284279
// Create the local data for this item that is
285280
// being interned.
286-
let local_item = Item {
281+
self.local_data.entry(index).set(Item {
287282
epoch: self.current_epoch,
288283
data: f(),
289-
};
290-
if self.local_data.len() == index {
291-
self.local_data.push(local_item);
292-
} else {
293-
self.local_data[index] = local_item;
294-
}
284+
});
295285

296286
handle
297287
}

webrender/src/util.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ const NEARLY_ZERO: f32 = 1.0 / 4096.0;
1717

1818
/// A typesafe helper that separates new value construction from
1919
/// vector growing, allowing LLVM to ideally construct the element in place.
20-
#[must_use]
2120
pub struct Allocation<'a, T: 'a> {
2221
vec: &'a mut Vec<T>,
2322
index: usize,
@@ -37,8 +36,28 @@ impl<'a, T> Allocation<'a, T> {
3736
}
3837
}
3938

39+
/// An entry into a vector, similar to `std::collections::hash_map::Entry`.
40+
pub enum VecEntry<'a, T: 'a> {
41+
Vacant(Allocation<'a, T>),
42+
Occupied(&'a mut T),
43+
}
44+
45+
impl<'a, T> VecEntry<'a, T> {
46+
#[inline(always)]
47+
pub fn set(self, value: T) {
48+
match self {
49+
VecEntry::Vacant(alloc) => { alloc.init(value); }
50+
VecEntry::Occupied(slot) => { *slot = value; }
51+
}
52+
}
53+
}
54+
4055
pub trait VecHelper<T> {
56+
/// Growns the vector by a single entry, returning the allocation.
4157
fn alloc(&mut self) -> Allocation<T>;
58+
/// Either returns an existing elemenet, or grows the vector by one.
59+
/// Doesn't expect indices to be higher than the current length.
60+
fn entry(&mut self, index: usize) -> VecEntry<T>;
4261
}
4362

4463
impl<T> VecHelper<T> for Vec<T> {
@@ -52,6 +71,17 @@ impl<T> VecHelper<T> for Vec<T> {
5271
index,
5372
}
5473
}
74+
75+
fn entry(&mut self, index: usize) -> VecEntry<T> {
76+
if index < self.len() {
77+
VecEntry::Occupied(unsafe {
78+
self.get_unchecked_mut(index)
79+
})
80+
} else {
81+
assert_eq!(index, self.len());
82+
VecEntry::Vacant(self.alloc())
83+
}
84+
}
5585
}
5686

5787

0 commit comments

Comments
 (0)