浏览器工作原理与实践--性能分析工具:如何分析Performance中的Main指标

news2024/12/24 11:42:44

节我们介绍了如何使用Performance,而且我们还提到了性能指标面板中的Main指标,它详细地记录了渲染主线程上的任务执行记录,通过分析Main指标,我们就能够定位到页面中所存在的性能问题,本节,我们就来介绍如何分析Main指标。

任务 vs 过程

不过在开始之前,我们要讲清楚两个概念,那就是Main指标中的任务和过程,在《消息队列和事件循环:页面是怎么活起来的?》和《任务调度:有了setTimeOut,为什么还要使用rAF?》这两节我们分析过,渲染进程中维护了消息队列,如果通过SetTimeout设置的回调函数,通过鼠标点击的消息事件,都会以任务的形式添加消息队列中,然后任务调度器会按照一定规则从消息队列中取出合适的任务,并让其在渲染主线程上执行。

而我们今天所分析的Main指标就记录渲染主线上所执行的全部任务,以及每个任务的详细执行过程。

你可以打开Chrome的开发者工具,选择Performance标签,然后录制加载阶段任务执行记录,然后关注Main指标,如下图所示:

任务和过程

观察上图,图上方有很多一段一段灰色横条,每个灰色横条就对应了一个任务,灰色长条的长度对应了任务的执行时长。通常,渲染主线程上的任务都是比较复杂的,如果只单纯记录任务执行的时长,那么依然很难定位问题,因此,还需要将任务执行过程中的一些关键的细节记录下来,这些细节就是任务的过程,灰线下面的横条就是一个个过程,同样这些横条的长度就代表这些过程执行的时长。

直观地理解,你可以把任务看成是一个Task函数,在执行Task函数的过程中,它会调用一系列的子函数,这些子函数就是我们所提到的过程。为了让你更好地理解,我们来分析下面这个任务的图形:

单个任务

观察上面这个任务记录的图形,你可以把该图形看成是下面Task函数的执行过程:

function A(){
    A1()
    A2()
}
function Task(){
    A()
    B()
}
Task()

结合代码和上面的图形,我们可以得出以下信息:

  • Task任务会首先调用A过程;

  • 随后A过程又依次调用了A1和A2过程,然后A过程执行完毕;

  • 随后Task任务又执行了B过程;

  • B过程执行结束,Task任务执行完成;

  • 从图中可以看出,A过程执行时间最长,所以在A1过程时,拉长了整个任务的执行时长。

分析页面加载过程

通过以上介绍,相信你已经掌握了如何解读Main指标中的任务了,那么接下来,我们就可以结合Main指标来分析页面的加载过程。我们先来分析一个简单的页面,代码如下所示:

<html>
<head>
    <title>Main</title>
    <style>
        area {
            border: 2px ridge;
        }


        box {
            background-color: rgba(106, 24, 238, 0.26);
            height: 5em;
            margin: 1em;
            width: 5em;
        }
    </style>
</head>


<body>
    <div class="area">
        <div class="box rAF"></div>
    </div>
    <br>
    <script>
        function setNewArea() {
            let el = document.createElement('div')
            el.setAttribute('class', 'area')
            el.innerHTML = '<div class="box rAF"></div>'
            document.body.append(el)
        }
        setNewArea()   
    </script>
</body>
</html>

观察这段代码,我们可以看出,它只是包含了一段CSS样式和一段JavaScript内嵌代码,其中在JavaScript中还执行了DOM操作了,我们就结合这段代码来分析页面的加载流程。

首先生成报告页,再观察报告页中的Main指标,由于阅读实际指标比较费劲,所以我手动绘制了一些关键的任务和其执行过程,如下图所示:

Main指标

通过上面的图形我们可以看出,加载过程主要分为三个阶段,它们分别是:

  1. 导航阶段,该阶段主要是从网络进程接收HTML响应头和HTML响应体。

  2. 解析HTML数据阶段,该阶段主要是将接收到的HTML数据转换为DOM和CSSOM。

  3. 生成可显示的位图阶段,该阶段主要是利用DOM和CSSOM,经过计算布局、生成层树(LayerTree)、生成绘制列表(Paint)、完成合成等操作,生成最终的图片。

那么接下来,我就按照这三个步骤来介绍如何解读Main指标上的数据。

导航阶段

我们先来看导航阶段,不过在分析这个阶段之前,我们简要地回顾下导航流程,大致的流程是这样的:

当你点击了Performance上的重新录制按钮之后,浏览器进程会通知网络进程去请求对应的URL资源;一旦网络进程从服务器接收到URL的响应头,便立即判断该响应头中的content-type字段是否属于text/html类型;如果是,那么浏览器进程会让当前的页面执行退出前的清理操作,比如执行JavaScript中的beforunload事件,清理操作执行结束之后就准备显示新页面了,这包括了解析、布局、合成、显示等一系列操作。

因此,在导航阶段,这些任务实际上是在老页面的渲染主线程上执行的。如果你想要了解导航流程的详细细节,我建议你回顾下《04 | 导航流程:从输入URL到页面展示,这中间发生了什么?》这篇文章,在这篇文中我们有介绍导航流程,而导航阶段和导航流程又有着密切的关联。

回顾了导航流程之后,我们接着来分析第一个阶段的任务图形,为了让你更加清晰观察上图中的导航阶段,我将其放大了,最终效果如下图所示:

请求HTML数据阶段

观察上图,如果你熟悉了导航流程,那么就很容易根据图形分析出这些任务的执行流程了。

具体地讲,当你点击重新加载按钮后,当前的页面会执行上图中的这个任务:

  • 该任务的第一个子过程就是Send request,该过程表示网络请求已被发送。然后该任务进入了等待状态。

  • 接着由网络进程负责下载资源,当接收到响应头的时候,该任务便执行Receive Respone过程,该过程表示接收到HTTP的响应头了。

  • 接着执行DOM事件:pagehide、visibilitychange和unload等事件,如果你注册了这些事件的回调函数,那么这些回调函数会依次在该任务中被调用。

  • 这些事件被处理完成之后,那么接下来就接收HTML数据了,这体现在了Recive Data过程,Recive Data过程表示请求的数据已被接收,如果HTML数据过多,会存在多个 Receive Data 过程。

等到所有的数据都接收完成之后,渲染进程会触发另外一个任务,该任务主要执行Finish load过程,该过程表示网络请求已经完成。

解析HTML数据阶段

好了,导航阶段结束之后,就进入到了解析HTML数据阶段了,这个阶段的主要任务就是通过解析HTML数据、解析CSS数据、执行JavaScript来生成DOM和CSSOM。那么下面我们继续来分析这个阶段的图形,看看它到底是怎么执行的?同样,我也放大了这个阶段的图形,你可以观看下图:

解析HTML数据阶段

观察上图这个图形,我们可以看出,其中一个主要的过程是HTMLParser,顾名思义,这个过程是用来解析HTML文件,解析的就是上个阶段接收到的HTML数据。

  1. 在ParserHTML的过程中,如果解析到了script标签,那么便进入了脚本执行过程,也就是图中的Evalute Script。

  2. 我们知道,要执行一段脚本我们需要首先编译该脚本,于是在Evalute Script过程中,先进入了脚本编译过程,也就是图中的Complie Script。脚本编译好之后,就进入程序执行过程,执行全局代码时,V8会先构造一个anonymous过程,在执行anonymous过程中,会调用setNewArea过程,setNewArea过程中又调用了createElement,由于之后调用了document.append方法,该方法会触发DOM内容的修改,所以又强制执行了ParserHTML过程生成的新的DOM。

  3. DOM生成完成之后,会触发相关的DOM事件,比如典型的DOMContentLoaded,还有readyStateChanged。

DOM生成之后,ParserHTML过程继续计算样式表,也就是Reculate Style,这就是生成CSSOM的过程,关于Reculate Style过程,你可以参考我们在《05 | 渲染流程(上):HTML、CSS和JavaScript,是如何变成页面的?》节的内容,到了这里一个完整的ParserHTML任务就执行结束了。

生成可显示位图阶段

生成了DOM和CSSOM之后,就进入了第三个阶段:生成页面上的位图。通常这需要经历布局(Layout)、分层、绘制、合成等一系列操作,同样,我将第三个阶段的流程也放大了,如下图所示:

生成可显示的位图

结合上图,我们可以发现,在生成完了DOM和CSSOM之后,渲染主线程首先执行了一些DOM事件,诸如readyStateChange、load、pageshow。具体地讲,如果你使用JavaScript监听了这些事件,那么这些监听的函数会被渲染主线程依次调用。

接下来就正式进入显示流程了,大致过程如下所示。

  1. 首先执行布局,这个过程对应图中的Layout。

  2. 然后更新层树(LayerTree),这个过程对应图中的Update LayerTree。

  3. 有了层树之后,就需要为层树中的每一层准备绘制列表了,这个过程就称为Paint。

  4. 准备每层的绘制列表之后,就需要利用绘制列表来生成相应图层的位图了,这个过程对应图中的Composite Layers。

走到了Composite Layers这步,主线程的任务就完成了,接下来主线程会将合成的任务完全教给合成线程来执行,下面是具体的过程,你也可以对照着Composite、Raster和GPU这三个指标来分析,参考下图:

