Javascript 性能:While 与 For 循环

IT技术 javascript optimization
2021-02-25 03:51:17

前几天在一次技术面试中,被问到的一个问题是“如何优化 Javascript 代码”?

令我惊讶的是,他告诉我 while 循环通常比 for 循环快。

这是真的吗?如果是,那是为什么?

5个回答

您应该反驳说,负while循环会更快!请参阅:JavaScript 循环性能 - 为什么将迭代器递减到 0 比递增更快

whilevs 中for,这两个来源通过在不同浏览器中运行各种循环并以毫秒为单位比较结果很好地记录了速度现象: https ://blogs.oracle.com/greimer/entry/best_way_to_code_a和: http://www.stoimen .com/blog/2012/01/24/javascript-performance-for-vs-while/

从概念上讲,for循环基本上是一个打包while循环,专门针对递增或递减(根据某种顺序或某种长度在逻辑上进行)。例如,

for (let k = 0; ++k; k < 20) {…}

可以通过使其成为负 while 循环来加速:

var k = 20;
while (--k) {…}

从上面链接中的测量结果可以看出,对于非常大的数字,节省的时间确实加起来。

这篇文章也很有趣:不可思议的-web.com/blog/...
2021-04-17 03:51:17
“可以通过使其成为负 while 循环来加速” - 这取决于程序在运行时使用的解释器类型。当定量逻辑不是我要找的东西时,我只是使用 while 循环。甚至您发送的 SO 问题也在“过去”的一个答案中说。如今,编译器和解释器要聪明得多。
2021-04-25 03:51:17
我使用 得到了一些不好的结果while(--k) {},但实际上我已经比 Chrome 中的 for 循环结果更快了while(k > 0) { -- k; }jsben.ch 中设置一个快速测试以亲自查看。
2021-05-16 03:51:17

虽然这是对速度和效率的微小检测的一个很好的答案,但我不得不离题到@Pointy 的原始声明。

正确的答案应该是担心这样的细节通常毫无意义,因为您在此类优化中投入的任何努力都可能在下次签入 V8 或 SpiderMonkey 时完全浪费掉

由于 Javascript 是客户端确定的,并且最初必须为每个浏览器进行编码以实现完全的跨浏览器兼容性(回到 ECMA 之前甚至更糟),由于显着优化,此时速度差异甚至可能不是合乎逻辑的答案以及在浏览器及其编译器引擎上采用 Javascript。

我们甚至不是在谈论非严格脚本,仅在 GAS 中编写应用程序,因此虽然答案和问题很有趣,但它们很可能在现实世界的应用程序中比有用更琐碎。

要阐述这个主题,您首先需要了解这个主题的最初来源以及编译与解释。让我们简要回顾一下语言的演变历史,然后再回到编译与解释之间。虽然不是必需阅读,但您可以阅读 Compiling vs Interpeting 以获得快速答案,但为了深入理解,我建议您阅读 Compiling vs Interpreting 和 Evolution of Programming(展示它们在今天的应用)。

编译与解释

编译语言编码是一种编程方法,在该方法中,您以编译器可以理解的可编译方式编写代码,当今一些更受认可的语言是 Java、C++ 和 C#。这些语言的编写意图是编译器程序然后将代码转换为目标机器使用的机器代码或字节码。

解释的代码
是处理后的码Ĵ乌斯Ñ Ťime (JIT) 在执行时没有先编译,它跳过这一步,允许更快的编写、调试、添加/更改等。它也永远不会存储脚本的解释以备将来使用,它会重新解释每次调用方法时编写脚本。解释的代码在定义的和预期的程序运行时环境(对于 javascript 通常是浏览器)中运行,一旦被环境解释,然后输出到所需的结果。解释过的脚本从来都不是独立的软件,并且总是希望插入有效的运行时环境以进行解释。这就是脚本不可执行的原因。他们永远不会直接与操作系统通信。如果您查看发生的系统进程,您将永远不会看到正在处理的脚本,

所以用 Javascript 编写一个 hello 脚本意味着浏览器解释代码,定义 hello 是什么,当发生这种情况时,浏览器正在将此代码转换回机器级代码,说我有这个脚本,我的环境想要显示单词 hello 所以然后机器将其处理为脚本的可视化表示。这是一个持续的过程,这就是为什么您在计算机中处理器以及在系统上发生的持续处理动作的原因。没有什么是静止的,无论情况如何,流程都在不断地执行。

编译器
通常将代码编译成已定义的字节码系统或机器代码语言,即现在是代码的静态版本。除非重新编译源代码,否则机器不会重新解释它。这就是为什么您会在编译后看到运行时错误,然后程序员必须在源代码中调试并重新编译。解释器预期的脚本(如 Javascript 或 PHP)只是在运行之前未编译的指令,因此源代码很容易编辑和修复,无需额外的编译步骤,因为编译是实时完成的。

