-
-
Save jung-kim/83676b2310c7c2a9c3d8 to your computer and use it in GitHub Desktop.
"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 | |
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
*/
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
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
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)] }));
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.
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:
- 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.
- 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
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?