Skip to content

Reduce data copies during internation #3366

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 26 additions & 26 deletions webrender/src/intern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ use internal_types::FastHashMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;
use std::mem;
use std::ops;
use std::u64;
use std::{mem, ops, u64};
use util::VecHelper;

/*

Expand Down Expand Up @@ -60,7 +59,9 @@ pub struct UpdateList<S> {
/// The current epoch of the scene builder.
epoch: Epoch,
/// The additions and removals to apply.
updates: Vec<Update<S>>,
updates: Vec<Update>,
/// Actual new data to insert.
data: Vec<S>,
}

#[cfg_attr(feature = "capture", derive(Serialize))]
Expand Down Expand Up @@ -89,17 +90,17 @@ impl <T> Handle<T> where T: Copy {

#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum UpdateKind<S> {
Insert(S),
pub enum UpdateKind {
Insert,
Remove,
UpdateEpoch,
}

#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct Update<S> {
pub struct Update {
index: usize,
kind: UpdateKind<S>,
kind: UpdateKind,
}

/// The data item is stored with an epoch, for validating
Expand Down Expand Up @@ -137,18 +138,14 @@ impl<S, T, M> DataStore<S, T, M> where S: Debug, T: From<S>, M: Debug {
&mut self,
update_list: UpdateList<S>,
) {
let mut data_iter = update_list.data.into_iter();
for update in update_list.updates {
match update.kind {
UpdateKind::Insert(data) => {
let item = Item {
data: T::from(data),
UpdateKind::Insert => {
self.items.entry(update.index).set(Item {
data: T::from(data_iter.next().unwrap()),
epoch: update_list.epoch,
};
if self.items.len() == update.index {
self.items.push(item)
} else {
self.items[update.index] = item;
}
});
}
UpdateKind::Remove => {
self.items[update.index].epoch = Epoch::INVALID;
Expand All @@ -158,6 +155,7 @@ impl<S, T, M> DataStore<S, T, M> where S: Debug, T: From<S>, M: Debug {
}
}
}
debug_assert!(data_iter.next().is_none());
}
}

Expand Down Expand Up @@ -194,7 +192,9 @@ pub struct Interner<S : Eq + Hash + Clone + Debug, D, M> {
/// List of free slots in the data store for re-use.
free_list: Vec<usize>,
/// Pending list of updates that need to be applied.
updates: Vec<Update<S>>,
updates: Vec<Update>,
/// Pending new data to insert.
update_data: Vec<S>,
/// The current epoch for the interner.
current_epoch: Epoch,
/// Incrementing counter for identifying stable values.
Expand All @@ -211,6 +211,7 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
map: FastHashMap::default(),
free_list: Vec::new(),
updates: Vec::new(),
update_data: Vec::new(),
current_epoch: Epoch(1),
next_uid: 0,
local_data: Vec::new(),
Expand Down Expand Up @@ -259,8 +260,9 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
// Add a pending update to insert the new data.
self.updates.push(Update {
index,
kind: UpdateKind::Insert(data.clone()),
kind: UpdateKind::Insert,
});
self.update_data.alloc().init(data.clone());

// Generate a handle for access via the data store.
let handle = Handle {
Expand All @@ -280,15 +282,10 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De

// Create the local data for this item that is
// being interned.
let local_item = Item {
self.local_data.entry(index).set(Item {
epoch: self.current_epoch,
data: f(),
};
if self.local_data.len() == index {
self.local_data.push(local_item);
} else {
self.local_data[index] = local_item;
}
});

handle
}
Expand All @@ -298,6 +295,8 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De
/// a GC step that removes old entries.
pub fn end_frame_and_get_pending_updates(&mut self) -> UpdateList<S> {
let mut updates = mem::replace(&mut self.updates, Vec::new());
let data = mem::replace(&mut self.update_data, Vec::new());

let free_list = &mut self.free_list;
let current_epoch = self.current_epoch.0;

Expand Down Expand Up @@ -327,6 +326,7 @@ impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + De

let updates = UpdateList {
updates,
data,
epoch: self.current_epoch,
};

Expand Down
32 changes: 31 additions & 1 deletion webrender/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ const NEARLY_ZERO: f32 = 1.0 / 4096.0;

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

/// An entry into a vector, similar to `std::collections::hash_map::Entry`.
pub enum VecEntry<'a, T: 'a> {
Vacant(Allocation<'a, T>),
Occupied(&'a mut T),
}

impl<'a, T> VecEntry<'a, T> {
#[inline(always)]
pub fn set(self, value: T) {
match self {
VecEntry::Vacant(alloc) => { alloc.init(value); }
VecEntry::Occupied(slot) => { *slot = value; }
}
}
}

pub trait VecHelper<T> {
/// Growns the vector by a single entry, returning the allocation.
fn alloc(&mut self) -> Allocation<T>;
/// Either returns an existing elemenet, or grows the vector by one.
/// Doesn't expect indices to be higher than the current length.
fn entry(&mut self, index: usize) -> VecEntry<T>;
}

impl<T> VecHelper<T> for Vec<T> {
Expand All @@ -52,6 +71,17 @@ impl<T> VecHelper<T> for Vec<T> {
index,
}
}

fn entry(&mut self, index: usize) -> VecEntry<T> {
if index < self.len() {
VecEntry::Occupied(unsafe {
self.get_unchecked_mut(index)
})
} else {
assert_eq!(index, self.len());
VecEntry::Vacant(self.alloc())
}
}
}


Expand Down