deniz.in

Markets

Weather

Loading weather

· via dev.to (home feed)

Node.js 26's Map.getOrInsertComputed benchmarks up to 40% slower than the get-and-set idiom

A dev.to benchmark finds Node.js 26's Map.getOrInsertComputed is 18-40% slower than the get-and-set pattern it replaces, though it adds built-in cache-stampede protection and caches rejected results indefinitely.

Node.js 26's Map.getOrInsertComputed benchmarks up to 40% slower than the get-and-set idiom

Node.js 26 ships V8 14.6, which brings two long-requested Map methods: getOrInsert() and getOrInsertComputed(), both also available on WeakMap. They collapse the familiar lookup-compute-store-return snippet into a single call. But benchmarks published on dev.to by alexgeorgiev17 indicate the computed variant is slower than the pattern it replaces — roughly 18% slower on an all-miss workload and roughly 40% slower on a warm cache, which is the typical case for real caches.

The tests ran on Node 26.13.0, the "Lithium" release dated 28 October 2026, carrying V8 14.6.202.34-node.40.

How the benchmark was run

Two workloads were timed with process.hrtime.bigint(): five million insertions of unique keys, where every call is a miss, and five million lookups spread across 1,000 keys, where every call is a hit. Reported figures are the fastest and median of five to nine repetitions.

  • All misses: the classic get-then-set sequence took a median of 1,678 ms against 1,978 ms for getOrInsertComputed, about 18% slower.
  • All hits: 29.7 ms against 41.6 ms, about 40% slower.

Because a gap that large on a warm cache seemed implausible, the author repeated the exercise with the implementations alternating order across nine runs, to cancel CPU frequency and cache-warming drift, and added a third contender: a plain JavaScript function mirroring the native method, receiving the map, the key and the callback. The built-in finished last of the three in both orderings; the hand-written wrapper was never slower than the engine's own method and sometimes beat it.

The likely explanation, which the author flags as an untested hypothesis since it cannot be verified from outside the engine, is that V8's optimising compiler has spent years specialising get and set as inlineable monomorphic calls, while the new method has not yet received the same treatment on this build.

The trade: built-in deduplication of concurrent async work

Where the method does pay off is asynchronous correctness. Because the insert happens synchronously, caching promises rather than resolved values means simultaneous requests for the same key share a single computation. In the dev.to example, three simultaneous requests for one key together with one for a different key caused only two executions of the fetching function. That reproduces, in one line, the in-flight promise bookkeeping that server code commonly writes by hand to prevent request pile-ups on a slow backend.

Cached rejections and discarded side effects

The same behaviour has sharp edges. A rejected promise is stored just like a fulfilled one, and nothing evicts it: in the author's demonstration, a function designed to fail once and then succeed never received its second attempt, because the first rejection was permanently stored under the key and every subsequent caller got the stale error. Anyone using the method to deduplicate network calls must add their own catch handler that deletes the failed key.

The method is also not atomic with respect to the callback. The engine records that the key was missing before invoking the callback, then writes the callback's return value regardless of what the callback did to that key in between — a set performed inside the callback is silently overwritten, and even a delete of the key is rolled back, with the key reappearing holding the returned value.

Why it matters

These methods were added to replace boilerplate, so they are likely to spread quickly through memoisation and caching code. The dev.to numbers suggest hot paths adopting getOrInsertComputed today pay a measurable penalty, at least until V8 gives the new method the same optimisation attention as get and set. The concurrent-work deduplication is a genuine benefit for I/O-bound code, but the indefinite caching of failures is a production hazard: one transient error can poison a cache entry until something explicitly removes it. Developers on Node 26 get the best of both by keeping plain get and set on hot synchronous paths and reserving the new method for concurrent async work, with failure-path eviction wired in from the start.

  • #node-js
  • #javascript
  • #performance
  • #v8
  • #caching

Related posts