Waterfall

通过瀑布流,您可以了解浏览器在运行网站或应用程序时所做的各种事情。它的基础是浏览器在运行网站时所做的事情可以分为各种类型 - 运行 JavaScript,更新布局等等 - 而且在任何时候,浏览器都在做这些事情之一。

因此,如果您看到性能问题的迹象 - 例如帧速下降,您可以前往瀑布流查看浏览器在录制过程中正在进行的操作。

沿着 X 轴是时间。记录的操作(称为标记)显示为水平条,以瀑布流形式显示,以反映浏览器执行的连续性。

选择标记后,您会在右侧的侧边栏中看到更多关于它的信息。这包括标记的持续时间以及特定于标记类型的更多信息

标记

操作标记用颜色标记和标记。记录下列操作:

名称和说明 颜色 详细资料

DOM 事件

为响应 DOM 事件而执行的 JavaScript 代码。

事件类型
例如,“点击”或“消息”。
活动阶段
例如,“目标”或“捕获”。

在页面中执行的 JavaScript 函数标有该函数被调用的原因:

脚本标记
setInterval
setTimeout
requestAnimationFrame
Promise回调
Promise Init
Worker
JavaScript URI
事件处理程序

调用堆栈,并链接到函数。

解析 HTML

花费时间解析页面的 HTML。

调用堆栈,并链接到函数。

解析 XML

花费时间解析页面的 XML。

调用堆栈,并链接到函数。

重新计算样式

计算适用于页面元素的计算样式。

Restyle 提示
指示需要什么样的重新列表的字符串。提示可能是以下任何一种:
自子

LaterSiblings
CSSTransitions
CSSAnimations
SVGAttrAnimations
StyleAttribute
StyleAttribute_Animations
Force
ForceDescendants

布局

计算页面元素的位置和大小。该操作有时称为“回流”。

绘制

将像素绘制到屏幕上。

垃圾回收

垃圾回收事件非增量 GC 事件标记为“(非增量)”。

原因
表示 GC 执行原因的字符串。
非增量原因
如果 GC 事件是非增量的,则该字符串指示执行非增量 GC 的原因。

Firefox 46 中的新增功能:如果GC 事件是由分配压力引起的,则会显示一个链接,标记为“显示分配触发器”。点击链接可查看导致此 GC 事件的分配配置文件。

有关更多详细信息,请参阅分配和垃圾收集

周期回收

回收 C ++ 引用计数的数据结构。

像垃圾回收一样,但是用于C ++对象。请参阅 Kyle Huey 关于周期收集的博客文章

类型
始终“回收”。

CC 图减少

循环回收的准备/预优化。

类型
总是“忘记滑雪”。

控制台

匹配 console.time() 之间的时间间隔console.timeEnd()

计时器名称
参数传递给 console 函数。
在开始堆叠
调用堆栈 console.time() ,并链接到函数。
在最后堆叠
(Firefox 41 中的新功能)。在堆栈调用堆栈console.timeEnd()如果这在a的回调中 Promise,这也会显示“异步堆栈”

时间戳

console.timeStamp()

标签
该论据传递给 timeStamp()

DOMContentLoaded

文档的 DOMContentLoaded 事件。

加载

文档的 load 事件。

主线程中的 worker 事件

主线程向 worker 发送消息或从 worker 接收消息时显示。

之一:

在主线程上序列化数据
主线程正在序列化要发送给工作人员的消息。
反序列化主线程中的数据
主线程反序列化从工作人员收到的消息。

worker 线程中的 worker 事件

当 worker 从主线程收到消息或向主线程发送消息时显示。

之一:

在 worker 中序列化数据
工作人员正在序列化要发送到主线程的消息。
在 worker 中反序列化数据
工作人员正在反序列化从主线程收到的消息。

瀑布流工具中的标记及其颜色与瀑布流概述中的相同,因此可以很容易地从一个到另一个进行关联。

过滤标记

您可以使用工具栏中的按钮控制显示哪些标记

​​​​​

瀑布流模式

您在瀑布流中看到的内容完全取决于您的网站所做的事情:JavaScript 网站会有很多橙色,而视觉动态网站会有很多紫色和绿色。但有一些常见的模式可以提醒您可能出现的性能问题。

渲染瀑布流

您在瀑布流视图中经常会看到的一种模式如下所示:

这是浏览器用于响应某些事件更新页面的基本算法的可视化:

  1. JavaScript 函数调用:某些事件(例如 DOM 事件)会导致页面中的某些JavaScript 运行。JavaScript 改变了页面的一些 DOM 或 CSSOM。
  2. 重新计算样式:如果浏览器认为页面元素的计算样式已更改,则必须重新计算它们。
  3. 布局:接下来,浏览器使用计算的样式来确定元素的位置和几何形状。此操作被标记为“布局”,但有时也称为“回流”。
  4. Paint:最后,浏览器需要重新绘制元素到屏幕上。最后一步不是按照这个顺序显示的:页面可以分成多个图层,这些图层被独立绘制,然后在称为“组合”的过程中合并。

该顺序需要匹配一帧,因为屏幕在完成之前不会更新。人们普遍接受每秒60帧是动画显示平滑的速率。对于每秒60帧的速率,浏览器会以16.7毫秒的时间执行完整的流程。

重要的是响应能力,浏览器​​并不总是执行每一步:

  • CSS 动画 更新页面,而无需运行任何 JavaScript。
  • 并非所有的 CSS 属性更改都会导致重排。修改可以改变对象的几何形状和位置的属性,例如 widthdisplayfont-size,或 top,将引起回流。但是,修改不会改变几何或位置的属性,例如 color 或 opacity 不会。
  • 并非所有 CSS 属性更改都会导致重新绘制。特别是,如果使用该transform 属性为元素设置动画效果,则浏览器将为已转换的元素使用单独的图层,并且在元素移动时甚至不必重绘:元素的新位置在组合中处理。

