Skip to content

make Map the default (round 2) #40

Open
@gurgunday

Description

I finally cracked the code after digging through more benchmarks and stumbling upon the comment of a V8 developper

TLDR is that if we use ordered keys as we do in the benchmarks, V8 will highly optimize property access. To illustrate, if we just force the keys to be non-numeric, with something like x + '_', the picture becomes much more interesting :)

Objection

Benchmarks

Let's modify the execute function to make our keys nonnumeric, the following results surface for cache-get-inmemory/:

{ cpu: { brand: 'M1', speed: '2.40 GHz' } }
| Node   | Option             | Msecs/op       | Ops/sec  | V8                    |
| ------ | ------------------ | -------------- | -------- | --------------------- |
| 20.9.0 | toad-cache-lru-map | 0.434283 msecs | 2302.644 | V8 11.3.244.8-node.16 |
| 20.9.0 | toad-cache-lru     | 0.601765 msecs | 1661.777 | V8 11.3.244.8-node.16 |

Here's the execute functions of toad-cache-lru-map.js and toad-cache-lru-object.js:

async function execute() {
  for (var x = 0; x < ELEMENT_COUNT; x++) {
    const value = cache.get(x + 'a') // became x + 'a'
    if (!value) {
      const newValue = {
        id: x
      }

      cache.set(x + 'a', newValue) // became x + 'a'
    }
  }
}

Then, I realized another potential issue with the default parameters — MAX_ITEMS was higher than ELEMENT_COUNT

Ideally, MAX_ITEMS should be less than ELEMENT_COUNT so that we can have deletions as well to get a more complete picture of performance

Let's modify common.js:

const { validateEqual } = require("validation-utils");

const MAX_ITEMS = 5000 // became 5000 from 10000
const TTL = 1000 * 60 * 60
const ELEMENT_COUNT = 8500 // became 8500 from 5000

async function validateAccuracy(getFn) {
  const value = await getFn('123')
  validateEqual(value.id, '123')
}

module.exports = {
  ELEMENT_COUNT,
  MAX_ITEMS,
  TTL,
  validateAccuracy,
};

Here are the results of a rerun:

{ cpu: { brand: 'M1', speed: '2.40 GHz' } }
| Node   | Option             | Msecs/op       | Ops/sec | V8                    |
| ------ | ------------------ | -------------- | ------- | --------------------- |
| 20.9.0 | toad-cache-lru-map | 1.387450 msecs | 720.747 | V8 11.3.244.8-node.16 |
| 20.9.0 | toad-cache-lru     | 2.053608 msecs | 486.948 | V8 11.3.244.8-node.16 |

I argue that a generic LRU will not have number-based keys, especially not the very special case of using consecutive integers as keys — if that was the case, we'd use an array — and I propose once again to make the default export Map-based

What do you think?

If you do not agree, it's fine! But I feel like repos such as fastify-rate-limit would greatly benefit from the change

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions