Skip to content

Instantly share code, notes, and snippets.

@jung-kim
Last active May 25, 2023 07:01
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save jung-kim/83676b2310c7c2a9c3d8 to your computer and use it in GitHub Desktop.
Save jung-kim/83676b2310c7c2a9c3d8 to your computer and use it in GitHub Desktop.
es6 map vs array vs obj performance test
"use strict";
let looper = (callback) => {
let n = 2000000;
while (n > 0) {
callback(n);
n--;
}
}
let timer = (log, callback) => {
let start = Date.now();
callback()
console.log(log, Date.now() - start);
}
let map = new Map();
let obj = {};
let ray = [];
timer('Map int key set took: ', () => looper(index => map.set(Math.random() * 2000000, index)));
timer('Obj int key set took: ', () => looper(index => obj[Math.random() * 2000000] = index));
timer('ray int key set took: ', () => looper(index => ray[Math.random() * 2000000] = index));
console.log();
timer('Map int key get took: ', () => looper(index => {let dummylet = map.get(Math.random() * 2000000) }));
timer('Obj int key get took: ', () => looper(index => {let dummylet = obj[Math.random() * 2000000] }));
timer('ray int key get took: ', () => looper(index => {let dummylet = ray[Math.random() * 2000000] }));
console.log('\n\n');
map = new Map();
obj = {};
ray = [];
timer('Map string key set took: ', () => looper(index => map.set('' + Math.random() * 2000000, '' + index)));
timer('Obj string key set took: ', () => looper(index => obj['' + Math.random() * 2000000] = '' + index));
timer('ray string key set took: ', () => looper(index => ray['' + Math.random() * 2000000] = '' + index));
console.log();
timer('Map string key get took: ', () => looper(index => {let dummylet = map.get('' + Math.random() * 2000000) }));
timer('Obj string key get took: ', () => looper(index => {let dummylet = obj['' + Math.random() * 2000000] }));
timer('ray string key get took: ', () => looper(index => {let dummylet = ray['' + Math.random() * 2000000] }));
// // Example output in node v8
//
// Map int key set took: 1072
// Obj int key set took: 2460
// ray int key set took: 3457
//
// Map int key get took: 824
// Obj int key get took: 1609
// ray int key get took: 2246
//
//
//
// Map string key set took: 1580
// Obj string key set took: 2933
// ray string key set took: 2149
//
// Map string key get took: 1511
// Obj string key get took: 1575
// ray string key get took: 1818
@tabarra
Copy link

tabarra commented May 2, 2020

Update from May/2020 with NodeJS v12.16.2:

Map int key set took:  1419
Obj int key set took:  2249
ray int key set took:  2139

Map int key get took:  31
Obj int key get took:  2002
ray int key get took:  1825


Map string key set took:  1442
Obj string key set took:  2168
ray string key set took:  3007

Map string key get took:  32
Obj string key get took:  2167
ray string key get took:  1598

@FishOrBear
Copy link

FishOrBear commented Aug 20, 2020

if key is index

"use strict";

let looper = (callback) =>
{
    let n = 2000000;
    while (n > 0) {
        callback(n);
        n--;
    }
}

let timer = (log, callback) =>
{
    console.time(log)
    callback()
    console.timeEnd(log)
}

let map = new Map();
let obj = {};
let ray = [];

function RandomIndex(count)
{
    return Math.floor(Math.random() * count);
}


timer('Map int key set took: ', () => looper(index => map.set(RandomIndex(500), index)));
timer('Obj int key set took: ', () => looper(index => obj[RandomIndex(500)] = index));
timer('ray int key set took: ', () => looper(index => ray[RandomIndex(500)] = index));
console.log();
timer('Map int key get took: ', () => looper(index => { let dummylet = map.get(RandomIndex(500)) }));
timer('Obj int key get took: ', () => looper(index => { let dummylet = obj[RandomIndex(500)] }));
timer('ray int key get took: ', () => looper(index => { let dummylet = ray[RandomIndex(500)] }));

