Skip to content

Object property delete/set in loop got very slow in Node 12 (quadratic?) #28571

Closed
@cakoose

Description

@cakoose

I was benchmarking different data structures and noticed a huge slowdown for the following test case:

  1. Create an object with a bunch 20-character string properties, the value is always set to the number 1.
  2. Loop over each key: delete it from the object and add it back.

The performance of object and Map is roughly the same in Node 11.15.0. In Node 12, the object version gets really slow (~10x slower for a 100-element object, ~60x slower for a 1000-element object).

Here's my full test case:

"package.json"

{
    "dependencies": {
        "benchmark": "2.1.4"
    }
}

"main.js"

const Benchmark = require('benchmark')

const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ';

function randomString(length) {
    const parts = [];
    for (let i = 0; i < length; i++) {
        parts.push(digits.charAt(Math.random() * digits.length));
    }
    return parts.join('');
}

console.log(`Node ${process.versions.node}, V8 ${process.versions.v8}`);

for (const size of [10, 100, 1000, 10000]) {
    const keys = []
    const object = {};
    const map = new Map();
    for (let i = 0; i < size; i++) {
        const key = randomString(20);
        keys.push(key);
        object[key] = 1;
        map.set(key, 1);
    }

    const suite = new Benchmark.Suite();
    {
        let keyI = 0;
        suite.add('Object', () => {
            const key = keys[keyI++];
            if (keyI >= keys.length) {
                keyI = 0;
            }
            delete object[key];
            object[key] = 1;
        });
    }

    {
        let keyI = 0;
        suite.add('Map', () => {
            const key = keys[keyI++];
            if (keyI >= keys.length) {
                keyI = 0;
            }
            map.delete(key);
            map.set(key, 1);
        });
    }

    suite.on('cycle', event => {
      console.log('  ' + String(event.target));
    });

    console.log(`size: ${size}`);
    suite.run();
}

Results

Node 11.15.0, V8 7.0.276.38-node.19
size: 10
  Object x 12,638,709 ops/sec ±2.40% (88 runs sampled)
  Map x 18,837,340 ops/sec ±1.18% (88 runs sampled)
size: 100
  Object x 18,198,146 ops/sec ±1.08% (92 runs sampled)
  Map x 16,736,960 ops/sec ±2.69% (81 runs sampled)
size: 1000
  Object x 9,668,335 ops/sec ±4.26% (75 runs sampled)
  Map x 14,076,212 ops/sec ±2.19% (81 runs sampled)
size: 10000
  Object x 7,074,321 ops/sec ±1.51% (86 runs sampled)
  Map x 11,127,011 ops/sec ±1.69% (87 runs sampled)

Node 12.0.0, V8 7.4.288.21-node.16
size: 10
  Object x 5,006,773 ops/sec ±10.47% (41 runs sampled)
  Map x 18,186,938 ops/sec ±1.41% (90 runs sampled)
size: 100
  Object x 1,026,833 ops/sec ±115.69% (9 runs sampled)
  Map x 16,332,681 ops/sec ±2.39% (85 runs sampled)
size: 1000
  Object x 204,617 ops/sec ±226.52% (9 runs sampled)
  Map x 14,577,494 ops/sec ±1.44% (87 runs sampled)
size: 10000
  Object x 89,706 ops/sec ±202.29% (26 runs sampled)
  Map x 11,407,806 ops/sec ±2.25% (86 runs sampled)

Node 12.6.0, V8 7.5.288.22-node.14
size: 10
  Object x 4,848,704 ops/sec ±8.96% (47 runs sampled)
  Map x 13,350,969 ops/sec ±1.14% (87 runs sampled)
size: 100
  Object x 1,331,497 ops/sec ±128.56% (11 runs sampled)
  Map x 14,494,479 ops/sec ±1.71% (86 runs sampled)
size: 1000
  Object x 360,305 ops/sec ±208.01% (14 runs sampled)
  Map x 11,921,007 ops/sec ±1.29% (88 runs sampled)
size: 10000
  Object x 74,324 ops/sec ±202.57% (27 runs sampled)
  Map x 10,444,440 ops/sec ±1.14% (89 runs sampled)

Metadata

Metadata

Assignees

No one assigned

    Labels

    performanceIssues and PRs related to the performance of Node.js.v8 engineIssues and PRs related to the V8 dependency.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions