Javascript 对象与地图性能(Chrome、V8、Node JS)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32886522/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 15:52:13  来源:igfitidea点击:

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

javascriptobjectmaps

提问by Andrew Marin

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).

如果我需要在大型数据集(> 1000 个对象)中按字符串键进行随机查找,我试图了解是使用 JS 对象还是 Map。

I wrote a simple benchmark http://jsperf.com/javascript-objects-vs-map-performanceand 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?

我写了一个简单的基准测试http://jsperf.com/javascript-objects-vs-map-performance并且结果显示在 Chrome (V8) 中对象的性能比地图高出大约 2 倍。但是,我检查了其他浏览器,结果相反。为什么它们在各种浏览器/引擎中如此不同?

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):

我也在 Node.JS 中写了一个类似的测试,但我看不到类似的结果(测试用例 6 比测试用例 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.

如果您对如何改进基准测试并使其更准确有任何想法,请告诉我。谢谢你。

回答by Seth Holladay

Your longest test case took less than 0.005 milliseconds. The computer is eating it for breakfast. You are in territory where microscopic fluctuations in cold boot time will drastically change the outcome as a relative percentage. That makes these numbers less meaningful.

您最长的测试用例不到 0.005 毫秒。电脑正在吃早餐。您所处的领域是冷启动时间的微观波动会以相对百分比的形式极大地改变结果。这使得这些数字的意义不大。

I would suggest optimizing for readability and other concerns. Make It Work, Make It Right, then Make It Fast. Remember that Map is new and will get faster as the engines evolve.

我建议优化可读性和其他问题。让它工作,让它正确,然后让它快速。请记住,Map 是新的,并且会随着引擎的发展而变得更快。

Here are some resources related to micro-benchmarks, pitfalls and ways to improve them.

以下是一些与微基准、陷阱和改进方法相关的资源。

回答by TwoThe

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.

我刚刚有一个类似的问题并写了一个测试用例,第一个答案与您的相似,但是我们都认为现代 JS 引擎非常有能力消除与函数结果无关的代码。

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.

这意味着您的测试用例向您展示了误导性的结果,因为 JS 引擎能够完全删除您的测试用例,因此您测量了引擎运行空循环的速度。

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

我写了一个新的测试用例,确保浏览器没有机会消除代码,结果显示地图几乎是关联对象的两倍:https: //jsperf.com/map-vs-object- vs 冷冻

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.

请注意,此测试不包括实际初始化 Map 对象的成本。实际上,因此对于小代码片段使用本地对象最有可能更快,而实际的 Maps 只有在您在全局上下文中存储大量数据的情况下才会更快。

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.

有趣的是,浏览器意识到对象上没有写操作,因此忽略了它本来必须执行的所有更新检查。因此,冻结的性能实际上更慢,而人们期望它更快。