并非所有编译后的代码都生而平等
一个简单的说明方法是视频游戏系统。Playstation 与 Xbox。Xbox系统内置支持.net框架,优化编码开发。C# 将此框架与公共语言运行时结合使用,以便将代码编译为字节码。字节码不是编译代码的严格定义,它是放置在过程中的中间步骤,它允许更快地为程序编写更大规模的代码,然后在运行时执行代码时对其进行解释,你猜对了,Ĵ乌斯ñ Ť IME(JIT)。不同之处在于此代码仅解释一次,一旦编译,除非重新启动,否则程序不会再次重新解释该代码。

解释性脚本语言永远不会编译代码,因此解释性脚本中的函数不断被重新处理,而编译后的字节码的函数被解释一次,并且指令被存储直到程序的运行时停止。好处是字节码可以移植到另一台机器的架构上,前提是你有必要的资源。这就是为什么您必须在系统中安装 .net 以及可能的更新和框架才能使程序正常工作。

Playstation 没有为其机器使用 .net 框架。您将需要使用 C++ 进行编码,C++ 旨在针对特定的系统架构进行编译和组装。代码永远不会被解释,并且需要完全正确才能运行。您永远无法像移动中间语言那样轻松地移动这种类型的语言它专为该机器的架构而设计,绝不会以其他方式解释。

因此,您会看到即使是编译语言也不是编译语言的固有最终版本。编译型语言的严格定义是为了使用而完全编译。解释型语言旨在由程序解释,但也是编程中最便携的语言,因为只需要安装一个理解脚本的程序,但由于不断被解释,它们也使用最多的资源。中间语言(如 Java 和 C#)是这两种语言的混合体,部分编译但也需要外部资源才能仍然正常工作。一旦运行,它们就会再次编译,这是在运行时的一次性解释。

编程的演变

机器代码 编码
的最低形式,该代码在其表示中是严格的二进制(我不会进入三元计算,因为它是基于理论和本次讨论的实际应用)。计算机理解自然值,开/关真/假。这是机器级别的数字代码,不同于下一级,汇编代码。

汇编代码
直接的下一级代码是汇编语言。这是语言被解释为机器使用的第一点。该代码旨在解释助记符、符号和操作数,然后以机器级代码发送到机器。理解这一点很重要,因为当您第一次开始编程时,大多数人会假设它是这个那个,意思是我编译或解释。除了低级机器代码之外的任何编码语言都不是仅编译指令或仅解释指令!!!

我们在“并非所有编译后的代码都生而平等”中对此进行了讨论。汇编语言是这方面的第一个实例。机器代码是机器读取的,而汇编语言是人类可以读取的。随着计算机处理速度更快,通过更好的技术进步,我们的低级语言在本质上开始变得更加简洁,不需要手动实现。汇编语言曾经是高级编码语言,因为它是对机器进行编码的更快方法。它本质上是一种语法语言,一旦组装(编译的最低形式)直接转换为机器语言。汇编器是编译器,但并非所有编译器都是汇编器。

高级编码
高级编码语言是比汇编语言高一级但甚至可能包含更高级别的语言(这将是字节码/中间语言)。这些语言从那里定义的语法结构编译成所需的机器代码、要解释的字节码或前一种方法与允许内联编写汇编的特殊编译器的混合。像它的前身 Assembly 一样的高级编码旨在减少开发人员的工作量,并消除冗余任务中出现关键错误的任何机会,例如构建可执行程序。在当今世界,您很少会看到开发人员在汇编中工作,只是为了大小而处理数据。比开发人员可能遇到的情况更常见,例如在视频游戏机开发中,他们需要加快流程的速度。由于高级编码编译器是寻求简化开发过程的工具,因此它们可能无法 100% 以最有效的方式为该系统架构编译代码。在这种情况下,将编写汇编代码以最大化系统资源。但是你永远不会看到一个人用机器代码编写,除非你遇到一个怪人。

摘要

如果你做到了这一步,恭喜你!你只是一次坐下来听的比我妻子听得更多,关于这些东西,一辈子。OP 的问题是关于 while 与 for 循环的性能。在今天的标准中这是一个有争议的问题的原因有两个。

原因一
解释 Javascript 的日子已经一去不复返了。所有主要浏览器(是的,甚至 Opera 和 Netscape)都使用 Javascript 引擎,该引擎用于在实现脚本之前对其进行编译。JS 开发人员在非调出方法方面讨论的性能调整在查看语言中的本机函数时是过时的研究方法。在成为 DOM 的一部分之前,代码已经为此进行了编译和优化。在该页面启动时不会再次解释它,因为该页面是运行时环境。Javascript 已经真正成为一种中间语言,而不是解释性脚本。它永远不会被称为中间脚本语言的原因是因为 Javascript 永远不会被编译。这是唯一的原因。除此之外它'

原因二 您编写脚本或脚本库需要与网站上的桌面应用程序一样多的处理能力的可能性几乎为零。为什么?因为 Javascript 的创建从来没有打算成为一种包罗万象的语言。它的创建只是为了提供一种中级语言编程方法,允许完成 HTML 和 CSS 未提供的过程,同时减轻需要专用高级编码语言(特别是 Java)的开发难题。

大多数早期的 Web 开发都不支持 CSS 和 JS。直到 1997 年左右,CSS 还不是一个安全的集成,JS 的战斗时间更长。除了 HTML 之外的一切都是网络世界中的一种补充语言。

HTML 专用于作为站点的构建块。您永远不会编写 javascript 来完全构建网站。您最多只能进行 DOM 操作,但会构建一个站点。

你永远不会在 JS 中设计你的网站,因为它不实用。CSS 处理这个过程。

除了临时,您永远不会使用 Javascript 进行存储。你会使用一个数据库。

那么我们还剩下什么呢?越来越只是功能和流程。CSS3 及其未来的迭代将采用 Javascript 的所有样式方法。您已经在动画和伪状态(悬停、活动等)中看到了这一点。

在这一点上,Javascript 中代码优化的唯一有效论据是写得不好的函数、方法和操作,它们可以通过优化用户的公式/代码模式来帮助。只要你学习正确和高效的编码模式 Javascript,在当今时代,它的原生功能并没有损失性能。

你的预测是错误的,在 2015 年我认为大部分都是错误的。2014 年,我在一家绝对是竞争对手的公司工作:主要是因为除了我们的服务器之外,我们还利用客户端机器(通过 javascript)来提供更好的性能和功能。许多用例之一是利用 canvas 和 webgl 进行图像处理和快速更新视觉信息的有效显示。还有node.js?和同构?如果您正在制作一个小型内容显示应用程序,那么我可能会同意您的value判断。
2021-04-20 03:51:17
+1。如果你不相信这个答案,你应该观看这个视频
2021-05-09 03:51:17
@BrianEllis 我敢打赌是你的妻子给了你反对票。哈哈。好读。
2021-05-11 03:51:17
@John 我希望你没有发表评论。让我回去写一篇关于这个主题的文章,因为我觉得信息不够丰富:) 顺便说一句,很棒的视频链接。
2021-05-14 03:51:17
for(var k=0; ++k; k< 20){ ... }