显示流程

结合渲染流水线和上图,我们再来梳理下最终图像是怎么显示出来的。

  1. 首先主线程执行到Composite Layers过程之后,便会将绘制列表等信息提交给合成线程,合成线程的执行记录你可以通过Compositor指标来查看。

  2. 合成线程维护了一个Raster线程池,线程池中的每个线程称为Rasterize,用来执行光栅化操作,对应的任务就是Rasterize Paint。

  3. 当然光栅化操作并不是在Rasterize线程中直接执行的,而是在GPU进程中执行的,因此Rasterize线程需要和GPU线程保持通信。

  4. 然后GPU生成图像,最终这些图层会被提交给浏览器进程,浏览器进程将其合成并最终显示在页面上。

通用分析流程

通过对Main指标的分析,我们把导航流程,解析流程和最终的显示流程都串起来了,通过Main指标的分析,我们对页面的加载过程执行流程又有了新的认识,虽然实际情况比这个复杂,但是万变不离其宗,所有的流程都是围绕这条线来展开的,也就是说,先经历导航阶段,然后经历HTML解析,最后生成最终的页面。

总结

本文主要的目的是让我们学会如何分析Main指标。通过页面加载过程的分析,就能掌握一套标准的分析Main指标的方法,在该方法中,我将加载过程划分为三个阶段:

  1. 导航阶段;

  2. 解析HTML文件阶段;

  3. 生成位图阶段。

在导航流程中,主要是处理响应头的数据,并执行一些老页面退出之前的清理操作。在解析HTML数据阶段,主要是解析HTML数据、解析CSS数据、执行JavaScript来生成DOM和CSSOM。最后在生成最终显示位图的阶段,主要是将生成的DOM和CSSOM合并,这包括了布局(Layout)、分层、绘制、合成等一系列操作。

通过Main指标,我们完整地分析了一个页面从加载到显示的过程,了解这个流程,我们自然就会去分析页面的性能瓶颈,比如你可以通过Main指标来分析JavaScript是否执行时间过久,或者通过Main指标分析代码里面是否存在强制同步布局等操作,分析出来这些原因之后,我们可以有针对性地去优化我们的程序。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何修改支付宝号?日赚300+,纯撸信息差!

最近更新的内容中&#xff0c;很多都是给大家讲到的“信息差”。但是&#xff0c;真正能理解信息差&#xff0c;并且使用信息差赚钱的&#xff0c;有多少&#xff1f; 包括前几天给朋友们分享的软件项目&#xff0c;靠信息差月入3万&#xff0c;直接复制粘贴搞定&#xff01;和…