console.log('\n\n');

map = new Map();
obj = {};
ray = [];

timer('Map string key set took: ', () => looper(index => map.set('' + RandomIndex(500), '' + index)));
timer('Obj string key set took: ', () => looper(index => obj['' + RandomIndex(500)] = '' + index));
timer('ray string key set took: ', () => looper(index => ray['' + RandomIndex(500)] = '' + index));
console.log();
timer('Map string key get took: ', () => looper(index => { let dummylet = map.get('' + RandomIndex(500)) }));
timer('Obj string key get took: ', () => looper(index => { let dummylet = obj['' + RandomIndex(500)] }));
timer('ray string key get took: ', () => looper(index => { let dummylet = ray['' + RandomIndex(500)] }));

@MaloKingi6
Copy link

very handy; thanks much!. I have not seen anything except tests against pre-sorted data elsewhere (and usually integers) which is useless for real life testing. Bit of a mystery why int key set dramatically decreased in performance in Mar 2020, but overall this seems to indicate significant optimization of get (which is what I'm looking for) in every single case. That alone makes it worth while.

@zen0wu
Copy link

zen0wu commented Jan 13, 2022

The fact that array is slower than Map makes little sense to me.

Here's a updated benchmark:

"use strict";

let looper = (callback) => {
  let n = 2000000;
  while (n > 0) {
    callback(n);
    n--;
  }
};

let timer = (log, callback) => {
  let start = Date.now();
  callback();
  console.log(log, Date.now() - start);
};

let map = new Map();
let obj = {};
let ray = [];

for (let i = 0; i < 2000000; i++) {
  ray.push(i);
}

function RandomIndex(count) {
  return Math.floor(Math.random() * count);
}

timer("Map int key set took: ", () =>
  looper((index) => map.set(RandomIndex(2000000), index))
);
timer("Obj int key set took: ", () =>
  looper((index) => (obj[RandomIndex(2000000)] = index))
);
timer("ray int key set took: ", () =>
  looper((index) => (ray[RandomIndex(2000000)] = index))
);
console.log();
timer("Map int key get took: ", () =>
  looper((index) => {
    let dummylet = map.get(RandomIndex(2000000));
  })
);
timer("Obj int key get took: ", () =>
  looper((index) => {
    let dummylet = obj[RandomIndex(2000000)];
  })
);
timer("ray int key get took: ", () =>
  looper((index) => {
    let dummylet = ray[RandomIndex(2000000)];
  })
);

console.log("\n\n");

map = new Map();
obj = {};
ray = [];

timer("Map string key set took: ", () =>
  looper((index) => map.set("" + RandomIndex(2000000), "" + index))
);
timer("Obj string key set took: ", () =>
  looper((index) => (obj["" + RandomIndex(2000000)] = "" + index))
);
timer("ray string key set took: ", () =>
  looper((index) => (ray["" + RandomIndex(2000000)] = "" + index))
);
console.log();
timer("Map string key get took: ", () =>
  looper((index) => {
    let dummylet = map.get("" + RandomIndex(2000000));
  })
);
timer("Obj string key get took: ", () =>
  looper((index) => {
    let dummylet = obj["" + RandomIndex(2000000)];
  })
);
timer("ray string key get took: ", () =>
  looper((index) => {
    let dummylet = ray["" + RandomIndex(2000000)];
  })
);

Two key points:

  1. As @FishOrBear pointed out, in the first test, the index needs to be integer, array is designed to be "an array", which doesn't support float keys.
  2. do not leave holes in array, when initializing, push the data as they go, where in the original benchmark, the array can have arbitrary gaps, which stops v8 from optimizing.

updated results

Map int key set took:  449
Obj int key set took:  432
ray int key set took:  45

Map int key get took:  31
Obj int key get took:  32
ray int key get took:  33



Map string key set took:  1176
Obj string key set took:  756
ray string key set took:  1234

Map string key get took:  33
Obj string key get took:  439
ray string key get took:  393

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment