Node 22 vs 24 vs 26: what actually got faster

Sanjeev SharmaSanjeev Sharma
13 min read

Advertisement

Every Node release post lists what is new. Almost none of them tell you whether your service will be faster, and the benchmarks that circulate afterwards are usually one run of each version, in order, on a laptop that was also doing something else.

This is the same seven workloads under Node 22, 24 and 26, interleaved across seven rounds, with the round-to-round spread printed beside every number so you can see which differences are real.

The short answer

On an API-shaped payload, Node 26 serialises JSON about 25% faster than Node 22, and that is the only large win in this set. Map insertion changed too, though it moved around between versions rather than improving steadily. Parsing, sorting, string concatenation, regex matching and hashing all landed inside the noise of the machine they ran on.

Why do most version benchmarks measure the wrong thing?

Because of how they are run. The usual shape is: run everything under version A, then everything under B, then C. That produces a tidy table and a wrong answer, because whatever else the machine was doing during the second block is charged to version B.

My first attempt did exactly that. Node 26 hashed 8MB in 6.46 ms and Node 24 took 9.71 ms, which reads like a 33% regression in Node 24. Fifteen minutes later the same command gave Node 22 the best hashing number and Node 26 the worst. The runtime had not changed. The laptop had.

blocked: one version at a timenode 22node 24node 26background build starts here→ the whole cost lands on node 24interleaved: one round of each, seven timessame background build→ the cost is shared, and the spread shows it happened

Interleaving does not remove interference. It stops interference from being attributed to one version, and the spread between rounds tells you how much of it there was.

What was measured

Seven workloads that a web service actually spends time on, not synthetic loops: parsing and serialising a 20,000-row API response, sorting 200,000 integers, concatenating 50,000 strings, running a regex over 100,000 email addresses, hashing 8MB with SHA-256, and filling a Map with 200,000 keys.

Each round runs every workload five times inside the container and keeps the median. The runner then takes the best round per version, because on a shared machine interference only ever adds time — the fastest round is the one that was interrupted least.

Seven workloads, best of seven interleaved roundsmilliseconds, lower is better
  • JSON.stringify · 2218.49
  • JSON.stringify · 2415.64
  • JSON.stringify · 2614.8
  • Map 200k keys · 2225.93
  • Map 200k keys · 2420.87
  • Map 200k keys · 2623.28
  • JSON.parse · 2212.72
  • JSON.parse · 2413.33
  • JSON.parse · 2613.98
  • JSON.stringify · 22 — spread between rounds ±23%
  • JSON.stringify · 24 — spread ±9%
  • JSON.stringify · 26 — spread ±14% — the one clear win
  • Map 200k keys · 24 — fastest, but 26 is slower again
  • JSON.parse · 26 — gap smaller than the ±27% spread

The full table, including the workloads that showed nothing:

WorkloadNode 22Node 24Node 26Verdict
JSON.stringify, 20k rows18.49 ms15.64 ms14.80 ms26 faster by 25%
Map of 200k keys25.93 ms20.87 ms23.28 ms24 faster by 24%, not linear
JSON.parse, 20k rows12.72 ms13.33 ms13.98 mswithin noise
sort 200k numbers41.72 ms37.76 ms37.44 mswithin noise
string concat 50k0.47 ms0.57 ms0.58 mswithin noise
regex over 100k emails6.87 ms7.07 ms6.20 mswithin noise
SHA-256 of 8MB6.44 ms6.33 ms6.29 mswithin noise

"Within noise" means the gap between the best and worst version was smaller than the spread one version showed across its own rounds. Publishing those as wins would be publishing the laptop.

Why is JSON.stringify the one that moved?

Because it is the one V8 actually rewrote. V8 13.8 shipped a new serialiser: a side-effect-free iterative fast path, SIMD scanning for characters that need escaping, Dragonbox instead of Grisu3 for number-to-string, and a segmented output buffer so large documents stop re-allocating (V8 blog).

The headline on that work is "more than twice as fast", and it is honest about where: the json-stringify-inspector benchmark in JetStream2. On a payload shaped like an API response — 20,000 small objects with strings, arrays and a nested meta object — the same change is worth 25% end to end. Both numbers are real. They answer different questions, and yours is the second one.

Note also that Node 24 ships V8 13.6, which is before that rewrite, yet it is already faster than Node 22 here. Serialisation improved across several V8 releases; the 13.8 work is the largest single step, not the only one.

What is 25% off serialisation worth to your service?

Serialisation time saved per second

1.3 ms

120 req × 60 KB ÷ 1024 × 0.74 ms/MB × 25%

As a share of one core

0.13%