可以通过使其成为负 while 循环来加速:

var k = 20; while(--k){ ... };

更准确的测试是在与while相同的程度上使用for唯一的区别是使用 for 循环提供了更多的描述。如果我们想变得超级疯狂,我们可以放弃整个街区;

var k = 0;
for(;;){doStuff till break}
//or we could do everything
for (var i=1, d=i*2, f=Math.pow(d, i); f < 1E9; i++, d=i*2, f=Math.pow(d,i)){console.log(f)}

无论哪种方式......在v0.10.38的NodeJS我处理的10一个JavaScript循环9在一季度末第二,对于被平均约13%的速度。但这对我未来决定使用哪个循环或我选择在循环中描述的数量没有影响。

> t=Date.now();i=1E9;
> while(i){--i;b=i+1}console.log(Date.now()-t);
292
> t=Date.now();i=1E9;
> while(--i){b=i+1}console.log(Date.now()-t);
285
> t=Date.now();i=1E9;
> for(;i>0;--i){b=i+1}console.log(Date.now()-t);
265
> t=Date.now();i=1E9;
> for(;i>0;){--i;b=i+1}console.log(Date.now()-t);
246 

2016 答案

在 JavaScript 中,反向 for 循环是最快的。对于循环是平凡 比while循环更快。更加注重可读性

这是一些基准标记。

测试了以下循环:

var i,
  len = 100000,
  lenRev = len - 1;

i = 0;
while (i < len) {
    1 + 1;
    i += 1;
}

i = lenRev;
while (-1 < i) {
    1 + 1;
    i -= 1;
}

for (i = 0; i < len; i += 1) {
    1 + 1;
}

for (i = lenRev; - 1 < i; i -= 1) {
    1 + 1;
}
我不认为这是一个完整的比较而不考虑i = len; while (i--)
2021-04-17 03:51:17

2017 答案

jsperf for vs foreach 在 Chrome 59 上

在这里您可以看到Array.forEach,截至撰写之日 (7/31/17),最新版本的 Chrome (59) 已成为最快。您可以在此处找到其他浏览器版本的平均时间:https : //jsperf.com/for-vs-foreach/66

这证明了 ES 引擎优化随时改变更有效的东西。

我的建议是您使用对您的用例更具表现力的任何一种。

随着计算机按照摩尔定律呈指数级增长,相同量级内的性能差异在未来几乎无关紧要。