Javascript 为什么使用循环从数组的开始到结束进行迭代比从开始到结束和从结束到开始进行迭代要快?

Javascript 为什么使用循环从数组的开始到结束进行迭代比从开始到结束和从结束到开始进行迭代要快?,javascript,arrays,performance,iteration,Javascript,Arrays,Performance,Iteration,给定一个具有.length100的数组,该数组包含在相应索引处具有0到99值的元素,其中要求查找数组中等于n的元素:51 为什么使用循环从数组的开始到结束进行迭代比从开始到结束和从结束到开始进行迭代要快? const arr=Array.from({length:100},(u,i)=>i); 常数n=51; 常数长度=阵列长度; console.time(“从开始迭代”); for(设i=0;i=/===/< | 100 | 200

给定一个具有
.length
100
的数组,该数组包含在相应索引处具有
0
99
值的元素,其中要求查找数组中等于
n
的元素:
51

为什么使用循环从数组的开始到结束进行迭代比从开始到结束和从结束到开始进行迭代要快?
const arr=Array.from({length:100},(u,i)=>i);
常数n=51;
常数长度=阵列长度;
console.time(“从开始迭代”);
for(设i=0;iconsole.timeEnd(“从开始迭代”)答案很明显:

更多的操作需要更多的时间。 在判断代码的速度时,要看它将执行多少操作。只要一步一步数一数。每一条指令都需要一个或多个CPU周期,CPU周期越多,运行时间越长。不同的指令占用不同数量的周期基本上无关紧要——虽然数组查找可能比整数算法成本更高,但它们基本上都需要固定的时间,如果有太多的周期,它将主导我们算法的成本

在您的示例中,有几种不同类型的操作可能需要单独计算:

  • 比较
  • 递增/递减
  • 数组查找
  • 条件跳转
(我们可以更精细一些,比如计算变量的获取和存储操作,但这些几乎不重要——不管怎样,所有操作都在寄存器中——它们的数量基本上与其他操作成线性关系)

现在两个代码都迭代了大约50次-它们在循环中的元素在数组的中间。忽略个别错误,以下是计数:

               |  forwards  | forwards and backwards
---------------+------------+------------------------
>=/===/<       |       100  |                   200
++/--          |        50  |                   100
a[b]           |        50  |                   100
&&/||/if/for   |       100  |                   200
|向前|向前和向后
---------------+------------+------------------------
>=/===/<       |       100  |                   200
++/--          |        50  |                   100
a[b]| 50 | 100
&&/||/如果/对于| 100 | 200
有鉴于此,做两倍的工作需要相当长的时间,这并非出乎意料

我还将回答您评论中的几个问题:

第二次对象查找是否需要额外的时间

是的,每个单独的查找都很重要。这并不是说它们可以一次执行,或者优化为单个查找(如果它们查找相同的索引,这是可以想象的)

每个起点到终点和终点到起点应该有两个单独的循环吗

操作的数量无关紧要,只是它们的顺序

或者,换言之,在数组中查找元素的最快方法是什么

关于顺序没有“最快的”,如果您不知道元素在哪里(并且它们均匀分布),那么您必须尝试每个索引。任何顺序,甚至是随机的,都是一样的。但是请注意,您的代码非常糟糕,因为在未找到元素时,它会查看两次索引——在中间没有停止。

但是,仍然有一些不同的方法可以对这种循环检查进行微观优化

  • let
    var
    慢(还是?),请参阅和。循环体作用域的这种分解和分解(大约50倍)实际上在运行时占主导地位——这就是为什么低效代码的运行速度不完全是原来的两倍
  • 0
    进行比较要比与长度进行比较快一些,这使得向后循环处于优势。看到了吗
  • 一般情况下,请参见:它会随着引擎更新而变化。不要做任何奇怪的事情,编写惯用的代码,这将得到更好的优化

因为您要查找的元素总是粗略地位于数组的中间,您应该期望从数组的开始和结束向内行进的版本要比刚开始时的两倍长。

每次变量更新都需要时间,每次比较都需要时间,而且您所做的是它们的两倍。因为您知道在这个版本中终止循环只需要一到两次迭代,所以您应该推断它将花费大约两倍的CPU时间


这个策略仍然是时间复杂的,因为它只看一次每个项目,当项目接近列表的中心时,它就更糟糕了。如果接近尾声,这种方法将具有更好的预期运行时。例如,尝试在这两种文本中查找项目90。

@Bergi是正确的。更多的操作意味着更多的时间。为什么?更多的CPU时钟周期。 时间实际上是指执行代码所需的时钟周期数。 为了了解其本质,您需要查看机器级代码(如组装级代码)以找到真正的证据。每个CPU(核心?)时钟周期可以执行一条指令,那么您要执行多少条指令

自从为嵌入式应用程序编程Motorola CPU以来,我已经很久没有计算过时钟周期了。如果您的代码花费的时间更长,那么它实际上会生成更大的机器代码指令集,即使循环更短或运行的次数相等

永远不要忘记,您的代码实际上正在编译成CPU将要执行的一组命令(内存指针、指令代码级指针、中断等)。这就是计算机的工作原理,在微控制器层面上,它更容易理解,就像ARM或摩托罗拉处理器一样,但对于我们今天运行的复杂机器来说也是如此

你的代码根本没有按照你写的方式运行(听起来很疯狂吧?)。它的运行方式是编译为作为机器级指令运行(编写编译器没有乐趣)。马蒂玛