目 录CONTENT

文章目录

使用 Chrome DevTools 的 Performance 工具 进行性能分析

Hello!你好!我是村望~!
2024-12-24 / 0 评论 / 0 点赞 / 22 阅读 / 1,735 字
温馨提示:
我不想探寻任何东西的意义,我只享受当下思考的快乐~

使用 Chrome DevTools 的 Performance 工具 进行性能分析

先简单的准备一段代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>worker performance optimization</title>
  </head>
  <body>
    <script>
        const xhr = new XMLHttpRequest()
        xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);
        xhr.send();
    </script>
    <script>
      function a() {
        b();
      }
      function b() {
        let total = 0;
        for (let i = 0; i < 10 * 10000 * 10000; i++) {
          total += i;
        }
        console.log("b:", total);
      }

      a();
    </script>
    <script>
      function c() {
        d();
      }
      function d() {
        let total = 0;
        for (let i = 0; i < 10 * 10000 * 10000; i++) {
          total += i;
        }
        console.log("c:", total);
      }
      c();
      function e() {
        d()
      }
      e();
    </script>
  </body>
</html>

image-20241223102703539

  • Main 就是主线程
  • FramesNetwork 等是浏览器的其他线程

HTML 中的script标签内的脚本代码也是我们常说的宏任务 【常见的宏任务包括setTimeoutsetIntervalrequestAnimationFrameI/O操作(如读取文件等)】

Performance 工具最重要的是分析主线程的 Event Loop,分析每个 Task 的耗时、调用栈等信息。

Task 浏览器执行的一个任务单元

浏览器在处理网页时,会将各种操作分解成一个个的任务来执行。这些任务包括解析 HTML、执行 JavaScript 代码、处理样式、进行网络请求等

image-20241223105020342

这里还有个需要注意的地方 时间线上是连续执行的可能会被分到同一个task 下面!

性能分析的时间轴上,它们可能会被视为在同一任务(task)中执行

image-20241223111050744

在性能分析图中,你可以看到不同类型的任务。例如,图中的 “Evaluate script” 就是一种任务类型,表示浏览器在执行 JavaScript 脚本。

还有像 “send” 这样的操作,它对应的是XMLHttpRequest对象的send方法,这也是一个任务。这个任务表示浏览器正在发送一个网络请求。

这些任务可能会被分组到一起,例如,在同一个函数调用或者同一段代码块中的操作可能会被归为一个任务,以便更好地理解代码的执行流程和性能瓶颈。例如函数a()调用b(),那么a()b()的执行很可能被视为一个任务。这是因为从代码逻辑角度看,它们是连贯的操作。

如果在XHR 外面包一层setTimeout 那么 这个send 和 后面的a() b()xhr 就不是连贯的了

image-20241223112450718

现在 setTimeouta() b() 是在一个任务单元了

XHR 的跑到这里来了

image-20241223112750085

函数分析并优化

Summary 可以看到整个执行摘要

image-20241223115835692

整个任务耗时 2.16s 主要是脚本耗时

function c() {
  d();
}
function d() {
  let total = 0;
  for (let i = 0; i < 10 * 10000 * 10000; i++) {
    total += i;
  }
  console.log("c:", total);
}

image-20241223105337786

“Bottom - up”(自底向上)

  • Self time(自耗时):指特定操作本身所花费的时间,不包括它调用的其他操作的时间。
  • Total time(总耗时):指特定操作及其调用的所有子操作总共花费的时间。
  • Activity(活动):描述了具体的操作或函数名称。可以展开开具体的信息

image-20241223120109492

右侧有源码地址,点击就可以跳到 Sources 对应的代码

image-20241223105614530

“Call tree”(调用树)

用于展示函数或操作的调用关系和时间消耗情况

“Bottom - up” 视图侧重于从耗时最长的操作开始展示

而 “Call tree” 视图则展示了完整的调用关系,帮助你理解函数是如何被调用和执行的。

image-20241223130854593

