Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/382.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 在for循环中,为什么在这些数组上使用find()会如此缓慢?_Javascript_Node.js_V8 - Fatal编程技术网

Javascript 在for循环中,为什么在这些数组上使用find()会如此缓慢?

Javascript 在for循环中,为什么在这些数组上使用find()会如此缓慢?,javascript,node.js,v8,Javascript,Node.js,V8,背景:我在使用findIndex函数处理大小数字数组时遇到了这个问题。我在下面给出了一个最低限度的工作示例。我可以避免这个问题,但我只是不明白为什么这个问题首先存在 在node.js(v12.16.3)中,为什么在本例中取消find函数的for循环会导致性能显著提高?(5600毫秒减少到250毫秒) 如果将第二个数组中的值从1e10更改为1e9或更小,或者将第一个数组中的值从1更改为1e10或更大,则不会出现此问题 const nSims=1e8 常量数组=[]; 数组[0]=[1]; 数组[

背景:我在使用findIndex函数处理大小数字数组时遇到了这个问题。我在下面给出了一个最低限度的工作示例。我可以避免这个问题,但我只是不明白为什么这个问题首先存在

在node.js(v12.16.3)中,为什么在本例中取消find函数的for循环会导致性能显著提高?(5600毫秒减少到250毫秒)

如果将第二个数组中的值从1e10更改为1e9或更小,或者将第一个数组中的值从1更改为1e10或更大,则不会出现此问题

const nSims=1e8
常量数组=[];
数组[0]=[1];
数组[1]=[1e10];
控制台时间('a')
对于(变量i=0;ivalue>0);
}
}
console.timeEnd('a')//5600毫秒
控制台时间('b')
对于(变量i=0;ivalue>0);
数组[1]。查找((值)=>value>0);
}
console.timeEnd('b')//250毫秒
注意:也许这不是正确的答案,但它只是一个非常大的注释(我需要代码片段来说明)

这是问题中的示例(对于
a
需要5秒以上,对于
b
需要不到2秒):

const nSims=1e8
常量数组=[];
数组[0]=[1];
数组[1]=[1e10];
控制台时间('a')
对于(变量i=0;ivalue>0);
}
}
console.timeEnd('a')//5600毫秒
控制台时间('b')
对于(变量i=0;ivalue>0);
数组[1]。查找((值)=>value>0);
}
console.timeEnd('b')//250毫秒
V8开发者在这里

“slow case”是调用
数组的真正成本。使用回调函数find
:对于每个元素,
数组的内置实现。find
执行对所提供回调函数的调用。除了完成您要求它完成的这项基本工作之外,实现实际上已经相当优化,包括内置的
Array.find
和提供的回调

fast案例得益于V8中的某些额外优化:如果调用
Array.find
只看到过相同类型的数组(包括内部表示,请参见下文),那么类型反馈收集系统和优化编译器中会有一些特殊处理,以发出它的特殊内联版本,它特别具有以下优点,即它还可以内联提供的回调,专门用于这种类型的数组。正如您在这里看到的,这种优化在适用时提供了巨大的速度提升

[1e9]
[1e10]
是引擎盖下不同类型的数组的原因是
1e9
是一个30位整数,因此V8内部为数组元素选择“小整数”(也称为“smi”,31位有符号整数)表示<但是,code>1e10需要34位,因此V8为数组元素选择双精度(64位浮点)表示。因此,如果同样出现的
Array.find
同时遇到
[1e9]
(或
[1]
)和
[1e10]
,它会决定“我在这里看到了多种类型的数组,内联一种以上的特殊情况可能成本比它的价值高,让我们使用通用版本”。在这种情况下,你可以说这个决定有点过于悲观,但这就是启发式的本质:引擎需要规则来决定做什么,因为它们无法预测代码将来会做什么,它们只需要做一些猜测——这可能是一个好的猜测,也可能是一个不太好的猜测:-)

这与循环本身无关;循环数组列表只是制作相同
数组的一种方法。查找
遇到几种数组类型。您可以使用一个使用不同输入调用的函数,在没有循环的情况下触发到通用路径的回退;或者你可以有一个循环(在其他东西上循环),同时仍然保持在快速路径上


@安东写道:

看来,find方法有一些问题

我不会那样说的。引擎优化
数组并不容易。查找
的程度与手工编写的for循环相同——例如,因为引擎通常无法将用户提供的回调内联到内置函数中。如上所述,V8掌握了足够的技巧,能够在某些情况下实现这种内联,但并不总是如此


这远远不是手写替代内置功能可以实现更快性能的唯一情况;在许多情况下,这是因为内置函数比手写替换函数更通用(即:支持更多奇怪的角落案例)。还有一种情况是,在目标微基准之外,很难(尽管肯定不是不可能)找到这些差异真正重要的案例。

让我们先回顾一下:你到底想在这里做什么?(在实际操作中,哪个版本的节点?因为有很多)可能导致
数组[0]
可以内联,而
数组[j]
cannot…@Mike'Pomax'Kamermans这只是我在处理大数数组和find函数时注意到的一个问题的最起码的工作示例。@Mike'Pomax'Kamermans看起来您误读了示例代码。这不是迭代1e8数组位置。。。它在1个数组位置上迭代(为简单起见)。测试进行了18次,以显示性能差异。实际上,我正在访问一个包含1000个元素的数组,但如果在中添加这些元素,则会使示例比这个最小的工作示例更复杂。@Mike'Pomax'Kamermans谢谢,我不知道类型化数组是件好事。我一定会调查的