Commit 5a0c002
committed
perf: pre-declare position on link/text factories
Problem: this extension builds link nodes (and their nested text
children) in two paths: the enter handler for literal autolinks
during parse, and a post-parse find-and-replace transform that walks
text nodes and produces link/text nodes from URL-shaped substrings.
In both paths the literals are fresh objects with no position field;
position is patched in afterwards. V8 treats the late assignment as
a shape change, so every link and text node walks through two hidden
classes instead of one. Core nodes already avoid this; extension
nodes leave the tree with mixed layouts and tree walks lose
monomorphic dispatch.
Goal: declare position on every link and text literal this extension
produces, both in the enter handler and in the post-parse transform,
so V8 keeps a single hidden class per node type.
Changes:
- lib/index.js: trailing position: undefined on the link literal in
enterLiteralAutolink.
- lib/index.js: trailing position: undefined on the link and nested
text literals returned from findUrl, plus the trailing-text node
when splitUrl strips punctuation.
- lib/index.js: trailing position: undefined on the link and nested
text literals returned from findEmail.
Notes: the transform-path literals at lines 175 through 180, 183, and
207 through 212 are returned from find-and-replace callbacks rather
than going through this.enter(). A one-off probe (parsing
'visit www.example.com\n' with the GFM extension) confirmed that
something further down the pipeline populates position on these
nodes, so the pre-declared field gets overwritten with a real value
and the test fixtures continue to assert real position objects.
Pairs with the matching core change in
syntax-tree/mdast-util-from-markdown#53;
merged on its own this branch is a no-op. Validated with npm test
(build, format, 100% coverage, 49 of 49 tests in dev and prod).
Bench (local harness, 3-run median-of-medians, GFM tokenizer and
mdast extensions stacked, with the core branch and three sibling
extension branches checked out alongside this one):
Two scenarios exercise this code path. The first is 5000 bare URLs
interleaved with surrounding text, which is the largest mdast-layer
share in the GFM corpus on the baseline (full 319.6 ms, tokenize-only
142.8 ms; the gap between those is roughly 35 microseconds of
mdast-layer work per URL, mostly node allocation and position
bookkeeping). The second is the syntax-tree GFM-stack readme used as
a real-world fixture, where the autolink resolver still walks every
text node.
scenario baseline core alone core + paired
5000 bare URLs + text 319.6 ms +2.3% -2.5%
syntax-tree GFM-stack readme 22.8 ms +29.4% +22.5%
The bare-URL scenario is the only paired-vs-baseline comparison
across the whole 5-repo paired set that crosses zero (i.e., the
paired configuration parses faster than upstream main does). The
4.8-point swing from solo to paired matches what the V8 hidden-class
hypothesis predicts: with link and text literals sharing the core
layout, every link in the resulting tree dispatches through one
inline cache.
A drift floor measured separately ran at +4 to +12 percent across
most GFM inputs on this machine in the same window; the readme's
+22.5 percent paired residual is in the upper end of that band, which
is consistent with no real regression.1 parent ba83f42 commit 5a0c002
1 file changed
Lines changed: 16 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
83 | | - | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
84 | 90 | | |
85 | 91 | | |
86 | 92 | | |
| |||
171 | 177 | | |
172 | 178 | | |
173 | 179 | | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
174 | 183 | | |
175 | 184 | | |
176 | 185 | | |
177 | 186 | | |
178 | 187 | | |
179 | | - | |
| 188 | + | |
| 189 | + | |
180 | 190 | | |
181 | 191 | | |
182 | 192 | | |
183 | | - | |
| 193 | + | |
184 | 194 | | |
185 | 195 | | |
186 | 196 | | |
| |||
204 | 214 | | |
205 | 215 | | |
206 | 216 | | |
| 217 | + | |
207 | 218 | | |
208 | 219 | | |
209 | 220 | | |
210 | 221 | | |
211 | | - | |
| 222 | + | |
| 223 | + | |
212 | 224 | | |
213 | 225 | | |
214 | 226 | | |
| |||
0 commit comments