saved ms ÷ 1000 ms of wall clock, per core

Cores freed across the fleet

0.001

at 4 cores provisioned, this is what the upgrade returns

0.74 ms/MB is this article's own measurement: 14.8 ms for a 20,000-row payload that serialises to roughly 20 MB of JSON. Your payload shape will differ — run the script against it rather than trusting this line.

For most services the answer is "not much, and that is fine". A 25% cut on a few milliseconds per request is not why you upgrade. It is worth knowing so you stop expecting a version bump to fix a latency problem that lives in your database — which is usually where it lives, as the Postgres performance tuning and pagination posts go into.

The workload file is worth reading before trusting any of this. It times with process.hrtime.bigint() rather than Date.now(), because a millisecond clock cannot resolve a 0.5 ms string concatenation, and it takes a median of five passes inside each container so one garbage-collection pause never becomes the published number.

Which version should you actually run?

The performance table is not the deciding input. Support windows are.

OptionStatus todayV8EndsPick it when
Node 22 (Jod)Maintenance LTS12.4Apr 2027You are on it and shipping; plan the move, do not rush it
Node 24 (Krypton)defaultActive LTS13.6Apr 2028Anything in production today
Node 26Current14.6LTS from Oct 2026You want the serialiser now and can take Current-line churn
Release status read from nodejs.org on 30 September 2026. From Node 27 the cycle becomes annual, with every major moving to LTS six months after release.

Node 24 is Active LTS and the default answer for production (Node.js releases). Node 26 is Current until October, and the project's own guidance is that production should run Active or Maintenance LTS. The 25% serialisation win is not a reason to break that rule; it is a reason to look forward to April, when 26 is the LTS.

How to run this on your own machine

The two scripts are in the repository this site is built from: tools/bench/node-versions.mjs is the workload, tools/bench/run-versions.mjs is the interleaving runner.

Reproducing the table
1 / 5

If your numbers disagree with this table, yours are the ones about your machine — which is the only machine your service runs on.

What this does not measure

Startup time, HTTP throughput, memory ceiling under load, and anything involving the network. Those need a different harness — a load generator, a warm-up period and a server under sustained concurrency — and they are where the more interesting differences between runtimes usually live. That is the next article in this series rather than a paragraph in this one.

It also does not measure your dependencies. A native addon compiled against an older ABI, a package that ships its own V8-version checks, or a transpiler step in the boot path can each cost more than every difference in the table above. The Node error handling and performance profiling posts cover finding those before blaming the runtime.

Check your reading of the table

3 questions — answers explained as you go.

  1. 1. Node 26 hashed 8MB in 6.29 ms and Node 22 took 6.44 ms. What should you conclude?

  2. 2. V8 says JSON.stringify is "more than twice as fast", this article measured 25%. Which is wrong?

  3. 3. Your production service is on Node 22 today. What does this table tell you to do?

Frequently Asked Questions

Is Node 26 faster than Node 24?

For JSON serialisation, slightly — 14.8 ms against 15.64 ms on a 20,000-row payload, which is inside the measurement spread on this machine. The clear gap is against Node 22, at about 25%. For parsing, sorting, regex and hashing, the three versions measured the same.

Why did JSON.parse not get faster when stringify did?

The V8 work published in 2025 targeted serialisation specifically: a side-effect-free fast path, SIMD escaping and a segmented output buffer. Parsing was not part of that change, and this measurement shows it — parse times moved by less than the noise.

Should I upgrade Node for performance?

Rarely. Upgrade for support windows, security backports and platform features. The measured performance difference between adjacent LTS lines on ordinary workloads is small enough that it will not fix a latency problem — those usually live in the database or in a serial chain of awaits.

How many rounds are enough for a benchmark like this?

Enough that the spread stops shrinking. Seven interleaved rounds was the point where the spread stabilised on this machine; on a quiet dedicated server, three is usually plenty. Report the spread either way, because it is what tells a reader whether to believe the gap.

Does this apply to Bun or Deno?

No. Both ship different JavaScript engines or different versions of the same one, and both change the IO layer as well. That comparison needs a load generator rather than in-process timings, and it is the next post in this series.

Where this leaves you

Two numbers out of seven survived contact with an honest method, and one of them is not linear across versions. That is a duller result than most upgrade posts report, and it is the useful one: it means a Node upgrade is a support decision, not a performance lever, and that the time you were about to spend on it is better spent on the query that takes 40 ms.

If you want the tooling side of that, the backend performance checklist lists what to measure before touching a runtime version, and async logging covers the one part of a Node service that is quietly synchronous more often than people expect. For the request path itself, Express REST API still describes the shape most of these services have.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro

Related reading