.forEach与Object.keys().forEach在稀疏数组上的性能 [英] .forEach vs Object.keys().forEach performance on sparse arrays

查看:109
本文介绍了.forEach与Object.keys().forEach在稀疏数组上的性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我错了,请告诉我:array.forEach(callbackFunction)适用于稀疏数组.它不对零和数组长度之间的每个索引执行callbackFunction,而仅对实际上为in数组的键执行.并且(告诉我,如果我错了)那些键正是Object.keys(array)会给我的.因此(告诉我为什么我错了),如果在array本身或Object.keys(array)上调用.forEach方法,则不会有所不同.因此,为什么会有这种性能差异-好像在一种情况下,将执行从零到长度的巨型无意义循环,而在另一种情况下则不会.

显示效果差异的代码段

 function doNothing(){}
CONSOLE = document.getElementById('console');

arr = [];
arr[49888999] = 42;

start = performance.now();
arr.forEach(doNothing);
duration1 = performance.now() - start;

start = performance.now();
Object.keys(arr).forEach(doNothing);
duration2 = performance.now() - start;

CONSOLE.textContent = [duration1, duration2].join('\n'); 

 <pre id='console'></pre> 

该片段显示在两种情况情况下,回调函数仅被一次调用

 console1 = document.getElementById('console1');
console2 = document.getElementById('console2');
function doNothingVerbose1(){
  console1.textContent = 1 + (+console1.textContent);
}
function doNothingVerbose2(){
  console2.textContent = 1 + (+console2.textContent);
}

arr = [];
arr[49888999] = 42;

start = performance.now();
arr.forEach(doNothingVerbose1);
duration1 = performance.now() - start;

start = performance.now();
Object.keys(arr).forEach(doNothingVerbose2);
duration2 = performance.now() - start;

console.log(duration1, duration2); 

 ~~~~~ 1 ~~~~~
<pre id='console1'>0</pre>
~~~~~ 2 ~~~~~
<pre id='console2'>0</pre> 

更新

我刚刚进行了一项测试,以确定上面的arr=[];arr[49888999]=42;是否是实际的稀疏数组,即与arr=new Array(49889000)相比,其内存占用量要少得多.是的,就是这种情况.循环执行数百次,稀疏版本会花费几秒钟,但不会崩溃,但是new Array(50 million)版本会使小提琴崩溃.因此,如果未在引擎中将其存储为普通C ++数组",那么引擎必须拥有"该数组的Object.keys,那么为什么引擎没有有效利用它呢?对于JS引擎必须做的事情,我可能太简单了.说引擎必须拥有" Object.keys是错误的,因为它拥有" 稀疏数组实现以某种方式支持我们的变量arr吗?也许实际上在浏览器/JS引擎上工作的人可以在这里阐明一些信息.

解决方案

,就像 ,因为,在一种情况下,从零到长度的巨大无意义循环将是 被执行,但在其他情况下则不执行.


根据 ECMA文档:

  1. .forEach方法将通过其.length属性循环遍历所有数组元素.
  2. 仅当元素不为空时,才会调用传递给.forEach的回调.

要证明这一点,您可以简单地做到:

 function doNothing(){}
let perf;


console.log('Array with 50 million length and 1 non-empty element:');
const a = [];
a[49999999] = 'a';
console.log('a.length:', a.length);

perf = performance.now();
a.forEach(doNothing);
console.log('a:', performance.now() - perf);
console.log('');


console.log('Array with 0 length:');
const b = [];
b.foo = 'a';
console.log('b.length:', b.length);

perf = performance.now();
b.forEach(doNothing);
console.log('b:', performance.now() - perf);
console.log('');


console.log('Array with 50 million length and 0 non-empty element:');
const c = [];
c.length = 50000000;
console.log('c.length:', c.length);

perf = performance.now();
c.forEach(doNothing);
console.log('c:', performance.now() - perf); 

Tell me if I'm wrong: array.forEach(callbackFunction) is suited for sparse arrays. It executes callbackFunction not for each index between zero and the array length, but only for the keys which are actually in the array. And (tell me if I'm wrong) those keys are exactly what Object.keys(array) will give me. Hence (tell me why I'm wrong) it shouldn't make a difference if the .forEach method is called on array itself or on Object.keys(array). So, why on earth is there this performance difference - as if, in one case, a giant pointless loop from zero to length would be executed, but not in the other case.

Snippet showing performance difference:

function doNothing(){}
CONSOLE = document.getElementById('console');

arr = [];
arr[49888999] = 42;

start = performance.now();
arr.forEach(doNothing);
duration1 = performance.now() - start;

start = performance.now();
Object.keys(arr).forEach(doNothing);
duration2 = performance.now() - start;

CONSOLE.textContent = [duration1, duration2].join('\n');

<pre id='console'></pre>

Snippet showing that the callback function IS CALLED ONLY ONCE in BOTH cases

console1 = document.getElementById('console1');
console2 = document.getElementById('console2');
function doNothingVerbose1(){
  console1.textContent = 1 + (+console1.textContent);
}
function doNothingVerbose2(){
  console2.textContent = 1 + (+console2.textContent);
}

arr = [];
arr[49888999] = 42;

start = performance.now();
arr.forEach(doNothingVerbose1);
duration1 = performance.now() - start;

start = performance.now();
Object.keys(arr).forEach(doNothingVerbose2);
duration2 = performance.now() - start;

console.log(duration1, duration2);

~~~~~ 1 ~~~~~
<pre id='console1'>0</pre>
~~~~~ 2 ~~~~~
<pre id='console2'>0</pre>

UPDATE

I just did a test to find out whether or not the above arr=[];arr[49888999]=42; is an actual sparse array, i.e. has much less memory footprint compared to doing arr=new Array(49889000). And yes, that is the case. Doing this hundreds of times in a loop, the sparse version takes a couple of seconds but doesn't crash, but the new Array(50 million) version crashes the fiddle. So if it's not stored as a 'normal C++ array' in the engine then the engine must "have" Object.keys of the array, so why doesn't the engine make efficient use of it? I might have a too simplistic view of what a JS engine has to do; is it wrong to say that the engine must "have" Object.keys because it "has" a sparse array implementation backing our variable arr in some fashion? Maybe someone actually working on a browser/JS engine can shed some light here.

解决方案

as if because, in one case, a giant pointless loop from zero to length would be is executed, but not in the other case.


According to the ECMA documentation:

  1. The .forEach method will loop through all array elements by its .length property.
  2. The callback passed to .forEach will only be invoked if an element is not empty.

To demonstrate this you can simply do:

function doNothing(){}
let perf;


console.log('Array with 50 million length and 1 non-empty element:');
const a = [];
a[49999999] = 'a';
console.log('a.length:', a.length);

perf = performance.now();
a.forEach(doNothing);
console.log('a:', performance.now() - perf);
console.log('');


console.log('Array with 0 length:');
const b = [];
b.foo = 'a';
console.log('b.length:', b.length);

perf = performance.now();
b.forEach(doNothing);
console.log('b:', performance.now() - perf);
console.log('');


console.log('Array with 50 million length and 0 non-empty element:');
const c = [];
c.length = 50000000;
console.log('c.length:', c.length);

perf = performance.now();
c.forEach(doNothing);
console.log('c:', performance.now() - perf);

这篇关于.forEach与Object.keys().forEach在稀疏数组上的性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