English
Backends and performance
Choose a backend using measurements of your dictionary, input and output volume.
Performance in charts
Measured snapshots, not a live benchmark or a claim about the latest npm release. Lower time and memory are better. Expand the tables for values and variation.
Scanning before and after optimization
All 18 scenarios share the same baseline. The dashed line is the original time (1×); a bar at 0.4× takes 40% of that time. Switch operations to see where the gains apply.
After / before · lower is faster · baseline = 1×
Exact measurements and variation
| Scenario | Before · ms | After · ms | After / before | Time change |
|---|---|---|---|---|
| Tiny ASCII | 0.001269675 (0.000014722; 0.000023004) | 0.000136628 (0.00000104; 0.00000094) | 0.107609 | -89.24% |
| CRLF | 2.572690125 (0.057604125; 0.026924375) | 1.281498688 (0.02346875; 0.014897125) | 0.498116 | -50.19% |
| ASCII + combining | 2.80034375 (0.043317625; 0.037565125) | 2.659710937 (0.04278125; 0.04969275) | 0.94978 | -5.02% |
| Unicode at start | 2.463591125 (0.030835938; 0.006440125) | 2.329559937 (0.01759375; 0.027726625) | 0.945595 | -5.44% |
| ASCII prefix | 2.87225525 (0.0160625; 0.068546938) | 0.856292328 (0.007295562; 0.035654953) | 0.298125 | -70.19% |
| Early Unicode | 4.819791625 (0.036427125; 0.114406125) | 1.504946594 (0.014993531; 0.094178344) | 0.312243 | -68.78% |
| Ordinary text | 2.021273437 (0.0326745; 0.0097005) | 0.609461578 (0.006155594; 0.002634109) | 0.301524 | -69.85% |
| No matches | 2.472632813 (0.011533937; 0.012984375) | 0.718931641 (0.013856766; 0.023575516) | 0.290756 | -70.92% |
| Mixed Unicode | 0.865691406 (0.013355469; 0.001882812) | 0.862647125 (0.009561219; 0.012480469) | 0.996483 | -0.35% |
| Shared prefixes | 1.442992187 (0.014175781; 0.060458313) | 0.425919594 (0.002939461; 0.012159187) | 0.295164 | -70.48% |
| Dense suffixes | 2.35503125 (0.020028625; 0.039721313) | 2.357835937 (0.02818225; 0.098164062) | 1.001191 | +0.12% |
| High-output, no hit | 4.63714575 (0.017401; 0.130124875) | 4.655984375 (0.0189115; 0.163510375) | 1.004063 | +0.41% |
| High-output, sparse | 4.782875 (0.043520875; 0.03275) | 4.759760375 (0.030213625; 0.098999875) | 0.995167 | -0.48% |
| Duplicate patterns | 2.288026062 (0.009601563; 0.022078063) | 1.725445312 (0.031473938; 0.0095) | 0.75412 | -24.59% |
| Early hit | 7.3808335 (0.04828125; 0.234719) | 2.25433075 (0.05160675; 0.032132812) | 0.30543 | -69.46% |
| Late hit | 7.4371145 (0.02235425; 0.1510415) | 2.260109375 (0.037114625; 0.019760375) | 0.303896 | -69.61% |
| 10k patterns | 1.967362 (0.0115755; 0.017362) | 0.601537109 (0.002729172; 0.005005203) | 0.305758 | -69.42% |
| Long text | 20.820208 (0.292167; 0.155749) | 20.587333 (0.39925; 0.385) | 0.988815 | -1.12% |
Timing cells show median ms (within-round MAD; between-round MAD). MAD measures variation, not a confidence interval. — means that statistic was not recorded.
benchmarks-optimization.json · 2026-09-23 · 5 independent rounds · 0d10f03
Environment and full provenance
- node
- v24.18.0
- platform
- darwin
- arch
- arm64
- cpu
- Apple M4 Max
- icu
- 78.3
- unicode
- 17.0
- Measured at
- 2026-09-23T07:35:25.510Z
- Source revision
- 0d10f03bade9609faf6bbe086b0dd4666452c4e0
- Working tree changes
- true
- Baseline
- b01c9f2
- Implementation SHA-256
- 3b9119908bfda141b7ebba8f291ebba40f285cb04bbde0caadb87781cade0e36
- Baseline SHA-256
- c0e85759b5226525ba51f7dc1f95dc427340118dca47fc93c72d613e6e46cdbf
- Corpus SHA-256
- 4afa3eee0768ace1840771227bd94753d446ab82a09bbaf11d2acf1834462c5e
- Report SHA-256
- 00e2cd3c6630174fa4be71ff03169a4317e0650f9b8d5856cb6fc81c07b8239f
Later targeted rechecks and remaining tradeoffs
The scan chart keeps its original five-round snapshot. Later measurements below use their own implementation fingerprints and are not spliced into it. Repeated warm construction of the ordinary small dictionary changed by 7.3% (positive means slower). High-output dictionaries use native full enumeration even on no-hit text, so ASCII gains are not universal.
Exact measurements and variation
| Scenario | Operation | Before · ms | After · ms | Time change | Rounds / source |
|---|---|---|---|---|---|
| Early Unicode | load | 0.007536184 (0.000202433; 0.000046631) | 0.007769521 (0.000126221; 0.000037882) | +3.1% | 7 · scanRecheck |
| Early Unicode | build | 0.006296193 (0.000046499; 0.000082372) | 0.0060964 (0.000044271; 0.00004863) | -3.17% | 7 · scanRecheck |
| Early Unicode | match | 0.000103466 (0.000000839; 0.000003649) | 0.000100652 (0.000000945; 0.000003679) | -2.72% | 7 · scanRecheck |
| Mixed Unicode | load | 0.161373859 (0.002421387; 0.001249348) | 0.160725586 (0.001869141; 0.002076008) | -0.4% | 7 · scanRecheck |
| Mixed Unicode | build | 0.162988445 (0.001462242; 0.001569176) | 0.154940594 (0.001020508; 0.000681969) | -4.94% | 7 · scanRecheck |
| Mixed Unicode | match | 0.003720205 (0.000103099; 0.000160105) | 0.00419518 (0.000515127; 0.000273702) | +12.77% | 7 · scanRecheck |
| Shared prefixes | load | 1.019778656 (0.038187469; 0.019308594) | 1.076451844 (0.037391937; 0.027854188) | +5.56% | 7 · scanRecheck |
| Shared prefixes | build | 1.314447938 (0.019407563; 0.023204406) | 0.849103516 (0.008429687; 0.030935547) | -35.4% | 7 · scanRecheck |
| Shared prefixes | match | 0.000300666 (0.000001889; 0.000009721) | 0.000310079 (0.000001184; 0.000011546) | +3.13% | 7 · scanRecheck |
| Duplicate patterns | load | 0.056904215 (0.001910727; 0.000343344) | 0.057346436 (0.002123615; 0.001427814) | +0.78% | 7 · scanRecheck |
| Duplicate patterns | build | 0.036539266 (0.000353801; 0.000282308) | 0.045132162 (0.000180543; 0.001015463) | +23.52% | 7 · scanRecheck |
| Duplicate patterns | match | 0.00009635 (0.000000731; 0.000001887) | 0.000096013 (0.000001251; 0.000003813) | -0.35% | 7 · scanRecheck |
| Duplicate patterns | load | 0.0510448 (0.000971191; 0.000046184) | 0.051154216 (0.000773642; 0.000258504) | +0.21% | 7 · builderRecheck |
| Duplicate patterns | build | 0.035663778 (0.000324342; 0.000484985) | 0.017146688 (0.000130717; 0.000104512) | -51.92% | 7 · builderRecheck |
| Duplicate patterns | match | 0.000097522 (0.000000668; 0.000002916) | 0.000099024 (0.000000907; 0.000001781) | +1.54% | 7 · builderRecheck |
| Ordinary text | load | 0.074622314 (0.002341635; 0.001869955) | 0.078218018 (0.003306396; 0.00222526) | +4.82% | 7 · builderRecheck |
| Ordinary text | build | 0.075951334 (0.001940672; 0.002001219) | 0.081459799 (0.001128416; 0.000841391) | +7.25% | 7 · builderRecheck |
| Ordinary text | match | 0.000145013 (0.000002099; 0.000007323) | 0.000146319 (0.0000032; 0.000005473) | +0.9% | 7 · builderRecheck |
| Shared prefixes | load | 1.012390625 (0.029386719; 0.030472656) | 1.033446625 (0.023977906; 0.045339844) | +2.08% | 7 · builderRecheck |
| Shared prefixes | build | 1.26889975 (0.042160187; 0.031625) | 0.841124344 (0.031281234; 0.051948562) | -33.71% | 7 · builderRecheck |
| Shared prefixes | match | 0.000313238 (0.000002728; 0.000009483) | 0.000354697 (0.000012394; 0.000044048) | +13.24% | 7 · builderRecheck |
| Mixed Unicode | load | 0.161232094 (0.002578449; 0.001061688) | 0.158745281 (0.003340008; 0.001036133) | -1.54% | 7 · builderRecheck |
| Mixed Unicode | build | 0.164593262 (0.00159798; 0.001666504) | 0.15644059 (0.002091469; 0.00152018) | -4.95% | 7 · builderRecheck |
| Mixed Unicode | match | 0.004181345 (0.000354461; 0.000251313) | 0.003797536 (0.000316915; 0.000310079) | -9.18% | 7 · builderRecheck |
| Shared prefixes | match | 0.000295103 (0.000003017; 0.000013225) | 0.000304079 (0.000003312; 0.000004858) | +3.04% | 7 · presenceRecheck |
benchmarks-optimization-recheck.json · 2026-09-23 · 7 independent rounds · 0d10f03
Environment and full provenance
- node
- v24.18.0
- platform
- darwin
- arch
- arm64
- cpu
- Apple M4 Max
- icu
- 78.3
- unicode
- 17.0
- Measured at
- 2026-09-23T07:50:21.300Z
- Source revision
- 0d10f03bade9609faf6bbe086b0dd4666452c4e0
- Working tree changes
- true
- Baseline
- b01c9f2
- Implementation SHA-256
- 3b9119908bfda141b7ebba8f291ebba40f285cb04bbde0caadb87781cade0e36
- Baseline SHA-256
- b6d995a840e4962a368f3f098a3e2c013a05f294ec8860efcd549ce9a5ff16a3
- Corpus SHA-256
- 4afa3eee0768ace1840771227bd94753d446ab82a09bbaf11d2acf1834462c5e
- Report SHA-256
- ba12ed87faa1dc14127bf7be75530b5d1477969c874c8f99e461320b2df08680
benchmarks-final-builder-recheck.json · 2026-09-23 · 7 independent rounds · dd97451
Environment and full provenance
- node
- v24.18.0
- platform
- darwin
- arch
- arm64
- cpu
- Apple M4 Max
- icu
- 78.3
- unicode
- 17.0
- Measured at
- 2026-09-23T08:16:31.795Z
- Source revision
- dd97451efdce9586bbe7d3f442a410ed77d07853
- Working tree changes
- true
- Baseline
- b01c9f2
- Implementation SHA-256
- c7af20de2e8a6a0064b3755ef656d653b0e6d469429ac614a7f0c03d5bc32a6c
- Baseline SHA-256
- 59e4199637e07de27cf118aee97e5cc736356eaf5813d2017811c39aa4e937c4
- Corpus SHA-256
- 4afa3eee0768ace1840771227bd94753d446ab82a09bbaf11d2acf1834462c5e
- Report SHA-256
- 4c02cf008470fc6b27a6d48f339ee593b22e476f3f268ef9406241caf7bbdcfe
benchmarks-presence-final-recheck.json · 2026-09-23 · 7 independent rounds · dd97451
Environment and full provenance
- node
- v24.18.0
- platform
- darwin
- arch
- arm64
- cpu
- Apple M4 Max
- icu
- 78.3
- unicode
- 17.0
- Measured at
- 2026-09-23T08:20:14.774Z
- Source revision
- dd97451efdce9586bbe7d3f442a410ed77d07853
- Working tree changes
- true
- Baseline
- b01c9f2
- Implementation SHA-256
- c7af20de2e8a6a0064b3755ef656d653b0e6d469429ac614a7f0c03d5bc32a6c
- Baseline SHA-256
- 3fc0b1a2891ab6fbc993d76904205615631e871dc262d41d3dac990d13b34725
- Runner SHA-256
- f45aeb0a9c3eda6c4b9fd27056167d3888c22f8a8c66f616ab829ea901243c55
- Corpus SHA-256
- 4afa3eee0768ace1840771227bd94753d446ab82a09bbaf11d2acf1834462c5e
- Report SHA-256
- 842c493eae1c6965b7456f24f639aea7fa86272975a13dcdd461c496c3a62a38
v1, v2 and optimized v3
With independent UTF-16 ranges as the shared output, optimized v3 is faster than v2 in 14 of 14 scenarios. v1 still wins on some ASCII inputs. Results that fail the correctness check have no bars or speed ratios.
Independent ranges · lower is faster · v2 = 1×. v1 is not comparable for emoji, ZWJ, combining marks and CRLF.
Independent range timings · median ms (MAD; between-round MAD)
| Scenario | v1 · ms | v2 · ms | Optimized v3 · ms | Rounds / source |
|---|---|---|---|---|
| Ordinary text | 0.671835937 (0.007359375; 0.019706375) | 3.49615625 (0.027041625; 0.029947875) | 0.598543625 (0.005460937; 0.003548172) | 5 · original |
| No matches | 0.421351562 (0.004225906; 0.008166016) | 4.42000525 (0.039885375; 0.0384895) | 0.598173172 (0.005952469; 0.024811844) | 7 · recheck |
| Shared prefixes | 0.475947906 (0.006143219; 0.009919281) | 2.524388 (0.020815125; 0.046349) | 0.393973313 (0.003625977; 0.007260742) | 5 · original |
| Dense suffixes | 3.114309938 (0.021072938; 0.015197875) | 3.259916688 (0.0251745; 0.051653688) | 2.305966125 (0.025445312; 0.0263125) | 5 · original |
| Duplicate patterns | 1.871252625 (0.017622375; 0.054755188) | 3.55315625 (0.02338525; 0.03960425) | 1.631329437 (0.016760406; 0.011783875) | 5 · original |
| Early hit | 1.580188812 (0.033903625; 0.009697906) | 13.1050625 (0.1155415; 0.08775) | 2.129585938 (0.015182313; 0.073263) | 7 · recheck |
| Late hit | 1.568641938 (0.013188812; 0.041936187) | 12.9421045 (0.0714375; 0.1565215) | 2.169085938 (0.027036438; 0.052179688) | 7 · recheck |
| 10k patterns | 0.819513016 (0.022947266; 0.032115234) | 3.840359375 (0.053541625; 0.099510375) | 0.598176438 (0.006532563; 0.005749344) | 5 · original |
| Long text | 12.49685425 (0.09892725; 0.2773645) | 60.378291 (1.155417; 0.773166) | 9.545948 (0.07855225; 0.21780225) | 5 · original |
| Chinese | 2.33740625 (0.043601625; 0.076854188) | 4.956802 (0.06201575; 0.082036375) | 2.350195313 (0.0244115; 0.056028625) | 5 · original |
| Emoji | Not comparable | 1.875125 (0.031598937; 0.0247005) | 0.809717438 (0.003578125; 0.009024719) | 5 · original |
| ZWJ sequences | Not comparable | 0.700683594 (0.004184891; 0.005352203) | 0.331774742 (0.002745773; 0.001072594) | 5 · original |
| Combining marks | Not comparable | 2.134664063 (0.026484375; 0.01678125) | 1.042622375 (0.008112; 0.008574188) | 5 · original |
| CRLF | Not comparable | 2.288255187 (0.019752563; 0.003966125) | 0.570280609 (0.004173156; 0.010173844) | 5 · original |
Native v1/v2 search returns grouped output; v3 returns independent match objects. The native timings below measure different output work and should not be read as equivalent throughput.
Native output timings · median ms (MAD; between-round MAD)
| Scenario | v1 · ms | v2 · ms | Optimized v3 · ms | Rounds / source |
|---|---|---|---|---|
| Ordinary text | 0.479438141 (0.007430984; 0.004863266) | 1.701596375 (0.018164062; 0.020921937) | 0.610501953 (0.004858719; 0.014290375) | 7 · recheck |
| No matches | 0.423757812 (0.005669266; 0.006125) | 2.1667995 (0.017763; 0.02735675) | 0.598205719 (0.007033203; 0.028513016) | 7 · recheck |
| Shared prefixes | 0.436317703 (0.004397125; 0.000654312) | 1.302658875 (0.008703156; 0.014859406) | 0.392411781 (0.002458328; 0.011767258) | 5 · original |
| Dense suffixes | 0.007156077 (0.000054026; 0.000077382) | 0.051514242 (0.000309977; 0.000531006) | 2.313322875 (0.032333313; 0.012132875) | 7 · recheck |
| Duplicate patterns | 0.327894531 (0.00320475; 0.001456055) | 0.943231781 (0.005602844; 0.011592437) | 1.623438813 (0.014154938; 0.016690125) | 7 · recheck |
| Early hit | 1.584460937 (0.019053406; 0.014001313) | 6.58317725 (0.04205225; 0.0798855) | 2.144432313 (0.017877625; 0.087416625) | 7 · recheck |
| Late hit | 1.571467438 (0.013057313; 0.020328125) | 6.478395875 (0.0583645; 0.109989625) | 2.182192687 (0.015388; 0.026875) | 7 · recheck |
| 10k patterns | 0.65314975 (0.004081359; 0.016722016) | 1.886552125 (0.0406745; 0.010877562) | 0.599035156 (0.005404297; 0.011255859) | 5 · original |
| Long text | 8.896427 (0.0968125; 0.1152395) | 29.96625 (0.276334; 0.093042) | 9.651927 (0.06138525; 0.2726145) | 7 · recheck |
| Chinese | 1.906877563 (0.02800525; 0.02590625) | 2.318304687 (0.052122375; 0.033828125) | 2.413601563 (0.051817688; 0.018005188) | 7 · recheck |
| Emoji | Not comparable | 1.018970031 (0.008847656; 0.015799438) | 0.819953125 (0.005609375; 0.018988281) | 5 · original |
| ZWJ sequences | Not comparable | 0.383055344 (0.00359375; 0.000662766) | 0.331748703 (0.002734055; 0.001452789) | 5 · original |
| Combining marks | Not comparable | 1.077026031 (0.004104156; 0.013281219) | 1.052846375 (0.010964875; 0.005266937) | 5 · original |
| CRLF | Not comparable | 1.088936187 (0.006821625; 0.011173188) | 0.584257156 (0.007115234; 0.009666672) | 5 · original |
A seven-round recheck replaces a complete scenario/operation group across all three versions only after source, environment, runner and corpus validation. Remaining groups keep five rounds. Memory measurements stay with the original report.
benchmarks-versions-optimization.json · 2026-09-23 · 5 independent rounds · dd97451
Environment and full provenance
- node
- v24.18.0
- pnpm
- pnpm@12.5.1
- platform
- darwin
- arch
- arm64
- cpu
- Apple M4 Max
- icu
- 78.3
- unicode
- 17.0
- Measured at
- 2026-09-23T08:22:10.112Z
- Source revision
- dd97451efdce9586bbe7d3f442a410ed77d07853
- Working tree changes
- true
- Implementation SHA-256
- c7af20de2e8a6a0064b3755ef656d653b0e6d469429ac614a7f0c03d5bc32a6c
- Runner SHA-256
- e557a2c3f4c207fb5a8f2dbf24a9da15572830e94a005716559218cb6323f7ea
- Corpus SHA-256
- 8dbac3966d9f23c4047c59576d55779948d57a39c7603acdf7b748506f701d4d
- Report SHA-256
- f9df675eeca635f032eb77e18f107bd6ba7d8031194ee4a4fcf7dcb2d6dcd9f3
- v1 · 1.1.0
- 92e364bded18733ea20c25d2ba2773677970600be6d65da05fa325267a19d2c1
- v2 · 2.0.4
- 8de77ed31903160adb53c77cf4aff80032144f685710c719c5490e592c9d7397
- current · 3.2.0
- c7af20de2e8a6a0064b3755ef656d653b0e6d469429ac614a7f0c03d5bc32a6c
benchmarks-versions-optimization-recheck.json · 2026-09-23 · 7 independent rounds · dd97451
Environment and full provenance
- node
- v24.18.0
- pnpm
- pnpm@12.5.1
- platform
- darwin
- arch
- arm64
- cpu
- Apple M4 Max
- icu
- 78.3
- unicode
- 17.0
- Measured at
- 2026-09-23T08:31:59.230Z
- Source revision
- dd97451efdce9586bbe7d3f442a410ed77d07853
- Working tree changes
- true
- Implementation SHA-256
- c7af20de2e8a6a0064b3755ef656d653b0e6d469429ac614a7f0c03d5bc32a6c
- Runner SHA-256
- e557a2c3f4c207fb5a8f2dbf24a9da15572830e94a005716559218cb6323f7ea
- Corpus SHA-256
- 8dbac3966d9f23c4047c59576d55779948d57a39c7603acdf7b748506f701d4d
- Report SHA-256
- ef2bb327ec120146210728cadba8d622c0498530b18a4072086db5daace5f1c2
- v1 · 1.1.0
- 92e364bded18733ea20c25d2ba2773677970600be6d65da05fa325267a19d2c1
- v2 · 2.0.4
- 8de77ed31903160adb53c77cf4aff80032144f685710c719c5490e592c9d7397
- current · 3.2.0
- c7af20de2e8a6a0064b3755ef656d653b0e6d469429ac614a7f0c03d5bc32a6c
Cold construction at scale
At 1,000,000 patterns, cold construction time changed by -59% and peak RSS by -71.3%. These are seven-process medians; cold process construction is distinct from repeated warm construction.
Construction time
Peak process RSS
Patterns on the horizontal axis. Peak RSS includes the process runtime and temporary construction state; it is not the retained dictionary size. The historical baseline loads modules through data URLs, so module-loader storage also differs within this whole-process measurement.
Exact measurements and variation
| Patterns | Before · ms | After · ms | Time change | Before MAD · ms | After MAD · ms | Before RSS · MiB | After RSS · MiB | RSS change | Before RSS MAD · MiB | After RSS MAD · MiB | Before heap · MiB | After heap · MiB | Before buffers · MiB | After buffers · MiB |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10,000 | 31.9375 | 20.737292 | -35.07% | 0.640625 | 1.149459 | 106.296875 | 71.625 | -32.62% | 0.640625 | 0.453125 | 0.54612 | 0.541451 | 1.602516 | 1.602516 |
| 100,000 | 186.384875 | 92.04775 | -50.61% | 2.611084 | 2.525708 | 407.109375 | 140.375 | -65.52% | 0.625 | 0.21875 | 3.495079 | 3.490295 | 16.022072 | 16.022072 |
| 1,000,000 | 1,945.30275 | 797.322209 | -59.01% | 67.384125 | 20.274958 | 2,300.21875 | 660.15625 | -71.3% | 2.453125 | 1.234375 | 34.217422 | 34.212784 | 160.217628 | 160.217628 |
benchmarks-builder-final.json · 2026-09-23 · 7 independent rounds · dd97451
Environment and full provenance
- node
- v24.18.0
- platform
- darwin
- arch
- arm64
- cpu
- Apple M4 Max
- icu
- 78.3
- unicode
- 17.0
- Measured at
- 2026-09-23T08:20:48.462Z
- Source revision
- dd97451efdce9586bbe7d3f442a410ed77d07853
- Working tree changes
- true
- Baseline
- b01c9f2
- Implementation SHA-256
- c7af20de2e8a6a0064b3755ef656d653b0e6d469429ac614a7f0c03d5bc32a6c
- Baseline SHA-256
- 18cd42aabc95d9d3dfcd94ae9983fdd8d1ad7e2908860f26cc36f38136e28dbf
- Runner SHA-256
- 05d461798498b3528241cf8dc5e088b4297f93da826d6caeeab01939ba2c7b85
- Report SHA-256
- ed7f42a9f1abb3eef12296b678b40edeead71419619cba954d7371fc20f2832a
Memory retained by the dictionary
The dedicated retention run disables timing and averages multiple live dictionary copies. The bars compare retained JS heap; ArrayBuffer storage is reported separately in bytes. Buffer measurements are unchanged in 18 of 18 scenarios.
After / before · baseline = 1× · zero-based scale to avoid exaggerating small differences
Exact measurements and variation
| Scenario | Before heap · B | After heap · B | Heap change | Before buffers · B | After buffers · B |
|---|---|---|---|---|---|
| Tiny ASCII | 3,929.904 | 3,902.088 | -0.71% | 0 | 0 |
| CRLF | 4,366.952 | 4,368.912 | +0.04% | 0 | 0 |
| ASCII + combining | 4,192.584 | 4,191.728 | -0.02% | 0 | 0 |
| Unicode at start | 4,148.256 | 4,146.648 | -0.04% | 0 | 0 |
| ASCII prefix | 4,081.648 | 4,066.56 | -0.37% | 0 | 0 |
| Early Unicode | 3,907.472 | 3,900.928 | -0.17% | 0 | 0 |
| Ordinary text | 13,854.672 | 13,817.168 | -0.27% | 8,156 | 8,156 |
| No matches | 29,062.84 | 29,109.44 | +0.16% | 20,356 | 20,356 |
| Mixed Unicode | 9,098.68 | 9,102.552 | +0.04% | 7,260 | 7,260 |
| Shared prefixes | 55,875.84 | 55,757.2 | -0.21% | 264,568 | 264,568 |
| Dense suffixes | 8,500.048 | 8,497.984 | -0.02% | 3,868 | 3,868 |
| High-output, no hit | 5,992.08 | 6,041.376 | +0.82% | 328 | 328 |
| High-output, sparse | 5,992.08 | 6,041.392 | +0.82% | 328 | 328 |
| Duplicate patterns | 14,492.592 | 14,432.592 | -0.41% | 1,600 | 1,600 |
| Early hit | 3,907.472 | 3,900.928 | -0.17% | 0 | 0 |
| Late hit | 3,907.472 | 3,900.928 | -0.17% | 0 | 0 |
| 10k patterns | 498,346.4 | 496,666.4 | -0.34% | 1,680,360 | 1,680,360 |
| Long text | 4,845.152 | 4,823.632 | -0.44% | 0 | 0 |
benchmarks-retained-optimization.json · 2026-09-23 · 7 independent rounds · 0d10f03
Environment and full provenance
- node
- v24.18.0
- platform
- darwin
- arch
- arm64
- cpu
- Apple M4 Max
- icu
- 78.3
- unicode
- 17.0
- Measured at
- 2026-09-23T08:13:54.285Z
- Source revision
- 0d10f03bade9609faf6bbe086b0dd4666452c4e0
- Working tree changes
- true
- Baseline
- b01c9f2
- Implementation SHA-256
- 6716d18808b34e066dc30eaaaa8b90d773adc567feda47150f18e51b0283296c
- Baseline SHA-256
- 68cd0ffc3df077980b83ce045256e4627b9c006a0d086f334c32492bb13ffb18
- Corpus SHA-256
- 4afa3eee0768ace1840771227bd94753d446ab82a09bbaf11d2acf1834462c5e
- Report SHA-256
- 71bc3027f7f0ff0101008e492526541a79f895ec517aff726c158a9e87a32ad0
10,000-pattern dictionary across versions
Stacked components share the same unit: 1 MiB = 1,048,576 bytes. This excludes returned result objects and process peak RSS.
Dictionary memory by version
| Version | JS heap · B | ArrayBuffer · B | Combined · MiB |
|---|---|---|---|
| v1 | 7,366,382.4 | 0 | 7.02513 |
| v2 | 7,366,560 | 0 | 7.025299 |
| Optimized v3 | 496,567.2 | 1,680,360 | 2.07608 |
benchmarks-versions-optimization.json · 2026-09-23 · 5 independent rounds · dd97451
Environment and full provenance
- node
- v24.18.0
- pnpm
- pnpm@12.5.1
- platform
- darwin
- arch
- arm64
- cpu
- Apple M4 Max
- icu
- 78.3
- unicode
- 17.0
- Measured at
- 2026-09-23T08:22:10.112Z
- Source revision
- dd97451efdce9586bbe7d3f442a410ed77d07853
- Working tree changes
- true
- Implementation SHA-256
- c7af20de2e8a6a0064b3755ef656d653b0e6d469429ac614a7f0c03d5bc32a6c
- Runner SHA-256
- e557a2c3f4c207fb5a8f2dbf24a9da15572830e94a005716559218cb6323f7ea
- Corpus SHA-256
- 8dbac3966d9f23c4047c59576d55779948d57a39c7603acdf7b748506f701d4d
- Report SHA-256
- f9df675eeca635f032eb77e18f107bd6ba7d8031194ee4a4fcf7dcb2d6dcd9f3
- v1 · 1.1.0
- 92e364bded18733ea20c25d2ba2773677970600be6d65da05fa325267a19d2c1
- v2 · 2.0.4
- 8de77ed31903160adb53c77cf4aff80032144f685710c719c5490e592c9d7397
- current · 3.2.0
- c7af20de2e8a6a0064b3755ef656d653b0e6d469429ac614a7f0c03d5bc32a6c
Long streams with continuous consumption
The caller consumes every output batch immediately. Points show post-GC heap growth relative to a baseline taken after creating the dictionary and input. This excludes their preexisting memory and caller-retained outputs, as well as native ICU allocations and peak RSS. Straight lines only connect measured input sizes; runtime and JIT noise remain included.
all
leftmost-longest
Before EOF, optimized leftmost-longest had already emitted 79,997 of 80,000 matches. The table also shows retained memory with the handle still held after finish and cancel.
Exact measurements and variation
| Strategy / snapshot | UTF-16 | Heap growth · KiB | ArrayBuffer growth · KiB | Emitted before EOF | Final matches | Closed heap growth · KiB | Closed buffer growth · KiB |
|---|---|---|---|---|---|---|---|
| all · Before | 40,000 | 2,031.015625 | 0 | 11,997 | 120,000 | 14,666.757813 | 0 |
| all · Before | 120,000 | 3,848.695313 | 0 | 35,997 | 120,000 | 14,666.757813 | 0 |
| all · Before | 400,000 | 14,707.84375 | 0 | 119,997 | 120,000 | 14,666.757813 | 0 |
| all · After | 40,000 | 263.6875 | 0 | 11,997 | 120,000 | 158.46875 | 0 |
| all · After | 120,000 | 268.117188 | 0 | 35,997 | 120,000 | 158.46875 | 0 |
| all · After | 400,000 | 264.007813 | 0 | 119,997 | 120,000 | 158.46875 | 0 |
| leftmost-longest · Before | 40,000 | 2,899.570313 | 0 | 0 | 80,000 | 23,179.203125 | 0 |
| leftmost-longest · Before | 120,000 | 6,388.929688 | 0 | 0 | 80,000 | 23,179.203125 | 0 |
| leftmost-longest · Before | 400,000 | 23,208.25 | 0 | 0 | 80,000 | 23,179.203125 | 0 |
| leftmost-longest · After | 40,000 | 271.140625 | 0 | 7,997 | 80,000 | 158.4375 | 0 |
| leftmost-longest · After | 120,000 | 275.679688 | 0 | 23,997 | 80,000 | 158.4375 | 0 |
| leftmost-longest · After | 400,000 | 271.09375 | 0 | 79,997 | 80,000 | 158.4375 | 0 |
benchmarks-stream-optimization.json · 2026-09-23 · 3 independent rounds · b6ebdce
Environment and full provenance
- node
- v24.18.0
- platform
- darwin
- arch
- arm64
- cpu
- Apple M4 Max
- icu
- 78.3
- unicode
- 17.0
- Measured at
- 2026-09-23T06:23:33.062Z
- Source revision
- b6ebdce7f04a8c511060a9af9a65ff64e9c9c04f
- Working tree changes
- true
- Baseline
- b01c9f22b2566b3a6058b41b05564b214899c8d7
- Implementation SHA-256
- 593af04bb53db03a3dd84c0be75c30ec04dc51cbd6e863af74db75d6a0e1fb7a
- Baseline SHA-256
- 8c4bb499ac67a4b7917f264e0f84b36c5d88a3ba7cd0f5cdb0c75fd87b9b3566
- Report SHA-256
- 0da839a7d97d632f9646508c5cc4a96da35371bb9e689a4013659235b573d389
Backend choices
Start with the default compact backend. /fast opts into double-array storage; /unicode-fast combines that backend with full folding. The default import does not load the optional compiler or folding data.
ts
import FastAhoCorasick from 'modern-ahocorasick/fast'
const matcher = new FastAhoCorasick(['cat'])
matcher.search('cat')[0]?.start // 0
matcher.getStats().backend // 'double-array'Measured tradeoffs
/fast is an explicit double-array backend, not a universal speed guarantee. It can reduce ArrayBuffer storage while increasing JavaScript heap usage; cursor overhead may dominate scans. In the snapshot before the streaming, scanner and builder optimizations, on Node 24.18.0 / Apple M4 Max, the 10,000-pattern ASCII comparison measured 28.23 ms construction and 4.34 ms search for /fast, versus 13.61 ms and 2.00 ms for the default backend. Retained heap was 3.52 MiB versus 0.47 MiB; buffers were 1.18 MiB versus 1.60 MiB. These are three-round medians, not measurements of the optimized build or a prediction for other dictionaries. Boundaries and folding use the general cursor, so filtered count() does enumerate accepted outputs. The default exact counting and presence paths retain their allocation-light implementation.
The feature comparison targets @monyone/aho-corasick@1.5.10 and @tanishiking/aho-corasick@0.0.1. Their index, duplicate and Unicode contracts differ; this package preserves its own ranges and semantics rather than copying defects or method aliases. Run pnpm benchmark:external for the printable-ASCII comparison, including normalized independent ranges, native results, build costs and memory.
Run pnpm benchmark:versions to compare pinned npm v1.1.0 and v2.0.4 against the current local default entry: construction, native search, conversion to independent ranges and retained memory. Historical Unicode cases with incorrect results receive no speed ratios. The optimized-build version comparison includes the current measurements; the earlier v1/v2/v3 report remains a historical snapshot.
In the optimized build's five-round comparison, independent range output was faster than v2 in all 14 fixtures. Seven-round rechecks confirmed v1 still scanned some ASCII inputs faster, and historical native grouped output has different allocation costs. The 10,000-pattern dictionary retained about 2.08 MiB of heap plus buffers, versus 7.03 MiB for either older version. These results support choosing by the output contract and workload, rather than a universal version ranking.
The streaming, scanning and construction report records the subsequent changes against b01c9f2: incremental transformed-stream retention, an 18-scenario scanner comparison, retained-dictionary measurements, and cold construction at 10,000–1,000,000 patterns. It distinguishes repeated warm construction, retained heap, ArrayBuffer storage and construction peak RSS, with raw process results, implementation fingerprints and targeted rechecks.
Full enumeration uses native grapheme segmentation for dictionaries that can emit many matches at one state. This avoids extra overhead in dense output, but those dictionaries also use that path when an input has few or no matches. ASCII acceleration therefore depends on the dictionary as well as the text; measure representative no-hit inputs too. Counting and selected-match scans keep their own paths.
Reproduce and interpret
sh
pnpm benchmark
pnpm benchmark:external
pnpm benchmark:versions
pnpm benchmark:scale
pnpm benchmark:stream
pnpm benchmark:docsRun these commands from the repository. Compare build and scan time, retained heap and buffers separately; getStats().typedArrayBytes is not total heap. Filtering, folding, result arrays and longest-first have costs beyond the default exact scan. The workbench includes debounce, Worker and transfer time, so its display is not a library benchmark.
Sources: Extension benchmarks · v2/v3 benchmarks · ASCII benchmarks · Workbench measurements