时间切片编程 timeslice

最近在做思考总结,项目原来在做智能合同的项目关于文档比对导致页面卡断问题,项目组同事给了我一个切片编程的方案很棒,在此做个总结!

需求

源文档和修改后的文档,差异点存在近 5000+ 的差异点需要在两个文档上进行颜色标注,整个标注过程都是在进行 DOM 操作,做完后存在严重的页面卡死问题,甚至等待 5~8分钟才标注完成,甚至卡死!这个问题亟待解决优化!

分析

在事件循环机制中主线程需要全部执行完才会进行页面渲染,5000+ 的 DOM 操作严重阻塞了主线程,所以是不是可以给主线程让出空间,将dom操作任务放进宏任务队列,在下一次渲染之后继续执行DOM操作呢?

思路

1、执行主线程,DOM操作(先进行20次dom操作,剩余的任务放进setTimeout宏任务)
2、页面渲染,显示差异对比的颜色标注(上一次dom操作的结果)
3、执行宏任务中 20个 DOM 操作任务
4、页面渲染
5、重复 3、4

最后是结了这个方案,5000+ 的dom操作还是蛮顺滑的!最后我们讲讲时间切片思想

什么是时间切片

时间切片的核心思想是:如果任务不能在50毫秒内执行完,那么为了不阻塞主线程,这个任务应该让出主线程的控制权,使浏览器可以处理其他任务。让出控制权意味着停止执行当前任务,让浏览器去执行其他任务,随后再回来继续执行没有执行完的任务。

所以时间切片的目的是不阻塞主线程,而实现目的的技术手段是将一个长任务拆分成很多个不超过50ms的小任务分散在宏任务队列中执行。

在这里插入图片描述
上图可以看到主线程中有一个长任务,这个任务会阻塞主线程。使用时间切片将它切割成很多个小任务后,如下图所示。

在这里插入图片描述
可以看到现在的主线程有很多密密麻麻的小任务,我们将它放大后如下图所示。
在这里插入图片描述
可以看到每个小任务中间是有空隙的,代表着任务执行了一小段时间后,将让出主线程的控制权,让浏览器执行其他的任务。

使用时间切片的缺点是,任务运行的总时间变长了,这是因为它每处理完一个小任务后,主线程会空闲出来,并且在下一个小任务开始处理之前有一小段延迟。
但是为了避免卡死浏览器,这种取舍是很有必要的。

如何使用时间切片

目前发现有两种解决方案,不过起主体还是通过让出主线程的控制权
1、setTimeout
2、requestAnimationFrame
3、setTimeout + Generator

方案一:setTimeout

function request() {
  let result = [];
  for (let i = 0; i < 100000; i++) {
    result.push({
      index: i,
      text: ~~(Math.random() * 100000000),
    });
  }
  return Promise.resolve(result);
}

let ul = document.querySelector("#app");