java可盈保险合同管理系统的设计与实现(springboot+mysql源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的可盈保险合同管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于Spring Boot的…

在【laravel框架】学习中遇到的常见的问题以及解决方法

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

net模块

建立TCP的链接 1 发送消息的服务 2 接收消息 2 建立http的链接让浏览器进行访问 import net from netconst html <h1>TCP</h1>const respinseHeaders [HTTP/1.1 200 OK,Content-Type:text/html,Content-Length: html.length,\r\n,html]const http net.create…

计算机系列之大话原码、补码、反码、移码

5、大话原码、补码、反码、移码 原码 最高位 低位&#xff08;7位二进制数&#xff09;&#xff0c; 最高位 0 表示正数&#xff0c;1表示负数 低位即该数字的二进制数 7 的原码 00000111&#xff0c; -7 的原码 10000111&#xff0c; 0000111 为 7 的二进制数&#xff…

VulnHub靶机 DC-5 打靶 渗透测试详情过程

VulnHub靶机 DC-5 打靶 详细渗透测试过程 目录 VulnHub靶机 DC-5 打靶 详细渗透测试过程一、将靶机导入到虚拟机当中二、渗透流程主机发现端口扫描目录爆破文件包含getshell反弹shell提权 一、将靶机导入到虚拟机当中 靶机地址&#xff1a; https://download.vulnhub.com/dc/…

Linux之C编程入门

目录 第1关&#xff1a;第一个C程序 任务描述 相关知识 编译C程序 编程要求 答案及其步骤&#xff1a; 第2关&#xff1a;Linux编译C程序 任务描述 相关知识 gcc编译器使用方法 编程要求 答案及其步骤&#xff1a; 第3关&#xff1a;Linux之静态库编写 任务描述 相关知识 生成…

【听劝】别盲目备考NPDP,思维导图助你高效学习

还在为如何高效学习NPDP而苦恼吗&#xff1f; 今天给大家分享NPDP认证考试必备的学习资料&#xff1a;思维导图。 &#xff08;内容来自圣略NPDP资深讲师整理&#xff09; 详细梳理了课本内容&#xff0c;保存到手机学习&#xff0c;非常方便&#xff01; 思维导图会陆续更…

web自动化系列-selenium的3种弹框操作(十二)

在进行功能测试时 &#xff0c;经常会遇到出现各种的弹出的提示 &#xff0c;比如删除数据给出提示 、做某个操作时也会弹框给出一些友好提示 &#xff0c;因为这些弹框都是做web操作时的一些常用组件 &#xff0c;所以&#xff0c;selenium就不得不支持这些组件 。 1.弹框介绍…

Python基础04-操作系统中的文件与目录操作

在与操作系统交互时&#xff0c;我们经常需要执行文件和目录的操作。Python提供了丰富的库来帮助我们完成这些任务。以下是一些常见的操作&#xff0c;以及如何使用Python代码来实现它们。 1. 导航文件路径 在不同的操作系统中&#xff0c;文件路径的格式可能不同。Python的o…

WAF攻防-漏洞发现协议代理池GobyAwvsXray

知识点 1、Http/s&Sock5协议 2、Awvs&Xray&Goby代理 3、Proxifier进程代理使用 4、Safedog&BT&Aliyun防护在漏洞发现中&#xff0c;WAF会对三个方向进行过滤拦截&#xff1a; 1、速度频率问题&#xff08;代理池解决&#xff09; 2、工具的指纹被识别&am…

【Leetcode笔记】236.二叉树的最近公共祖先

文章目录 题目要求ACM运行结果 题目要求 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能…

Zabbix监控Oracle归档日志空间

1、oracle查看归档日志空间的sql语句 select sum(PERCENT_SPACE_USED) from v$recovery_area_usage; 2、交互式查看oracle归档日志空间的命令&#xff0c;可以手动执行一下&#xff0c;注意要用oracle用户 sqlplus -S "/ as sysdba" << EOF select sum(PER…

python使用uiautomator2操作真机(vivo Y30)

环境&#xff1a; python3.8.10&#xff0c;vivo Y30(8G,128g)&#xff0c;版本android 10。 之前写过&#xff1a; python使用uiautomator2操作真机&#xff08;荣耀10青春版&#xff09;_python真机操作-CSDN博客 python使用uiautomator2操作真机&#xff08;oppo a9x&a…

CompletableFuture编排异步线程

CompletableFuture 是 Java 8 引入的一种新的 Future&#xff0c;设计目的是为了编写非阻塞的异步代码。 传统异步编程方式 传统异步编程方式获得异步任务值&#xff0c;首先我们得通过future task &#xff0c;然后创建一个实现callable内部类&#xff0c;或者通过lambda的表…

JAVA学习笔记31(IO流)

1.IO流 1.文件流 ​ *文件在程序中是以流的形式来操作的 2.常用文件操作 1.创建文件对象 1.new File(String pathname) //根据路径构建一个File对象 main() {}public void create01() {String filePath "e:\\news1.txt";File filePath new File(filePath);tr…

人人都会给视频换脸_出色的AI换脸软件离线版你可以把视频换上明星脸

网盘下载 简单几步骤: 1、找个人脸照片&#xff0c;正面高清 2、找个视频&#xff0c;最好是单人的视频&#xff0c;或者只有一个女的&#xff0c;这样可以按照条件换脸 3、点击开始&#xff0c;等待完成即可&#xff08;显卡勾选显卡&#xff0c;显卡不行选择CPU&#xff09;…

最新win11配置cuda以及cudnn补丁教程

1、首先使用指令 nvidia-smi 查看电脑支持的**最高cuda**版本&#xff0c;例如&#xff1a;本机 12.2 2、进入CUDA下载cuda安装包 https://developer.nvidia.com/cuda-toolkit-archive 2、点击上方绿色的链接&#xff0c;按照图中序号选择的即可&#xff0c;最后点击下载。 …

【MySQL 数据宝典】【磁盘结构】- 004 redolog 重做日志

一、背景介绍 持久性要求&#xff1a; 对于已提交的事务&#xff0c;即使系统发生崩溃&#xff0c;其对数据库的更改也不能丢失。问题&#xff1a; 在事务提交前将所有修改的页面刷新到磁盘浪费资源。随机IO导致刷新速度慢。 解决方案&#xff1a; 【数据副本】记录事务执行过…

中仕公考:广东省2024高校毕业生‘三支一扶‘开始报名

广东省2024高校毕业生三支一扶于今日4月22日正式开始报名&#xff0c;报名人员请于2024年4月22日9:00-4月26日17:00登录广东省高校毕业生“三支一扶”计划信息管理系统进行报名。