perf: DOM read fast paths (constant nodeType, borrowed op reads, base64 table) (#259)
* DOM read fast paths: constant nodeType, fewer ops and allocations - Element.nodeType is now a constant getter instead of inheriting Node's dynamic op-backed getter. nodeType is invariant (1) for element wrappers (wrappers are only built for element nodes and node ids are never freed and reused), so this drops one JS/Rust op per read on a pervasive path. Micro-bench: ~600k nodeType reads go from ~380ms to ~5ms. - attributes getter reads each attribute once instead of twice. - dataset Proxy is cached per element instead of reallocated on every access, which also makes el.dataset === el.dataset (spec-correct). - stylesheet <link> scan borrows the node (with_node) instead of deep cloning it and compares rel via eq_ignore_ascii_case, dropping a clone and a lowercased String alloc per link per navigation. No regression: obstacle course 33/33, WPT dom/nodes Node- subset identical to baseline (3551/4163 subtests), targeted DOM probe identical. * DOM op reads: borrow the node instead of deep-cloning it The scalar read arms in op_dom (node_type, node_name, tag_name, get_attribute, attribute_names, parent/first/last/next/prev lookups, has_child_nodes, pi_target, doctype_name, doctype_public_id) and the node_root / node_ancestors / node_child_index walks called get_node, which clones the whole Node (for elements a deep clone of the attribute Vec and its Strings) just to read one field. Switch them to with_node, which borrows under the RefCell. element_children and the document_element / document_doctype child scans stay on get_node on purpose (they iterate, so the borrow story is left untouched). Output is identical. Traversal/attribute micro-bench ~25-30% faster with fewer allocations per op. No regression: obstacle course 33/33, WPT dom/nodes identical to baseline (7057/10620 across 314 files). * base64 decode: reverse-lookup table instead of indexOf scan _base64ToUint8Array decodes every fetch/XHR response body. It ran an O(64) alphabet.indexOf plus a one-char substring alloc per character. Build a charCode->6-bit reverse table once at module load and index it by charCodeAt, folding out-of-range (NaN or code >= 128) to -1 so the bitmath stays byte-identical to the old indexOf path on valid and malformed input alike. ~20-25% faster decoding a 2MB body; output byte-identical (differential test across valid, unpadded, '='-position, >127, non-alphabet and truncated inputs matches the old code exactly). No regression: obstacle course 33/33.
This commit is contained in:
1 parent
24cf7cad56
commit
5135e3cb4d
3 files changed
+71
-44
No files matched your search
@@ -929,12 +929,17 @@ impl Page {
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.filter_map(|&nid| {
|
||||
let node = dom.get_node(nid)?;
|
||||
let rel = node.get_attribute("rel")?;
|
||||
if rel.to_lowercase() != "stylesheet" {
|
||||
return None;
|
||||
}
|
||||
node.get_attribute("href").map(|s| s.to_string())
|
||||
// Borrow the node instead of deep-cloning it; rel keywords are
|
||||
// ASCII so eq_ignore_ascii_case matches to_lowercase() exactly
|
||||
// without allocating a lowercased String.
|
||||
dom.with_node(nid, |node| {
|
||||
let rel = node.get_attribute("rel")?;
|
||||
if !rel.eq_ignore_ascii_case("stylesheet") {
|
||||
return None;
|
||||
}
|
||||
node.get_attribute("href").map(|s| s.to_string())
|
||||
})
|
||||
.flatten()
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
Reference in new issue
Block a user