React18源码: React调度中的3种优先级类型和Lane的位运算

优先级类型

  • React内部对于优先级的管理,贯穿运作流程的4个阶段(从输入到输出),根据其功能的不同,可以分为3种类型:
    • 1 )fiber优先级(LanePriority)
      • 位于 react-reconciler包,也就是Lane(车道模型)
    • 2 )调度优先级(SchedulerPriority)
      • 位于scheduler包
    • 3 )优先级等级(ReactPriorityLevel)
      • 位于react-reconciler包中的 SchedulerWithReactIntegration.js
      • 负责上述2套优先级体系的转换.
  • Lane 是在 react@17.0.0的新特性.

Lane(车道模型)

  • 英文单词lane翻译成中文表示"车道,航道"的意思,所以很多文章都将Lanes模型称为车道模型

  • Lane模型的源码在 ReactFiberLane.js,源码中大量使用了位运算

  • 首先引入对Lane的解释, 这里简单概括如下:

    • 1 )Lane类型被定义为二进制变量,利用了位掩码的特性,在频繁运算的时候占用内存少,计算速度快.
      • Lane和Lanes就是单数和复数的关系,代表单个任务的定义为Lane,代表多个任务的定义为Lanes
    • 2 )Lane是对于expirationTime的重构,以前使用expirationTime表示的字段,都改为了lane
      renderExpirationTime -> renderlanes
      update.expirationTime -> update.lane
      fiber.expirationTime -> fiber.lanes
      fiber.childExpirationTime -> fiber.childLanes
      root.firstPendingTime and root.lastPendingTime -> fiber.pendingLanes
      
    • 3 )使用Lanes模型相比expirationTime模型的优势
      • Lanes把任务优先级从批量任务中分离出来
      • 可以更方便的判断单个任务与批量任务的优先级是否重叠
      // 判断:单task与batchTask的优先级是否重叠
      // 1.通过expirationTime判断
      const isTaskIncludedInBatch = priorityOfTask >= priorityOfBatch;
      // 2.通过Lanes判断
      const isTaskIncludedInBatch =(task & batchOfTasks) !== 0;
      
      // 当同时处理一组任务,该组内有多个任务,且每个任务的优先级不一致
      // 1. 如果通过expirationTime判断,需要维护一个范围(在Lane重构之前,源码中就是这样比较的)
      const isTaskIncludedInBatch =
        taskPriority <= highestPriorityInRange &&
        taskPriority >= lowestPriorityInRange;
      // 2. 通过Lanes判断
      const isTaskIncludedInBatch = (task & batchOfTasks) !== 0;
      
  • Lanes使用单个32位二进制变量即可代表多个不同的任务

  • 也就是说一个变量即可代表一个组(group)

  • 如果要在一个group中分离出单个task,非常容易

  • 在expirationTime模型设计之初,react体系中还没有 Suspense 异步渲染的概念

  • 现在有如下场景:有3个任务,其优先级A>B>C,正常来讲只需要按照优先级顺序执行就可以了

  • 但是现在情况变了:A和C任务是CPU密集型,而B是IO密集型(Suspense会调用远程api,算是IO任务)

  • 即A(cpu)>B(IO)>C(cpu).此时的需求需要将任务B从group中分离出来,先处理cpu任务A和C

    //从group中删除或增加task
    
    // 通过expirationTime实现
    // 维护一个链表,按照单个task的优先级顺序进行插入
    // 删除单个task(从链表中删除一个元素)
    task. prev.next = task.next;
    //2)增加单个task(需要对比当前task的优先级,插入到链表正确的位置上)
    let current = queue;
    while (task.expirationTime >= current.expirationTime) {
      current = current.next;
    }
    task.next = current.next;
    current.next = task;
    //3)比较task是否在group中
    const isTaskIncludedInBatch =
      taskPriority <= highestPriorityInRange &&
      taskPriority >= lowestPriorityInRang;
    //2.通过Lanes实现
    //1)删除单个task
    batchOfTasks &= ~task;
    //2)增加单个task
    batchOfTasks |= task;
    //3)比较task是否在group中
    const isTaskIncludedInBatch = (task & batchOfTasks) !== 0;
    
  • Lanes是一个不透明的类型,只能在ReactFiberLane.js这个模块中维护

  • 如果要在其他文件中使用,只能通过 ReactFiberLane.js 中提供的工具函数来使用

  • 分析车道模型的源码(ReactFiberLane.js中),可以得到如下结论:

    • 1.可以使用的比特位一共有31位
    • 2.共定义了18种车道(Lane/Lanes)变量,每一个变量占有1个或多个比特位,分别定义为Lane和Lanes类型.
    • 3.每一种车道(Lane/Lanes)都有对应的优先级,所以源码中定义了18种优先级(LanePriority).
    • 4.占有低位比特位的Lane变量对应的优先级越高
      • 最高优先级为 SynclanePriority 对应的车道为
        • Synclane = 0b0000000000000000000000000000001
      • 最低优先级为 OffscreenLanePriority 对应的车道为
        • OffscreenLane = 0b1000000000000000000000000000000

