Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript Objects vs Map performance (Chrome, V8, Node JS)

I am trying to understand whether to use JS Object or Map if I need random lookups by string key in large datasets (>1000 objects).

I wrote a simple benchmark http://jsperf.com/javascript-objects-vs-map-performance and the results show that in Chrome (V8) objects outperform maps in around 2 times. However, I checked other browsers and the results were the opposite. Why are they that different in various browsers/engines?

I also wrote a similar test in Node.JS and I can't see similar results (test case 6 took much more than test case 4):

Tests

var now = require("performance-now");  var mapKeyValue = new Map(); var mapStringKeyValue = new Map(); var objectKeyValue = {}; var n = 10000; var testSamples = 100;  var firstRow = 0; var firstRowString = firstRow + "";  var middleRow = Math.floor(n / 2); var middleRowString = middleRow + "";  var lastRow = n - 1; var lastRowString = lastRow + "";  var nonExist = n * 2; var nonExistString = nonExist + "";  function makeid() {   var text = "";   var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";    for (var i = 0; i < 20; i++)     text += possible.charAt(Math.floor(Math.random() * possible.length));    return text; }  for (var i = 0; i < n; i++) {   var value = makeid();   mapKeyValue.set(i, value);   mapStringKeyValue.set(i + "", value);   objectKeyValue[i + ""] = value; }  var t0, t1;  var averages = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];  for (var j = 0; j < testSamples; j++) {   var k = 0;   t0 = now();   mapKeyValue.get(firstRow);   t1 = now();   averages[k++] += (t1 - t0);    t0 = now();   mapStringKeyValue.get(firstRowString);   t1 = now();   averages[k++] += (t1 - t0);    t0 = now();   objectKeyValue[firstRowString];   t1 = now();   averages[k++] += (t1 - t0);     t0 = now();   mapKeyValue.get(middleRow);   t1 = now();   averages[k++] += (t1 - t0);    t0 = now();   mapStringKeyValue.get(middleRowString);   t1 = now();   averages[k++] += (t1 - t0);    t0 = now();   objectKeyValue[middleRowString];   t1 = now();   averages[k++] += (t1 - t0);     t0 = now();   mapKeyValue.get(lastRow);   t1 = now();   averages[k++] += (t1 - t0);     t0 = now();   mapStringKeyValue.get(lastRowString);   t1 = now();   averages[k++] += (t1 - t0);    t0 = now();   objectKeyValue[lastRowString];   t1 = now();   averages[k++] += (t1 - t0);     t0 = now();   mapKeyValue.get(nonExist);   t1 = now();   averages[k++] += (t1 - t0);    t0 = now();   mapStringKeyValue.get(nonExistString);   t1 = now();   averages[k++] += (t1 - t0);    t0 = now();   objectKeyValue[nonExistString];   t1 = now();   averages[k++] += (t1 - t0); }  console.log("Test samples number " + testSamples);  for (var i = 0; i < averages.length; i++) {   averages[i] /= testSamples;   console.log("Test case " + (i + 1) + " took in average " + (averages[i] * 1000000) + " ns"); } 

Results

Test samples number 100 Test case 1 took in average 2050.269999999692 ns Test case 2 took in average 751.2899999997202 ns Test case 3 took in average 567.3000000004081 ns Test case 4 took in average 727.2699999999688 ns Test case 5 took in average 4760.029999999489 ns Test case 6 took in average 1939.3400000004135 ns Test case 7 took in average 673.549999999885 ns Test case 8 took in average 689.3600000002564 ns Test case 9 took in average 541.3700000001143 ns Test case 10 took in average 1146.0599999999843 ns Test case 11 took in average 3096.7699999998285 ns Test case 12 took in average 644.7400000000058 ns 

Let me know if you have any ideas on how to improve the benchmark and make it more accurate. Thank you.

like image 576
Andrew Marin Avatar asked Oct 01 '15 11:10

Andrew Marin


People also ask

Is JavaScript Map faster than object?

Object is the great choice for scenarios when we only need simple structure to store data and knew that all the keys are either strings or integers (or Symbol), because creating plain Object and accessing Object's property with a specific key is much faster than creating a Map (literal vs constructor, direct vs get() ...

Should I use object or Map JavaScript?

An object behaves like a dictionary because JavaScript is dynamically typed, allowing you to add or remove properties at any time. But Map() is much better because it: Provides get , set , has , and delete methods. Accepts any type for the keys instead of just strings.

Why should we stop using objects as maps in JavaScript?

This is because the in operator designates properties in the object's prototype as being part of the object, which we don't really want for dictionaries or maps. To create a pure object with no prototype, we have to write: let obj = Object. create(null);

Is Map faster than for loop JavaScript?

Under these specific circumstances, if you need the benefit of around half a second of performance per-10,000,000 elements in Chrome you might be better off using a for loop for now. However, on other platforms / environments or other circumstances, map might still be faster, and in fact it may be faster in the future.


1 Answers

I just had a similar question and wrote a test case, and the first answer was similar to yours, however we both did not consider that modern JS-engines are very capable at eliminating code that is irrelevant for the result of a function.

That means that your test-case is showing you misleading results, because the JS-engine was able to remove your test-case entirely, therefore you measured how fast the engine can run an empty loop.

I wrote a new test-case that makes sure that the browser has no chance to eliminate the code, and the results shows that maps are almost twice as fast as associate objects: https://jsperf.com/map-vs-object-vs-frozen

performance test results

Please note that this test does not include the cost to actually initialize the Map-object. In reality it is therefore most likely faster to use local objects for small snippets of code, where actual Maps are only faster in a case where you store larger amounts of data in a global context.

It is also interesting to see, that the browser realizes that there are no write-operations on the object and therefore ignores all update checks it would otherwise have to do. Therefore the frozen performance is actually slower, while one would expect it to be faster.

like image 180
TwoThe Avatar answered Oct 08 '22 19:10

TwoThe