Performance Optimization – DOM Subsystem Internals for Crawling at Scale #127

Open
opened 2026-06-20 08:19:58 +00:00 by Artur · 0 comments
Owner

Ziel

Unser eigenes DOM muss nicht nur spec-korrekt sein, sondern auch performant genug fuer tausende Pages pro Minute. Anders als Browser koennen wir optimieren wo echte Browser Layout/Painting/Garbage-Collection brauchen.

Optimierungs-Strategien

1. String-basierte DOM Nodes (keine vollen JS Objekte fuer jeden Node)

  • Aktuell: Jeder Node ist ein vollwertiges JS-Objekt mit Prototype-Chain
  • Optimierung: Simple Nodes (Text, Comment, DocumentFragment) als strukturierte Strings/Daten
    -> Nur Element-Nodes bekommen volle Objekte
    -> Text-Nodes: { type:3, data, parent } - einfaches Objekt oder sogar nur String + Parent-Ref

2. Lazy Attribute Parsing

  • Aktuell: Alle Attribute werden beim Parsen in Map gespeichert
  • Optimierung: Raw HTML String wird gehalten, erst bei getAttribute() wird geparsed
  • Nur classList, id, dataset werden hot-cached

3. Inline-Strings statt DOM Nodes fuer kleine Text-Content

  • <div>Hallo</div> - der Text "Hallo" braucht keinen vollen Text-Node
  • Optimierung: childNodes kann gemischt sein [Node, string, Node, string]
  • Nur bei textContent-Zugriff oder DOM-Mutation wird in Node konvertiert

4. Batch-Mutation-Notifications

  • Aktuell: Jede appendChild feuert notifyChildList() fuer MutationObserver
  • Optimierung: Batch-Queue mit microtask-flush (wie Reacts batching)
    • appendChild/removeChild/insertBefore sammeln in Queue
    • Queue wird am Ende des Tasks geflusht
    • Frameworks sehen trotzdem korrekte MutationRecords (nur spaeter)

5. Selektiver Style-Engine

  • Kein echtes Layout/Box-Modell/Cascade fuer alle Elemente
  • Style-Engine wird nur aktiviert wenn getComputedStyle() aufgerufen wird
  • CSS-Regeln werden nicht automatisch evaluated, nur auf Anfrage

6. Memory Pooling

  • Wiederverwendung von DOM-Node-IDs statt ++_nodeIdCounter (vermeidet Zahl-Overflow und spart Memory)
  • WeakMap-freie Event-Target-Implementierung (array-basiert fuer kleine Event-Listener-Zahlen)

7. Zero-Copy HTML Parsing

  • Aktuell: String-basierter Tokenizer erzeugt viele Substrings
  • Optimierung: View-basiertes Parsing (Index-Offset statt Substring) - groessere HTML-Seiten gewinnen stark

Labels

performance, html-spec

Architektur

  • src/dom/node.ts: Node-Klassen optimieren (inline strings, lazy data)
  • src/dom/mutation-observer.ts: Batching-Queue
  • src/dom/parser.ts: Zero-Copy Index-basiertes Parsen
  • src/css/style-engine.ts: Lazy-Evaluation

Tests

  • Keine Aenderung an spec-korrektem Verhalten
  • Performance-Benchmarks: tests/bench/dom.bench.ts
  • Speicherverbrauch: pages mit 10k+ Nodes parsen + vergleichen
  • Speed: 1000 Pages in <1s
## Ziel Unser eigenes DOM muss nicht nur spec-korrekt sein, sondern auch performant genug fuer tausende Pages pro Minute. Anders als Browser koennen wir optimieren wo echte Browser Layout/Painting/Garbage-Collection brauchen. ## Optimierungs-Strategien ### 1. String-basierte DOM Nodes (keine vollen JS Objekte fuer jeden Node) - Aktuell: Jeder Node ist ein vollwertiges JS-Objekt mit Prototype-Chain - Optimierung: Simple Nodes (Text, Comment, DocumentFragment) als strukturierte Strings/Daten -> Nur Element-Nodes bekommen volle Objekte -> Text-Nodes: { type:3, data, parent } - einfaches Objekt oder sogar nur String + Parent-Ref ### 2. Lazy Attribute Parsing - Aktuell: Alle Attribute werden beim Parsen in Map gespeichert - Optimierung: Raw HTML String wird gehalten, erst bei getAttribute() wird geparsed - Nur classList, id, dataset werden hot-cached ### 3. Inline-Strings statt DOM Nodes fuer kleine Text-Content - `<div>Hallo</div>` - der Text "Hallo" braucht keinen vollen Text-Node - Optimierung: childNodes kann gemischt sein [Node, string, Node, string] - Nur bei textContent-Zugriff oder DOM-Mutation wird in Node konvertiert ### 4. Batch-Mutation-Notifications - Aktuell: Jede appendChild feuert notifyChildList() fuer MutationObserver - Optimierung: Batch-Queue mit microtask-flush (wie Reacts batching) - appendChild/removeChild/insertBefore sammeln in Queue - Queue wird am Ende des Tasks geflusht - Frameworks sehen trotzdem korrekte MutationRecords (nur spaeter) ### 5. Selektiver Style-Engine - Kein echtes Layout/Box-Modell/Cascade fuer alle Elemente - Style-Engine wird nur aktiviert wenn getComputedStyle() aufgerufen wird - CSS-Regeln werden nicht automatisch evaluated, nur auf Anfrage ### 6. Memory Pooling - Wiederverwendung von DOM-Node-IDs statt ++_nodeIdCounter (vermeidet Zahl-Overflow und spart Memory) - WeakMap-freie Event-Target-Implementierung (array-basiert fuer kleine Event-Listener-Zahlen) ### 7. Zero-Copy HTML Parsing - Aktuell: String-basierter Tokenizer erzeugt viele Substrings - Optimierung: View-basiertes Parsing (Index-Offset statt Substring) - groessere HTML-Seiten gewinnen stark ## Labels performance, html-spec ## Architektur - src/dom/node.ts: Node-Klassen optimieren (inline strings, lazy data) - src/dom/mutation-observer.ts: Batching-Queue - src/dom/parser.ts: Zero-Copy Index-basiertes Parsen - src/css/style-engine.ts: Lazy-Evaluation ## Tests - Keine Aenderung an spec-korrektem Verhalten - Performance-Benchmarks: tests/bench/dom.bench.ts - Speicherverbrauch: pages mit 10k+ Nodes parsen + vergleichen - Speed: 1000 Pages in <1s
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
glow-all/true-headless-browser#127
No description provided.