位运算

  • 什么是位运算?程序中的所有数在计算机内存中都是以二进制的形式储存的。

  • 位运算就是直接对整数在内存中的二进制位进行操作。

  • 比如

    • 0 在二进制中用 0 表示,我们用 0000 代表;
    • 1 在二进制中用 1 表示,我们用 0001 代表;
  • 那么先看两个位运算符号 & 和 |

    • & 对于每一个比特位,两个操作数都为 1 时,结果为 1,否则为 0
    • | 对于每一个比特位,两个操作数都为0时,结果为 0,否则为 1
    • 我们看一下两个 1 & 0 和 1 | 0
    • 如上 1 & 0 = 0, 1 | 0 = 1
  • 参考

      0       0000
      1       0001
      -------------
      0 & 1 = 0000 = 0
    
  • 再参考

    0       0000
    1       0001
    ----------------
    0 | 1 = 0001 = 1
    

使用一张表格详细说明

运算符用法描述
与 &a & b如果两位都是1则设置每位为1
或 | a | b 如果两位之一为1则设置每位为1
异或 ^a^b如果两位只有一位为1则设置每位为1
非 ~~ a反转操作数的比特位, 即0变成1, 1变成0
左移(<<)a << b 将a的二进制形式向左移b(< 32)比特位, 右边用0填充
有符号右移(>>)a>>b将a的二进制形式向右移b(<32)比特位,丢弃被移除的位,左侧以最高位来填充
无符号右移(>>>)a>>>b将a的二进制形式向右移b(<32)比特位,丢弃被移除的位,并用0在左侧填充

位运算的一个使用场景

  • 比如有一个场景下,会有很多状态常量A,B,C…,这些状态在整个应用中在一些关键节点中做流程控制
  • 比如:
    if(value === A) {
      // TODO..
    }
    
  • 如上判断value等于常量A,那么进入到if的条件语句中
  • 此时是value属性是简单的一对一关系,但是实际场景下value可能是好几个枚举常量的集合
  • 也就是一对多的关系,那么此时value可能同时代表A和B两个属性
  • 如下图所示:
  • 这时候,如果按照下面的代码来写,就会很麻烦
    if(value === A || value === B) {
      // TODO..
    }
    
  • 此时的问题就是如何用一个value表示A和B两个属性的集合,这个时候位运算就派上用场了
  • 因为可以把一些状态常量用32位的二进制来表示(这里也可以用其他进制),比如:
    const A = 0b0000000000000000000000000000001 // 优先级最高
    const B = 0b0000000000000000000000000000010
    const C = 0b0000000000000000000000000000100 // 优先级最低
    
  • 通过移位的方式让每一个常量都单独占一位,这样在判断一个属性是否包含常量的时候
  • 可以根据当前位数的1和0来判断
  • 这样如果一个值即代表A又代表B那么就可以通过位运算的 | 来处理
  • 就有 AB = A | B = 0b0000000000000000000000000000011
  • 那么如果把AB的值赋予给value,那么此时的value就可以用来代表A和B
  • 此时当然不能直接通过等于或者恒等来判断value是否为A或者B,此时就可以通过&来判断。具体实现如下:
    const A = 0b0000000000000000000000000000001
    const B = 0b0000000000000000000000000000010
    const C = 0b0000000000000000000000000000100
    const N = 0b0000000000000000000000000000000
    const value = A | B
    console.log((value & A ) !== N) // true
    console.log((value & B ) !== N) // true
    console.log((value & C ) !== N ) // false
    

位运算在 react 中的应用

export const NoLanes = /*                */ 0b0000000000000000000000000000000;
const SyncLane = /*                      */ 0b0000000000000000000000000000001;

const InputContinuousHydrationLane = /*  */ 0b0000000000000000000000000000010;
const InputContinuousLane = /*           */ 0b0000000000000000000000000000100;

const DefaultHydrationLane = /*          */ 0b0000000000000000000000000001000;
const DefaultLane = /*                   */ 0b0000000000000000000000000010000;

