Инструмент производительности Chrome Devtools — это мощный инструмент для анализа и оптимизации производительности, поскольку он может регистрировать время, затрачиваемое на каждый фрагмент кода, анализировать узкие места в производительности, а затем выполнять целевую оптимизацию.
Такой мощный инструмент нужно хорошо освоить.Сегодня мы сделаем кейс по оптимизации производительности, чтобы быстро начать работу с Performance.
анализ производительности
Сначала мы подготовим этот фрагмент кода:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>worker performance optimization</title>
</head>
<body>
<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< 1*10000*10000; i++) {
total += i;
}
console.log('c:', total);
}
c();
</script>
</body>
</html>
Очевидно, что два тега скрипта — это две задачи макроса, стек вызовов первой задачи макроса — это a, b, а стек вызовов второй задачи макроса — c, d.
Давайте используем Performance, чтобы увидеть, так ли это:
Сначала откройте хром в режиме инкогнито, в режиме инкогнито нет плагинов, и плагины не повлияют на производительность анализа.
Откройте панель «Производительность» chrome devtools, нажмите кнопку перезагрузки, она перезагрузит страницу и начнет запись времени:
Нажмите Готово через несколько секунд.
В это время интерфейс будет отображать записанную информацию:
Основная отмеченная на рисунке основная нить.
Основной поток непрерывно выполняет цикл событий.Вы можете видеть, что есть две задачи (макрозадачи), а стеки вызовов — это a, b и c, d соответственно, что соответствует нашему анализу. (Конечно, есть и некоторые внутренние функции браузера, такие как parseHtml, AssessmentScript и т. д., которые можно игнорировать)
Самое важное для инструмента «Производительность» — проанализировать цикл событий основного потока, а также проанализировать время, стек вызовов и другую информацию о каждой задаче.
Когда вы нажимаете на задачу макроса, детали стека вызовов будут отображаться на панели ниже (выберите «снизу вверх» — это отображение списка, «дерево вызовов» — это отображение дерева)
Время, затраченное на каждую функцию, также отображается слева, а адрес источника — справа.Щелкните, чтобы перейти к коду, соответствующему источникам.
Прямое шоу занимает каждую строчку кода, тоже удобно!
После знакомства с инструментом давайте проанализируем, где в коде есть проблемы с производительностью.
Очевидно, что время накопления циклов двух функций b и d слишком велико.
В разделе «Производительность» вы также можете видеть, что задача отмечена красным, а на панели сводки ниже также отображается предупреждение о длительной задаче.
Некоторые студенты могут спросить: зачем оптимизировать длинную задачу?
Поскольку рендеринг и выполнение JS выполняются в основном потоке, в цикле событий они будут блокировать друг друга. Если у JS есть длительная задача, она заблокирует рендеринг и приведет к зависанию страницы. Поэтому основной целью анализа производительности является поиск длинных задач и последующее их устранение.
Многие студенты могут не знать, что, по сути, отрисовка веб-страниц — это тоже макрозадача, поэтому они будут блокировать друг друга выполнением JS. Доказательство этому можно найти в моей предыдущей статье:
Производительность доказала, что рендеринг веб-страниц — это макрозадача.
Найдите код, который нужно оптимизировать, и узнайте цель оптимизации (устранение длительных задач), а затем приступайте к оптимизации.
оптимизация производительности
Цель нашей оптимизации — удалить или разделить трудоемкую логику (накопление циклов) в двух длинных задачах на несколько задач.
По поводу разделения задач можно сослаться на оптимизацию перехода React от рекурсивного рендеринга vdom к прерываемому рендерингу связного списка vdom, то есть структуру файбера, которая также предназначена для разделения длинных задач.
Но очевидно по нашей логике тут нечего разбивать, это большой цикл.
Так вы можете не запускать его в основном потоке, а запускать в других потоках? Веб-воркер браузера, по-видимому, оптимизирован для выполнения трудоемких вычислений.
Давайте попробуем:
Инкапсулируйте такую функцию, передайте URL-адрес и номер, функция создаст рабочий поток, передаст num через postMessage и прослушает событие сообщения, чтобы получить возвращенные данные.
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;
});
};
Затем функции b и c можно изменить на это:
function b() {
runWorker('./worker.js', 10*10000*10000).then(res => {
console.log('b:', res);
});
}
Трудоемкая логика перенесена в рабочий поток:
addEventListener('message', function(evt) {
let total = 0;
let num = evt.data;
for(let i = 0; i< num; i++) {
total += i;
}
postMessage(total);
});
Идеально. Давай еще раз попробуем:
Ничего себе, больше нет длинной задачи!
Затем вы также обнаружите, что под основным потоком есть еще два рабочих потока:
Хотя у Worker еще есть длинная задача, это не важно, ведь сумма расчета есть, лишь бы у основного потока не было длинной задачи.
Таким образом, разделяя объем вычислений на рабочие потоки, мы в полной мере используем возможности многоядерных процессоров и решаем проблему длинных задач основного потока, а взаимодействие интерфейсов будет очень плавным.
Давайте еще раз посмотрим на панель Sources:
Сравните с предыдущим:
Эти усилия по оптимизации видны невооруженным глазом!
Таким образом, мы вместе выполнили оптимизацию производительности веб-страницы, проанализировали длинную задачу через Peformance, нашли трудоемкий код, а затем разделили объем вычислений по воркерам для оптимизации, успешно устранив длинную задачу основного потока.
Код выложен на github, и те, кому интересно, могут скачать его и проанализировать с помощью инструмента производительности:
Суммировать
Инструмент производительности Chrome Devtools — это мощный инструмент для анализа производительности веб-страницы.Он может записывать выполнение кода в течение определенного периода времени, например цикл событий основного потока, задачу каждого цикла событий, стек вызовов каждого Задача, время выполнения каждой функции и т. д., вы также можете найти расположение исходного кода в Sources.
Цель оптимизации производительности — найти в Задаче длинную задачу и устранить ее. Поскольку отрисовка веб-страницы является макрозадачей, она находится в том же цикле событий, что и макрозадача JS, и блокирует друг друга.
Мы сделали реальный кейс оптимизации, проанализировали трудоемкую часть кода через Performance и обнаружили, что это вызвано большим объемом вычислений, поэтому мы разделили логику вычислений на рабочие потоки, чтобы в полной мере использовать возможности параллельной обработки. многоядерных процессоров, устраняя длинную задачу основного потока.
После того, как вы выполнили этот пример оптимизации производительности, не кажется ли вам, что инструмент Peformance не сложен в использовании?
На самом деле он проанализирует цикл событий основного потока, проанализирует стек вызовов Task и Task, обнаружит длинную задачу и найдет трудоемкий код.Даже если инструмент Performance освоил большую часть этого, это часто используемые функции.