"火焰图"也可以是源码阅读的利器

2,798 阅读6分钟

什么是火焰图?

火焰图是一种常用的性能分析工具。它展示了程序在时间轴上的执行情况,通过颜色深浅来表示消耗时间的长短。在 Web 开发中,火焰图通常被用来分析前端性能问题,例如卡顿、加载时间过长等等。它可以让我们更加清楚地了解程序中哪些方法消耗了时间,从而定位瓶颈并进行优化。

如何使用火焰图?

这里我们以Chrome浏览器为例,在 Chrome 浏览器中,要生成火焰图需要以下步骤:

  1. 打开一个网页并打开 DevTools(按 F12 或者右键菜单-检查)
  2. 选择顶部菜单中的 Performance 选项卡
  3. 点击左上方的录制按钮开始记录性能数据
  4. 在网页中操作,模拟网页性能瓶颈
  5. 结束性能记录,停止按钮停止录制操作
  6. 通过 Summary 视图查看火焰图

现在我们来进行更详细的可视化讲解。右键检查打开控制面板,选择performance面板选项:

火焰图-3.png

1、Disable JavaScript samples:禁用JavaScript样例,忽略调用栈信息。
2、Enable advanced paint instrumentation (slow):开启加速渲染选项。
3、CPU:控制录制过程中,CPU的工作效率。
4、NetWork:控制录制过程中,网络加载速度。
5、Hardware concurrency:控制并发的。这个是根据浏览器版本来决定是否显示,所以有些人的浏览器里没有,有些人的浏览器里有这个选项。

如何读懂火焰图?

这里我们以React项目为例,使用create-react-app(简称cra)来快速创建项目,我们这里使用react@17版本为例,

index.js文件如下:

function App (){
    return <div>这是函数式组件</div>
}

ReactDOM.render(
    <App/>,
    document.getElementById('root')
)

接下来录制的这个动作以下面的动图为例:

火焰图-1.gif

这里我们主要分析的是“首页渲染”。我们进行的操作是先点击录制按钮,然后再刷新页面。这里可能会有小伙伴提问了,我们从刷新地址到页面渲染 这个过程中,也就花费了3-5s,但是录制时间为什么录制了10s?

这里就涉及了这个火焰图的基本原理,这里先卖个关子,想知道答案的需要继续阅读哟~~

假设我们知道为啥要录制10s,我们来看一下这个录制的结果:

火焰图-4.png

我们可以把这个结果看做是一张表格,表格的标题是时间,表格的每一行都代表着在时间基础上的不同维度,在这里面我们重点关注TimingsMain这2个维度就可以。

火焰图-5.png

首先来看一下Timings,在这个维度上我们看到浏览器为我们标注了5个标签,分别是LCPFPFCPDCLL

LCP:最大内容的渲染。

FP:首次渲染。

FCP:首次有内容的渲染。

DCL:HTML文件加载完成并且解析也完成。

L:页面中所有资源都加载完成。

最后再关注一下Main这个维度,在这个维度里,我们可以看到更多的代码细节,比如可以看到哪个代码块执行的时间较长,在某个时间段程序都干了啥等等。想要知道更多的有关这个维度的事情,请继续往下阅读。

火焰图的基本原理可以说下不?

火焰图的基本原理是将程序的执行时间沿着时间轴展开,并用彩色的“火焰”图形表示不同函数的执行时间。图形中的每一个矩形代表一个函数,它的宽度表示这个函数执行的时间长度,颜色的深浅表示这个函数执行的时间长度所占比例,从而可以很清晰地看出程序中哪些部分占用了较多的时间。

这里来解释一下什么叫做将程序的执行时间沿着时间轴展开

首先,我们需要明确的是,所有的程序都有时间维度。就是当我们运行一个程序时,每一条指令都会在某个时刻执行,在另一个时刻结束。这个过程中,程序的运行时间是逐步推进的。

而火焰图所做的就是将这个运行时间沿着时间轴展开,类似于时间线,从程序开始执行到程序结束的时间范围内,每一时刻都显示程序中正在执行的部分代码的状态。

再具体来说,火焰图将时间轴分成了若干个时间片段,每个时间片段都代表了一个时间段内的执行状态。在每个时间片段内,程序的执行状态会用不同的颜色来表示,而每个颜色代表了一种特定的代码部分

通过观察这个图像,我们可以看出程序在不同时刻的执行状态,以及每种代码部分的相对运行时间和频率。这样就能够清晰地看到代码的执行情况,看出哪些部分的代码运行时间较长,哪些部分的代码执行效率较差等信息。这有助于我们找出程序性能上的瓶颈,并作出优化或改进。

所以这就很好的解释了我们应该如何控制录制的时间?还是以上面的例子来说,从地址栏刷新到页面渲染完毕这个过程也就花3-5s的样子,但是我们却录制了10s,这是因为我们想要看到整个过程的全貌,所以我们只需要保证录制过程能够覆盖程序执行时间就可以,不一定是10s,只要大于5s都可以。

如何用它阅读框架源码?

在这一个章节里,我们来看一下更多的有关Main维度上的东西。在前面的部分我们知道Main维度主要是暴露更多的代码细节,就冲它这一点,我们就可以用它来阅读React源码。我们以ReactDOM.render()这个方法为例进行剖析:

这里有2种方法来看,一种是利用Main主线程维度,另一种是 Timings维度 + Main主线程维度(2者综合起来看)。

第一种方法,选中Main主线程窗口,ctrl+f打开搜索框,输入render,这么做会查出来很多,所以需要你一个一个的确认。

火焰图-6.png

这里还需要说明一点,在火焰图里,同一时刻上的代码阅读顺序一定是从上向下的

火焰图-7.png

第二种方法,我们同时打开TimingsMain这2个维度,Timings里也会暴露出一些有用的信息

火焰图-8.png

在这里它就暴露了这个信息:React Tree Reconciliation: Completed Root。它在调和哎,我们知道React.render是会触发这个操作的,同时打开Main主线程,找到调和的这一个时间轴,我们就能看到render就在这个时间轴的左侧附近。当然,这种方法更偏向技巧性。

最后

好啦,本次分享到这里就结束啦,如果上面的分享有错误或者不足,欢迎大家指正,希望我写的对大家有启发。