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
@AdamWillden
Copy link

Correct me if I'm wrong, but surely you should use the same math.random() for each obj, map, ray when getting and setting? Considering map and array work in insertion order you might get a random number of 0 for one and 20000 for another which then isn't a fair test. You'll get different results every time right?

@geronimo-iia
Copy link

With the same random array :

"use strict";

var indexes = [];
let n = 2000000;
while (n > 0) {
indexes.push(Math.random() * 2000000);
n--;
}

let looper = (callback) => {
let n = 2000000;
while (n > 0) {
callback(indexes[n], 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((key ,index)=> map.set(key, index)));
timer('Obj int key set took: ', () => looper((key ,index) => obj[key] = index));
timer('ray int key set took: ', () => looper((key ,index) => ray[key] = index));
console.log();
timer('Map int key get took: ', () => looper((key ,index) => {let dummylet = map.get(key) }));
timer('Obj int key get took: ', () => looper((key ,index) => {let dummylet = obj[key] }));
timer('ray int key get took: ', () => looper((key ,index) => {let dummylet = ray[key] }));

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

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

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

// // Example output in node v8
/*

Map int key set took: 1689
Obj int key set took: 3661
ray int key set took: 3523

Map int key get took: 1030
Obj int key get took: 2370
ray int key get took: 2411

Map string key set took: 3137
Obj string key set took: 4531
ray string key set took: 3961

Map string key get took: 2426
Obj string key get took: 4263
ray string key get took: 1949
*/

@strattonn
Copy link

strattonn commented Dec 31, 2019

I came here looking to see if Map was efficient, it made the most sense for writing my code but I wanted to know about runtimes. If I looked at your results for more complex objects or strings, I might not choose Map. Now, a couple of years later, in Chrome 79, the results tell me to choose what makes the most sense for the writer. Optimization will always improve and choosing the correct structure when writing the code is the best optimization.

Map int key set took: 887
Obj int key set took: 2698
ray int key set took: 2754

Map int key get took: 33
Obj int key get took: 1379
ray int key get took: 1366

Map string key set took: 1839
Obj string key set took: 2291
ray string key set took: 2510

Map string key get took: 554
Obj string key get took: 1966
ray string key get took: 1302

@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