js 优化for循环-Duff装置

AI_Explorer
• 阅读 2501

循环是编程中常见的结构,在javaScript程序中同样随处可见。当我们要处理的数据集很大时,for循环的优化就显得格外重要了。
如下是一个常见的for循环:

for(var i = 0; i < values.length; i++) {
     console.log(values[i])
}

这段代码中每循环一次都要计算一次values的长度,所以我们可以将循环由i递增改为i递减,在这个过程中,将终止条件values.l ength的O(n)调用简化成了O(1)调用,初次优化的代码如下:

for(var i = values.length -1; i >= 0; i--){
  console.log(values[i])
}

我们还可以将循环改为后测试循环,如下:

var i = values.length - 1;
if (i > -1) {
  do{
    console.log(values[i]);
  }while(--i >= 0)
}

以上的代码主要是将终止条件和自减操作符组合成了单个语句,不过使用后测试循环必须要保证要处理的值必须有一个。空数组会导致多余的一次循环。

以上的优化都是基于终止条件和自减操作,循环的次数依然没有变化,如果我们能减少循环的次数,代码运行速度是不是会更快呢。接下来我们介绍下一种叫Duff装置的技术。Duff装置的基本概念是通过计算迭代的次数是否为8的倍数将一个循环展开为一系列语句。请看以下代码:

var iterations = Math.ceil(values.length / 8);
var startAt = values.length % 8;
var i = 0;
do{
  switch(startAt){
    case 0: console.log(values[i++]);
    case 7: console.log(values[i++]);
    case 6: console.log(values[i++]);
    case 5: console.log(values[i++]);
    case 4: console.log(values[i++]);
    case 3: console.log(values[i++]);
    case 2: console.log(values[i++]);
    case 1: console.log(values[i++]);
  }
  startAt = 0;
}while(--iterations > 0)

我们假设循环次数是10次,在普通的for循环中,会进入循环体10次,每一次打印对应的值。而在以上的代码中,我们计算出iterations是2,startAt是2,第一次进入switch语句的时候,会执行两次console语句,startAt重置为0,iterations自减为1,第二次进入switch语句时,就,会执行8次console语句, 然后退出循环。以上只执行了两次循环,打印的次数依然是10次,我们减少了循环的次数和处理终止条件的额外开销,在数据量很大的时候,可以使代码运行的更快。
基于以上代码,Andrew B.King提出了一个更快的Duff装置技术,将do-while循环分成两个单独的循环。代码如下:

var iterations = Math.floor(values.length / 8);
var leftover = values.length % 8;
var i = 0;
if(leftover > 0){
  do{
    console.log(values[i++]);
  }while(--leftover > 0);
}
do{
  console.log(values[i++]);
  console.log(values[i++]);
  console.log(values[i++]);
  console.log(values[i++]);
  console.log(values[i++]);
  console.log(values[i++]);
  console.log(values[i++]);
  console.log(values[i++]);
}while(--iterations > 0)

以上的代码没有switch语句,每次进入第二个do-while循环时,执行8次console即可,这个方法比几乎比原始的Duff装置实现快上40%。针对大数据集使用Duff装置技术可以节省很多时间,但对于小数据集,额外的开销则可能得不偿失。
我们可以将duff装置技术封装成一个类似于forEach的方法,代码如下:

Array.prototype.duffForEach = function (fn) {
  const len = this.length;
  var num = Math.floor(len / 8);
  var leftover = len % 8;
  var i = 0;
  if (leftover > 0) {
    do {
      fn(this[i], i++);
    } while (--leftover > 0);
  }
  if (this.length < 8) {
    return
  }
  do {
    fn(this[i], i++);
    fn(this[i], i++);
    fn(this[i], i++);
    fn(this[i], i++);
    fn(this[i], i++);
    fn(this[i], i++);
    fn(this[i], i++);
    fn(this[i], i++);
  } while (--num > 0);
};

我们可以测试一下:

let arr = [1, 2, 3, 4, 5];
arr.duffForEach((item, index) => {
    console.log(item, index)
})

可以看到,结果不出意料是正常的。
js 优化for循环-Duff装置

点赞
收藏
评论区
推荐文章
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Souleigh ✨ Souleigh ✨
4年前
JavaScript 和 Node.js 中事件循环
1.JavaScript中事件循环可以参考《JavaScript忍者秘籍第二版》第十三章,讲解的很好。JavaScript中事件循环,主要就在理解宏任务和微任务这两种异步任务。宏任务(macrotask):setTimeOut、setInterval、setImmediate、I/O、各种callback、UI渲染、messageCh
Wesley13 Wesley13
4年前
java基础语法循环结构
循环结构是指在程序中需要反复执行某个功能而设置的一种程序结构。Java中主要的循环结构:while循环(适用情况,固定次数循环)do…while循环(适用情况,“当.....”循环)for循环(适用情况,“直到....”循环)while循环while是最基本的循环,它的结构为:whi
Stella981 Stella981
4年前
Python for
原文链接: Pythonforelse语句(https://my.oschina.net/ahaoboy/blog/1836014)当循环正常退出时,包括循环结束和continue时,才会调用else中的语句 当使用break结束循环时,不会执行else中的语句foriinrange(5):print(i
Stella981 Stella981
4年前
Block的循环引用
在ios常见的循环引用中曾经提到过block:!(http://static.oschina.net/uploads/space/2016/0830/112327_c1yY_1463495.png)看看上面最基本的block循环应用,self包含block,block包含了self中的变量val,所以形成了循环应用,编译器给出了循环引用的警告,当
Wesley13 Wesley13
4年前
Java循环结构
Java循环结构for,while和do...while顺序结构的程序语句只能被执行一次。如果您想要同样的操作执行多次,就需要使用循环结构。while循环do...while循环for循环在Java5中引入了一种主要用于数组的增强
Stella981 Stella981
4年前
Shell编程之while&until循环详解
循环语句命令常用于执行一条指令或者一组指令,那么直到条件不在满足时停止,在shell脚本中循环语句常见有whileuntilforselect循环语句。在while循环语句主要用来重复执行一组命令或语句,在企业实际应用中,常用于守护进程持续运行的程序。1、在这么多语句中,while循环有它的语法格式,如下:
小万哥 小万哥
1年前
C++ Break、Continue 和 数组操作详解
CBreak和Continuebreak语句还可以用来跳出循环。在以下示例中,当i等于4时跳出循环:cppfor(inti0;i<10;i)if(i4)break;cout<<i<<"\n";CContinue以下示例跳过了值为4的情况:cpp
AI_Explorer
AI_Explorer
Lv1
天街小雨润如酥,草色遥看近却无。
文章
2
粉丝
0
获赞
0