“Event log” (事件日志)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>worker performance optimization</title>
  </head>
  <body>
    <button id="myButton">点击</button>
    <script>
      // 获取按钮元素
      const button = document.getElementById("myButton");
      // 定义事件处理函数
      function handleClick() {
        alert("你点击了按钮");
      }
      // 为按钮添加点击事件监听器
      button.onclick = handleClick;
    </script>
  </body>
</html>

image-20241223131252587

它提供了一个按时间顺序排列的事件列表 StartTime,一个宏观的时间线视角,帮助开发者了解在整个操作流程中各个事件是如何依次发生的。这个 StartTime 是页面渲染完成后时间操作的时间

比如你页面渲染完 1s 后点击的按钮 StartTime 就是1000 ms

上面那些用按钮点击的那个demo 比较易看

image-20241223135100331

image-20241223135141195

image-20241223135233420

image-20241223135256810

可以使用定时器来解决阻塞的问题,但是不能解决Long task 的问题

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>worker performance optimization</title>
  </head>
  <body>
    <button id="myButton">点击</button>
    <script>
      function c() {
        return d();
      }
      function d() {
        return new Promise((resolve) => {
          setTimeout(() => {
            let total = 0;
            for (let i = 0; i < 20 * 10000 * 10000; i++) {
              total += i;
            }
            resolve(total);
          }, 100);
        });
      }
      c().then((r) => {
        console.log(r);
      });
    </script>
  </body>
</html>

image-20241224102541012

因为不管是宏任务和微任务的回调都还是在js 的主线程执行的!

使用 web worker优化 long task

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>worker performance optimization</title>
  </head>
  <body>
    <button id="myButton">点击</button>
    <script>
      function c() {
        return d();
      }
      function runWorker(url, num) {
        return new Promise((resolve, reject) => {
          const worker = new Worker(url);
          worker.postMessage(num);
          worker.addEventListener("message", function (evt) {
            resolve(evt.data);
          });
          worker.onerror = reject;
        });
      }
      function d() {
        return runWorker("./worker.js", 10 * 10000 * 10000);
      }
      c().then((r) => {
        console.log(r);
      });
    </script>
  </body>
</html>

worker.js

addEventListener('message', function(evt) {
    let total = 0;
    let num = evt.data;
    for(let i = 0; i< num; i++) {
        total += i;
    }
    postMessage(total);
});

image-20241224105020885

主线程脚本执行了 380ms

大计算量费时的转到了 work 中!

image-20241224105119857

虽然 Worker 还有 long task,但是不重要,毕竟计算量在那,只要主线程没有 long task 就行。

使用Performance 分析 Event Loop

<!DOCTYPE html>
<html lang="en">
  <body>
    <script>
      function calc1() {
        let product = 1;
        for (let k = 1; k < 10000000; k++) {
          product *= k;
        }
      }
      function calc2() {
        let product = 1;
        for (let k = 1; k < 10000000; k++) {
          product *= k;
        }
      }
      function calc3() {
        let product = 1;
        for (let k = 1; k < 10000000; k++) {
          product *= k;
        }
      }
      function calc4() {
        let product = 1;
        for (let k = 1; k < 10000000; k++) {
          product *= k;
        }
      }
      function calc5() {
        let product = 1;
        for (let k = 1; k < 10000000; k++) {
          product *= k;
        }
      }

      const timer1 = setTimeout(() => {
        console.log(3);
        calc3();

        Promise.resolve().then(() => {
          console.log(4);
          calc4();
        });
      }, 0);
      Promise.resolve().then(() => {
        console.log(1);
        calc1();
        const timer2 = setTimeout(() => {
          console.log(2);
          calc2();
        }, 0);
      });
      console.log("5");
    </script>
  </body>
</html>

image-20241224135614217

可以看到异步的执行顺序 1,3,4,2

  • “Run microtasks”(运行微任务)
  • “Timer fired”(定时器触发)
  • “Function call”(函数调用)
0

评论区