const TransitionHydrationLane = /*       */ 0b0000000000000000000000000100000;
const TransitionLane = /*                */ 0b0000000000000000000000001000000;
  • 如上 SyncLane 代表的数值是1,它却是最高的优先级

  • 也即是说lane的代表的数值越小,此次更新的优先级就越大

  • 在新版本的React中,还有一个新特性,就是render阶段可能被中断,在这个期间会产生一个更高优先级的任务

  • 那么会再次更新lane属性,这样多个更新就会合并,这样一个lane可能需要表现出多个更新优先级

  • 我们来看一下React是如何通过位运算分离出优先级的

    function getHighestPriorityLane(lanes) {
      return lanes & -lanes;
    }
    
  • 如上就是通过 lanes & -lanes 分离出最高优先级的任务的,我们来看一下具体的流程

    • 比如SyncLane和InputContinuousLane合并之后的任务优先级lane为
    • SyncLane = 0b0000000000000000000000000000001
    • InputContinuousLane = 0b0000000000000000000000000000100
    • lane = SyncLane|InputContinuousLane
    • lane = 0b0000000000000000000000000000101
    • 那么通过 lanes & -lanes 分离出 SyncLane
    • 首先我们看一下 -lanes,在二进制中需要用补码表示为:
    • -lane = 0b1111111111111111111111111111011
    • 那么接下来执行 lanes & -lanes 看一下,& 的逻辑是如果两位都是1则设置改位为1,否则为0
    • 那么 lane & -lane, 只有一位(最后一位)全是1,所有合并后的内容为:
    • lane & -lane = 0b0000000000000000000000000000001
  • 可以看得出来lane&-lane的结果是SyncLane,所以通过lane&-lane就能分离出最高优先级的任务

    const SyncLane = 0b0000000000000000000000000000001
    const InputContinuousLane = 0b0000000000000000000000000000100
    const lane = SyncLane | InputContinuousLane
    console.log((lane & -lane) === SyncLane ) // true
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/405866.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Java SSM框架实现网络作业提交与批改系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现网络作业提交与批改系统演示 摘要 随着互联网时代的到来&#xff0c;同时计算机网络技术高速发展&#xff0c;网络管理运用也变得越来越广泛。因此&#xff0c;建立一个B/S结构的网络作业提交与批改系统&#xff0c;会使网络作业提交与批改系统工作系统化…

JavaScript字符串的常用方法(非常详细)

文章目录 一、操作方法增concat 删改trim()、trimLeft()、trimRight()repeat()padEnd() toLowerCase()、 toUpperCase()查charAt()indexOf()startWith()、includes() 二、转换方法split 三、模板匹配方法match()search()replace() 一、操作方法 我们也可将字符串常用的操作方法…

c编译器学习07:minilisp编译器改造(debug模式支持调试)

问题 原版的minilisp编译器不支持argv输入测试&#xff0c;不方便单步调试。 代码改造目标是既不改变原有程序的各种功能&#xff0c; 又能支持个人习惯的vs单步debug模式。 CMakeLists.txt变更 定义DEBUG宏 解决单步调试源码定位偏差问题 cmake_minimum_required(VERSION …

Puppeteer 使用实战:如何将自己的 CSDN 专栏文章导出并用于 Hexo 博客(二)

文章目录 上一篇效果演示Puppeteer 修改浏览器的默认下载位置控制并发数错误重试并发控制 错误重试源码 上一篇 Puppeteer 使用实战&#xff1a;如何将自己的 CSDN 专栏文章导出并用于 Hexo 博客&#xff08;一&#xff09; 效果演示 上一篇实现了一些基本功能&#xff0c;…

8.qt5使用opencv的库函数打开图片

1.配置opencv动态库的环境变量 2.在创建的qt工程中加入如下opencv代码&#xff0c;具体代码如下&#xff1a; 使用opencv库函数显示图片

Mamba详细介绍和RNN、Transformer的架构可视化对比

Transformer体系结构已经成为大型语言模型(llm)成功的主要组成部分。为了进一步改进llm&#xff0c;人们正在研发可能优于Transformer体系结构的新体系结构。其中一种方法是Mamba&#xff08;一种状态空间模型&#xff09;。 Mamba: Linear-Time Sequence Modeling with Select…

JVM面试题(1)

1.说一下jvm的主要组成部分&#xff0c;以及作用 类加载器&#xff08;ClassLoader&#xff09;&#xff1a;将java代码转换成字节码 运行时数据区&#xff08;Runtime Data Area&#xff09;&#xff1a;将字节码加载到内存中 执行引擎&#xff08;Execution Engine&#x…

【Flink数据传输(一)】NetworkStack架构概述:实现tm之间的数据交换

文章目录 1. NetworkStack整体架构2. StreamTask内数据流转过程 NetworkStack提供了高效的网络I/O和反压控制 除了各个组件之间进行RPC通信之外&#xff0c;在Flink集群中TaskManager和TaskManager节点之间也会发生数据交换&#xff0c;尤其当用户提交的作业涉及Task实例运行在…

个人博客系列-环境配置-gitee(2)

注册gitee账户 地址&#xff1a;https://gitee.com/ 此步骤省略 新建仓库 执行以下命令 即可 拉取代码 创建目录 mkdir myCode && cd myCode 登录gitee找到项目&#xff0c;点击克隆&#xff0c;拉取代码 连接远程仓库命令 git remote add origin 仓库地址http…