动画效果的 CSS 属性介绍了如何为不同的CSS属性设置动画进而赋予不同的性能结果,与瀑布流如何能够帮助发出信号。

阻塞 JavaScript

默认情况下,站点的 JavaScript 在与浏览器用于布局更新,重绘,DOM 事件等相同的线程中执行。这意味着长时间运行的 JavaScript 函数可能会导致无响应(无效):动画可能不平滑,或者该网站甚至可能会冻结。

同时使用帧速率工具和瀑布流,很容易看到长时间运行的 JavaScript 引起响应问题的时间。在下面的屏幕截图中,我们放大了导致帧频下降的 JS 函数:

密集的 JavaScript 文章显示了瀑布流如何突出造成长期的 JavaScript 函数响应的问题,以及如何使用异步方法来保持主线程响应。

昂贵的绘制

某些绘画效果(例如 box-shadow)可能很昂贵,尤其是在浏览器必须在每一帧中计算它们的过渡中应用它们时。如果您看到帧频下降,尤其是在图形密集型操作和转换期间,请检查瀑布流是否有长绿色标记。

垃圾回收

瀑布流中的红色标记表示垃圾回收(GC)事件,其中 SpiderMonkey (Firefox 中的 JavaScript 引擎)遍历堆寻找不再可及的内存并随后释放它。GC 与性能相关,因为它在运行时必须暂停 JavaScript 引擎,以便程序暂停并且完全无响应。

为了减少暂停的时间,SpiderMonkey 实现了增量 GC:这意味着它可以以相当小的增量执行垃圾回收,让程序在两者之间运行。但有时候,它需要执行完整的非增量回收,程序必须等待它完成。

在试图避免 GC 事件,尤其是非增量 GC 事件时,明智的做法是不尝试针对JavaScript 引擎的特定实现进行优化。SpiderMonkey 使用一套复杂的启发式方法来确定何时需要 GC,何时需要非增量 GC。一般来说,虽然:

  • GC 在分配大量内存时需要
  • 当内存分配速率足够高以至于 SpiderMonkey 在增量GC期间可能会耗尽内存时,通常需要非增量GC

当瀑布流记录 GC 标记时,它表示:

  • GC 是否是增量式的
  • GC 执行的原因
  • 如果 GC 是非递增的,则它是非递增的原因
  • 从 Firefox 46 开始,如果 GC 事件是由分配压力引起的,则会显示一个链接,标记为“显示分配触发器”。点击链接可查看导致此 GC 事件的分配配置文件。有关更多详细信息,请参阅分配和垃圾回收

使用控制台 API 添加标记

两个标记直接由控制台 API 调用控制:“控制台”和“时间戳”。

控制台标记

这些使您可以标记录制的特定部分。

要制作控制台标记,请在该部分的开始处调用console.time() 结尾处调用console.timeEnd()这些函数带有一个用于命名该部分的参数。

例如,假设我们有这样的代码:

var iterations = 70;
var multiplier = 1000000000;

function calculatePrimes() {

  console.time("calculating...");

  var primes = [];
  for (var i = 0; i < iterations; i++) {
    var candidate = i * (multiplier * Math.random());
    var isPrime = true;
    for (var c = 2; c <= Math.sqrt(candidate); ++c) {
      if (candidate % c === 0) {
          // not prime
          isPrime = false;
          break;
       }
    }
    if (isPrime) {
      primes.push(candidate);
    }
  }

  console.timeEnd("calculating...");

  return primes;
}

瀑布流的输出将如下所示:

标记用您传递给的参数 console.time() 进行标记,当您选择标记时,可以在右侧边栏中看到程序堆栈。

异步堆栈

Firefox 中的新功能41。

从 Firefox 41 开始,右侧侧边栏也会在结尾显示堆栈:即在 console.timeEnd()被调用时。如果 console.timeEnd() 从 a 的解析中被调用 Promise,它也会显示“(Async:Promise)”​​,在这个下面它将显示“异步堆栈”:也就是说,在承诺的地方调用堆栈。

例如,考虑这样的代码:

var timerButton = document.getElementById("timer");
timerButton.addEventListener("click", handleClick, false);

function handleClick() {
  console.time("timer");
  runTimer(1000).then(timerFinished);
}

function timerFinished() {
  console.timeEnd("timer");
  console.log("ready!");
}

function runTimer(t) {
  return new Promise(function(resolve) {
    setTimeout(resolve, t);
  });
}

瀑布流将显示一个标志物之间的期间 time() 和 timeEnd(),如果你选择它,你会看到侧边栏的异步栈:

时间戳标记

时间戳使您可以在录制中标记一个瞬间。

要制作时间戳记标记,请调用 console.timeStamp()您可以传递参数来标记时间戳。

例如,假设我们调整上面的代码以每循环 10 次迭代一次时间戳,并用迭代号标记:

var iterations = 70;
var multiplier = 1000000000;

function calculatePrimes() {
  console.time("calculating...");

  var primes = [];
  for (var i = 0; i < iterations; i++) {

    if (i % 10 == 0) {
      console.timeStamp(i.toString());
    }
    
    var candidate = i * (multiplier * Math.random());
    var isPrime = true;
    for (var c = 2; c <= Math.sqrt(candidate); ++c) {
      if (candidate % c === 0) {
          // not prime
          isPrime = false;
          break;
       }
    }
    if (isPrime) {
      primes.push(candidate);
    }
  }
  console.timeEnd("calculating...");
  return primes;
}

在瀑布现在你会看到这样的东西: