Use FnvHashmap for LiveDOMReferences (#37673)

These maps are keyed on pointers, so using FnvHashMap should be faster
and we don't need the collision resistance properties of the default
hasher.

These maps showed up as hot when profiling the testcase from
https://github.com/servo/servo/issues/37223#issuecomment-3000705438.
Switching the hashing algorithm reduces the time spent hashing, but
overall that is still only a fractional part of the testcase.

Testing: Functionality unchanged. Overall the performance change is to
small to show up in any of our automated performance tests. When
comparing the perf profile of the above linked testcase, one should see
a reduction of time spent hashing (inverse call stack)

Signed-off-by: Jonathan Schwender <schwenderjonathan@gmail.com>
This commit is contained in:
Jonathan Schwender 2025-06-25 08:47:20 +02:00 committed by GitHub
parent ef5784da0d
commit 922d4b83de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 9 additions and 8 deletions

View file

@ -24,12 +24,12 @@
use std::cell::RefCell;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::hash_map::HashMap;
use std::hash::Hash;
use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::{Arc, Weak};
use fnv::FnvHashMap;
use js::jsapi::JSTracer;
use script_bindings::script_runtime::CanGc;
@ -230,8 +230,8 @@ impl<T: DomObject> Clone for Trusted<T> {
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
pub(crate) struct LiveDOMReferences {
// keyed on pointer to Rust DOM object
reflectable_table: RefCell<HashMap<*const libc::c_void, Weak<TrustedReference>>>,
promise_table: RefCell<HashMap<*const Promise, Vec<Rc<Promise>>>>,
reflectable_table: RefCell<FnvHashMap<*const libc::c_void, Weak<TrustedReference>>>,
promise_table: RefCell<FnvHashMap<*const Promise, Vec<Rc<Promise>>>>,
}
impl LiveDOMReferences {
@ -239,8 +239,8 @@ impl LiveDOMReferences {
pub(crate) fn initialize() {
LIVE_REFERENCES.with(|r| {
*r.borrow_mut() = Some(LiveDOMReferences {
reflectable_table: RefCell::new(HashMap::new()),
promise_table: RefCell::new(HashMap::new()),
reflectable_table: RefCell::new(FnvHashMap::default()),
promise_table: RefCell::new(FnvHashMap::default()),
})
});
}
@ -289,7 +289,7 @@ impl LiveDOMReferences {
}
/// Remove null entries from the live references table
fn remove_nulls<K: Eq + Hash + Clone, V>(table: &mut HashMap<K, Weak<V>>) {
fn remove_nulls<K: Eq + Hash + Clone, V>(table: &mut FnvHashMap<K, Weak<V>>) {
let to_remove: Vec<K> = table
.iter()
.filter(|&(_, value)| Weak::upgrade(value).is_none())

View file

@ -35,6 +35,7 @@ use embedder_traits::{
};
use encoding_rs::{Encoding, UTF_8};
use euclid::default::{Point2D, Rect, Size2D};
use fnv::FnvHashMap;
use html5ever::{LocalName, Namespace, QualName, local_name, ns};
use hyper_serde::Serde;
use ipc_channel::ipc;
@ -380,7 +381,7 @@ pub(crate) struct Document {
appropriate_template_contents_owner_document: MutNullableDom<Document>,
/// Information on elements needing restyle to ship over to layout when the
/// time comes.
pending_restyles: DomRefCell<HashMap<Dom<Element>, NoTrace<PendingRestyle>>>,
pending_restyles: DomRefCell<FnvHashMap<Dom<Element>, NoTrace<PendingRestyle>>>,
/// This flag will be true if the `Document` needs to be painted again
/// during the next full layout attempt due to some external change such as
/// the web view changing size, or because the previous layout was only for
@ -4162,7 +4163,7 @@ impl Document {
current_parser: Default::default(),
base_element: Default::default(),
appropriate_template_contents_owner_document: Default::default(),
pending_restyles: DomRefCell::new(HashMap::new()),
pending_restyles: DomRefCell::new(FnvHashMap::default()),
needs_paint: Cell::new(false),
active_touch_points: DomRefCell::new(Vec::new()),
dom_interactive: Cell::new(Default::default()),