PCIe 5.0 Layout Guide笔记

一、松耦合和紧耦合 松耦合优点是相同走线宽度下电介质更薄,同时对线间距的变化不敏感,提供了更好的阻抗控制;松耦合缺点是需要更大的区域进行绕线;紧耦合优点是更高的布线密度,相同阻抗下走线可以更细,同时具有更好的共模噪声抑制;紧耦合缺点是阻抗随线间距的变化大;【…

单片机02_寄存器_GPIO设置__点灯

芯片概述 C51&#xff1a;0口、1口、2口、3口&#xff0c;P00~p07、P10~P17、P20~P27、P30~P37 STM32&#xff1a;A口、B口、C口、D口&#xff0c;PA0~PA15/PA5 GPIOA.5 STM32F407ZGT6有7组GPIO端口&#xff0c;分别是&#xff1a;A B C D E F G&#xff0c;每组均有16个GPIO端…

Linux Android USB gadget(从设备驱动)

Linux Android USB gadget 一:Linux usb gadget 与 Android Composite Gadget二:原生方式和Android方式如何配置函数调用逻辑内核配置原生驱动android驱动三:mass_storage配置虚拟化U盘四:遍历usb设备五:adb usb判断usb设备为adb获取adb配置信息adb设备序列号发送与接收《Linux…

vue中使用echarts绘制双Y轴图表时,刻度没有对齐的两种解决方法

文章目录 1、原因2、思路3、解决方法3.1、使用alignTicks解决3.2、结合min和max属性去配置interval属性1、首先固定两边的分隔的段数。2、结合min和max属性去配置interval。 1、原因 刻度在显示时&#xff0c;分割段数不一样&#xff0c;导致左右的刻度线不一致&#xff0c;不…

解决Maven爆红以及解决 Idea 卡在 Resolving问题

关于 Idea 卡在 Resolving&#xff08;前提是Maven的setting.xml中配置好了阿里云和仓库&#xff09; 参考文章https://blog.csdn.net/jiangyu1013/article/details/95042611 解决Maven爆红参考文章https://devpress.csdn.net/beijing/656d993b76f0791b6eca7bb0.html?dp_toke…

可在线免费使用的5款ChatGPT平替网站!

可在线免费使用的5款ChatGPT平替网站&#xff01; 渗透智能 ShirtAI 是一款全方位AI产品&#xff0c;集成问答绘画导图等功能!支持联网功能、 支持上下文对话、支持模糊匹配自定义回复消息、 支持注册配置自定义赠送额度、支持生成专属邀请码邀请用户双方共同获得额度。 https…

【深蓝学院】移动机器人运动规划--第6章 模型预测控制(MPC)与运动规划--笔记

0. Outline 1. Reactive Control&#xff08;反应式控制&#xff09; 控制学中的 “Reactive Control” 通常指的是一种控制策略&#xff0c;它依赖于系统对特定事件或变化的即时反应&#xff0c;而不是按照预定的计划或策略行动。这种控制往往是基于当前的传感器输入来做出决…

ARM服务器部署Kafka集群

安装前必备的条件是: (1)安装jdk(提供环境); (2)安装zookeeper(注册kafka信息); 需要这方面信息的可以查看我之前写的文档; 一.下载安装包 Kafka官网下载地址 Apache Kafka 根据自己需要下载相应的版本 目前最新的版本是3.6.1。 二.解压安装包 服务器上传下载好的kafk…

紫光同创初使用

芯片PGC2KG-6LPG144 1、安装好软件接&#xff0c;加载license,有两个&#xff0c;与电脑MAC地址绑定的 2、正常使用后&#xff0c;新建个工程&#xff0c;配置管脚Tools→UCE 3、程序中有些信号被软件认为是时钟信号&#xff0c;会报错&#xff08;时钟输入I0约束在非专用时钟…

【Python如何求出水仙花数】

1、求水仙花数Python代码如下&#xff1a; # 求水仙花数&#xff1a;只需要个十百位的3次幂之和与原数相等 for i in range(100, 1000): # 循环100-999整数i1 i % 10 # 取个位 “%”表示除以后取余数i2 i // 10 % 10 # 取十位i3 i // 100 # 取百位 “//”表示除以后取整…

使用 yarn 的时候,遇到 Error [ERR_REQUIRE_ESM]: require() of ES Module 怎么解决?

晚上回到家&#xff0c;我打开自己的项目&#xff0c;执行&#xff1a; cd HexoPress git pull --rebase yarn install yarn dev拉取在公司 push 的代码&#xff0c;然后更新依赖&#xff0c;最后开始今晚的开发时候&#xff0c;意外发生了&#xff0c;竟然报错了&#xff0c;…