request().then((res) => {
  let total = res.length;
  let once = 20;
  let page = total / once;
  let curIndex = 0;

  function loop(curTotal, curIndex) {

    let pageCount = Math.min(curTotal , once);

    setTimeout(function () {
      let fragement = document.createDocumentFragment();
      for (let i = 0; i < pageCount; i++) {
        let li = document.createElement("li");
        li.innerText =
          "index: " + res[curIndex + i].index + "; text: " + res[curIndex + i].text;
        fragement.appendChild(li);
      }

      ul.appendChild(fragement);
      loop(curTotal - pageCount, curIndex + pageCount);
    });
  }

  loop(total, curIndex);
}

简单聊一下 setTimeout 和闪屏现象
  • setTimeout的执行时间并不是确定的。在JS中,setTimeout任务被放进事件队列中,只有主线程执行完才会去检查事件队列中的任务是否需要执行,因此setTimeout的实际执行时间可能会比其设定的时间晚一些。
  • 刷新频率受屏幕分辨率和屏幕尺寸的影响,因此不同设备的刷新频率可能会不同,而setTimeout只能设置一个固定时间间隔,这个时间不一定和屏幕的刷新时间相同。

以上两种情况都会导致setTimeout的执行步调和屏幕的刷新步调不一致。
在setTimeout中对dom进行操作,必须要等到屏幕下次绘制时才能更新到屏幕上,如果两者步调不一致,就可能导致中间某一帧的操作被跨越过去,而直接更新下一帧的元素,从而导致丢帧现象。

方案二:requestAnimationFrame

与setTimeout相比,requestAnimationFrame最大的优势是由系统来决定回调函数的执行时机。

如果屏幕刷新率是60Hz,那么回调函数就每16.7ms被执行一次,如果刷新率是75Hz,那么这个时间间隔就变成了1000/75=13.3ms,换句话说就是,requestAnimationFrame的步伐跟着系统的刷新步伐走。它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧现象。

function request() {
  let result = [];
  for (let i = 0; i < 100000; i++) {
    result.push({
      index: i,
      text: ~~(Math.random() * 100000000),
    });
  }
  return Promise.resolve(result);
}

let ul = document.querySelector("#app");

request().then((res) => {
  let total = res.length;
  let once = 20;
  let page = total / once;
  let curIndex = 0;

  function loop(curTotal, curIndex) {

    let pageCount = Math.min(curTotal , once);

    requestAnimationFrame(function () {
      let fragement = document.createDocumentFragment();
      for (let i = 0; i < pageCount; i++) {
        let li = document.createElement("li");
        li.innerText =
          "index: " + res[curIndex + i].index + "; text: " + res[curIndex + i].text;
        fragement.appendChild(li);
      }

      ul.appendChild(fragement);
      loop(curTotal - pageCount, curIndex + pageCount);
    });
  }

  loop(total, curIndex);
}

方案三:setTimeout + Generator

function request() {
  let result = [];
  for (let i = 0; i < 100000; i++) {
    result.push({
      index: i,
      text: ~~(Math.random() * 100000000),
    });
  }
  return Promise.resolve(result);
}

let ul = document.querySelector("#app");

request().then((res) => {
  function ts(gen) {
    if (typeof gen === "function") gen = gen();
    if (!gen || typeof gen.next !== "function") return;
    return function next() {
      const res = gen.next();
      if (res.done) return;
      requestAnimationFrame(next);
    };
  }

  let total = res.length;
  let once = 20;
  let page = total / once;
  let curIndex = 0;

  ts(addDom(total))();

  function* addDom(curTotal) {
    while (curTotal > 0) {
      let pageCount = Math.min(curTotal , once);

      let fragement = document.createDocumentFragment();
      for (let i = 0; i < pageCount; i++) {
        let li = document.createElement("li");
        li.innerText =
          "index: " +
          res[curIndex + i].index +
          "; text: " +
          res[curIndex + i].text;
        fragement.appendChild(li);
      }
      ul.appendChild(fragement);
      curIndex = curIndex + pageCount;
      curTotal = curTotal - pageCount;
      yield;
    }
  }

参考:
「前端进阶」高性能渲染十万条数据(时间分片)
时间切片(Time Slicing)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在门控循环单元(Gate Recurrent Unit,GRU)中,timeslice 通常指的是时间步(time step),也就是模型在处理序列数据时的每一个时间点。确定合适的 timeslice 取决于多个因素,包括数据序列的长度、模型的复杂度和需要处理的任务等。 一般来说,如果数据序列较短,例如几十个时间步,那么使用较小的 timeslice(例如 10)可能已经足够了。如果数据序列较长,例如上千个时间步,那么使用较大的 timeslice(例如 50)可能会更好,因为这样可以减少模型的训练时间和计算成本。 此外,需要根据具体任务的要求和模型的表现来调整 timeslice。如果模型在训练时出现了过拟合或者欠拟合,可以考虑调整 timeslice 的大小,或者使用其他的调节方法来优化模型。因此,确定合适的 timeslice 是一个需要根据实际情况和经验进行调整的过程。 ### 回答2: 在操作系统中,timeslice时间片)是指CPU分配给每个进程的时间段。在gate中确定合适的timeslice可以遵循以下步骤: 1. 考虑CPU的性能和负载:首先需要了解CPU的处理能力和当前系统的负载情况。如果CPU性能较高且负载较低,可以设置较短的timeslice;如果CPU性能较低或者负载较高,可以设置较长的timeslice。 2. 考虑系统的实时性要求:如果系统中有实时任务,则需要设置较短的timeslice,以便快速响应实时需求。实时任务通常需要较高的优先级。 3. 考虑进程的类型和特性:不同类型的进程可能需要不同的timeslice。例如,对于交互式进程,建议使用较短的timeslice,以提供较好的响应时间。而对于CPU密集型进程,可以使用较长的timeslice,以充分利用CPU资源。 4. 进行实验和调整:在确定初始timeslice后,可以通过实验和观察来进行调整。观察系统的响应时间、资源利用率和吞吐量等参数,并根据需要对timeslice进行调整。 总而言之,在确定合适的timeslice时,需要综合考虑CPU性能、负载情况、实时性要求和进程特性等因素。通过不断观察和调整,可以找到最适合系统的timeslice设置,以提高系统的性能和效率。 ### 回答3: 在计算机操作中,调度算法是确定合适的时间片的关键。时间片是指CPU分配给每个进程执行的时间段。在gate系统中,确定合适的时间片是基于以下几个方面的考虑。 首先,需要考虑进程的类型。不同类型的进程对于时间片的需求也是不同的。例如,I/O密集型进程往往需要更多的时间来进行数据传输,而CPU密集型进程则需要更多的时间来执行计算操作。因此,对于I/O密集型进程,时间片应该设置得稍微长一些,以便处理I/O操作。而对于CPU密集型进程,时间片可以设置得稍短一些,以支持更多的进程切换。 其次,内存的容量也会影响时间片的设置。如果系统的内存容量较小,那么时间片应该相应设置得较短,以便更多的进程能够得到调度和执行。而如果内存容量较大,时间片可以设置得长一些,以减少频繁的进程切换所带来的开销。 另外,对于实时应用程序,时间片的设置也需要考虑其实时性要求。实时应用程序对于响应时间的要求较高,因此时间片应该设置得短一些,以便能够及时响应外部事件。这样可以保证实时应用程序的稳定性和可靠性。 最后,也需要考虑系统的负载情况。当系统负载较轻时,时间片可以设置得长一些,以提高单个进程的执行效率。而当系统负载较重时,时间片应该相应设置得短一些,以便更快地切换进程,保证系统的响应能力。 综上所述,确定合适的时间片需要考虑进程类型、内存容量、实时性要求和系统负载情况等因素综合考虑。在gate系统中,调度算法会根据这些因素来动态地调整时间片的设置,以保证系统的性能和效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值