Native WeakValueMap

I think adding a native WeakValueMap to the standard would be useful for caching a large number of objects.

In modern runtimes, instantiating WeakRef has about the same performance as instantiating Maps, WeakMap, Sets, & WeakSets. When creating many WeakRefs, the overhead may be an issue. Given the number of ops is 1M to 10M per second. While this is reasonable in most cases these operations are orders of magnitude slower than map.get & map.set & creating an empty object.

Being able to instantiate a single WeakValueMap instead of n WeakRef objects can help in making client side libraries smaller & faster as well as server side apps handle more traffic.

Having a native WeakValueMap also reduces the need for a dependency when this behavior is needed.

To check: by 'WeakValueMap` this is referring to something like this: GitHub - tc39/proposal-weakrefs: WeakRefs

I expect it'd be more something like this: WeakValueMap.mjs ยท GitHub

While it's not straightforward to get right, I would be opposed to get things like that in the language because:

  • it's not obvious what the expected semantics are, and often times a bare WeakValueMap like this is not the right tool for the job.
  • I don't want new ways to observe GC (unless it's a fundamental new building block that can't be built from the existing features we already have)

I think a library is exactly where this kind of WeakValueMap belongs. And I doubt a native implementation would have much performance advantages over a library.

1 Like

In my experience, weak value maps are almost always the wrong thing to use. As values get dropped for non-use, you'll risk having keys recreated over and over, creating performance issues. Look at Cache replacement policies - Wikipedia instead for much better ways to do automatic cache eviction.

And for most caching, you should be using TTLs for size-independent automatic expiry, not weak values. If you genuinely have millions of keys and extremely frequent updates, use an eviction timestamp field (set to the performance.now() of the entry's creation) on each entry and a (monotone) priority queue sorted by that field. Maintain a single timer that repeatedly waits for Math.ceil(performance.now() - minDeadline) (rescheduling as necessary to target the most recent closest deadline), so you only walk one link each time. This keeps memory overhead down (~3 properties per entry) much better without sacrificing performance, and it'll likely outperform even a natively implemented WeakValueMap. Plus, you'll get way more cache hits.

1 Like