Код быстрый? Запустите счет, чтобы знать

внешний интерфейс GitHub Операция 360

Примечание редактора. Автором этой статьи является Лю Юйчэнь, фронтенд-разработчик из 360 Qi Dance Company и член рабочей группы W3C Performance.

В прошлый раз автор представилNavigation Timingа такжеResource TimingПрактическое приложение для мониторинга загрузки страниц.

На этот раз автор познакомит вас сPerformance Timelineа такжеUser TimingStandard, и используйте соответствующий API для «оценки» внешнего кода.

Зачем изучать эти два стандарта?

В реальном бизнесе иногда возникают операции, требующие высокой производительности, особенно частое использование DOM. Так как же количественно оценить производительность этих операций?

Обычной практикой является запись функций до и после выполнения функции отдельно.Date.now(), а затем возьмите разницу, чтобы получить конкретное время выполнения.

Хорошо записывать одну-две функции, если их больше, разработчику нужно вести глобальный хэш для подсчета всех данных.

вместе сPerformance Timeline + User TimingС введением стандарта разработчики могут напрямую использовать соответствующий API, а браузер будет напрямую считать релевантную информацию, что значительно упрощает процесс измерения производительности интерфейса.

чтоPerformance Timeline?

Согласно определению W3C,Performance TimelineОн предназначен для помощи веб-разработчикам в доступе, обнаружении и получении различных показателей производительности на протяжении всего жизненного цикла веб-приложений, а также в определении связанных интерфейсов.

чтоUser Timing?

User TimingВ сравнении сPerformance TimelineПодробнее. Этот стандарт расширяет первоначальныйPerformanceинтерфейс, а также добавлены новые методы для фронтенд-разработчиков для активной записи показателей производительности.

По состоянию на 29 июля 2018 г.Performance Timelineа такжеUser TimingПоследние стандартыLevel 2, и оба находятся в статусе редактирования черновика.

Совместимость с браузером

Картина показываетPerformance Timeline Level 2серединаPerformanceObserverПоддержка API:

Performance Timeline Level 2В практических приложениях в основном используетсяPerformanceObserverAPI.

Performance Timeline

Картина показываетUser Timingподдержки:

User Timing

Как используются два?

начальный уровень

Если у вас есть относительно дорогая функцияfoo, Вы любопытны в разных браузерах, выполнитеfooСколько времени требуется, тогда вы можете сделать:

const prefix = fix => input => `${fix}${input}`
const prefixStart = prefix('start')
const prefixEnd = prefix('end')
const measure = (fn, name = fn.name) => {
  performance.mark(prefixStart(name))
  fn()
  performance.mark(prefixEnd(name))
}

В приведенном выше коде используется новый API:performance.mark.

По стандарту звонитеperformance.mark(markName), происходят следующие шаги:

  • создать новыйPerformanceMarkобъект (далее запись);
  • Будуnameсвойство установлено наmarkName;
  • БудуentryTypeсвойство установлено на'mark';
  • БудуstartTimeсвойство установлено наperformance.now()значение;
  • Будуdurationсвойство установлено на0;
  • поставить запись в очередь;
  • добавить элемент вperformance entry bufferсередина;
  • вернутьundefined

По смыслу «поставить в очередь», пожалуйста, смотритеЯ 3 из .GitHub.IO/performance… [1]в очередиPerformanceEntry

Вышеупомянутый процесс можно просто понять так: «Пожалуйста, обозреватель запишет файл с именемmarkNameзапись выступления».

После этого мы можем пройтиperformance.getEntriesByTypeПолучить конкретные данные:

const getMarks = key => {
  return performance
    .getEntriesByType('mark') // 只获取通过 performance.mark 记录的数据
    .filter(({ name }) => name === prefixStart(key) || name === prefixEnd(key))
}
const getDuration = entries => {
  const { start = 0, end = 0 } = entries.reduce((last, { name, startTime }) => {
    if (/^start/.test(name)) {
      last.start = startTime
    } else if (/^end/.test(name)) {
      last.end = startTime
    }
    return last
  })
  return end - start
}
const retrieveResult = key => getDuration(getMarks(key))

performance.getEntriesByType('mark')что получаетсяmarkсгенерированные данные.

"Получение данных, зачем много кода? ОсобенноgetDuration, часть, которая различает время начала и окончания, слишком тривиальна! ? "

Команда производительности W3C, предвидя некоторые жалобы, пошла еще дальше.performance.measureметод~

performance.measureМетод получает три параметра, чтобыmeasureName,startMarkтак же какendMark.

startMarkа такжеendMarkЭто легко понять, это соответствует началу и концуmarkName.measureNameдля каждогоmeasureПоведение, обеспечивающее личность.

После звонка,performance.measureбудет основываться наstartMarkа такжеendMarkСоответствующие две записи (обе поperformance.markпроизводить), образуяentryTypeдля'measure'новых записей и автоматически рассчитывает время выполнения.

конкретные шаги, которые происходят за кулисами, иperformance.markОчень похоже, заинтересованные читатели могут обратиться к разделу 3.1.3 спецификации.Woohoo. Я 3.org/TR/user-topic… [2].

использоватьperformance.measureРефакторинг предыдущего кода:

const measure = (fn, name = fn.name) => {
  const startName = prefixStart(name)
  const endName = prefixEnd(name)
  performance.mark(startName)
  fn()
  performance.mark(endName)
  // 调用 measure
  performance.measure(name, startName, endName)
}
const getDuration = entries => {
  // 直接获取 duration
  const [{ duration }] = entries
  return duration
}
const retrieveResult = key => getDuration(performance.getEntriesByName(key))

// 使用时
function foo() {
  // some code
}
measure(foo)
const duration = retrieveResult('foo')
console.log('duration of foo is:', duration)

как? Это яснее и короче?

Здесь мы непосредственно переходимperformance.getEntriesByName(measureName)форму, полученнуюmeasureсгенерированные данные.

асинхронная функция

Асинхронная функция?async awaitПриходите на набор:

const asyncMeasure = async (fn, name = fn.name) => {
  const startName = prefixStart(name)
  const endName = prefixEnd(name)
  performance.mark(startName)
  await fn()
  performance.mark(endName)
  // 调用 measure
  performance.measure(name, startName, endName)
}

рассмотрение

mark measure
эффект Когда операция выполняется, запишите метку времени для начала + концаmarkзначения, агрегированные для формирования непосредственно используемых данных о производительности
недостаточный Для операции требуются две временные метки для измерения производительности. Если вы хотите измерить несколько операций, вам нужно вызывать их несколько раз.

Вышеупомянутые связанные API все изUser Timing Level 2. при присоединенииPerformance TimelineПосле этого мы можем оптимизировать структуру кода~

Расширенное издание

Как было сказано выше, каждый раз, когда вы хотите увидеть производительность, она как бы вызывается активноretrieveResultфункция. Один-два раза — это нормально, но чаще это, несомненно, увеличит повторяющийся код, что нарушает принцип DRY.

существуетPerformance Timeline Level 2, рабочая группа добавила новыеPerformanceObserverИнтерфейс предназначен для решения следующих трех задач:

  • Каждый раз, когда вы просматриваете данные, следует взять на себя инициативу вызова интерфейса;
  • При получении различных типов индикаторов данных генерируется повторяющаяся логика;
  • Другие ресурсы должны работать одновременноperformance buffer, возникает ситуация конкуренции ресурсов.

Для интерфейсных инженеров фактическое использование — это просто изменение API.

Еще предстоит измерить производительность операции, в поддержкуPerformance Timeline Level 2браузер, вы можете написать:

const observer = new PerformanceObserver(list =>
  list.getEntries().map(({ name, startTime }) => {
    // 如何利用数据的逻辑
    console.log(name, startTime)
    return startTime
  })
)
observer.observe({
  entryTypes: ['mark'],
  buffered: true
})

Умный телефон должен найти некоторые изменения:

  • использовалgetEntriesвместоgetEntriesByType;
  • передачаobserveметод, наборentryTypesа такжеbuffer

потому что звонюobserveметод установки желаемого наблюденияentryTypes, так что не надо звонитьgetEntriesByType.

bufferedЗначение поля состоит в том, следует лиobserverизbufferдобавить эту запись (buffer), значение по умолчаниюfalse.

о том, почемуbufferedнастройки, заинтересованные читатели могут обратиться кGitHub.com/my3from/perform… [3]

PerformanceObserverКонструктор

оглядыватьсяPerformanceObserver.

При создании экземпляра он получает параметр с именемPerformanceObserverCallback, как следует из названия, является функцией обратного вызова.

Функция имеет два параметра, которыеPerformanceObserverEntryListа такжеPerformanceObserver. Первый — это сбор данных о производительности, которые нас интересуют. На самом деле мы видели это несколько раз, например.performance.getEntriesByType('navigation')Этот тип данных будет возвращен; последний представляет собой конкретизированный объект, который можно понимать как функцию, предоставляющуюthisстоимость.

Все специфические операции, связанные с данными, такие как создание отчетов, печать и т. д., могут бытьPerformanceObserverCallbackв ходе выполнения.

После создания возвращаетobserverобъект. Этот объект имеет два ключевых метода, а именноobserveа такжеdisconnect.

  • observeИспользуется, чтобы сообщить браузеру: «Я хочу наблюдать за этими типами данных о производительности»;
  • disconnectИспользуется для отключения наблюдения, пустойbuffer.

почему бы не бытьdisconnectметод? Несколько ироничный факт заключается в том, что непрерывное наблюдение за данными о производительности в течение длительного времени является поведением, требующим относительно высокой производительности. Поэтому лучше прекратить наблюдение и очистить соответствующийbuffer, производительность выпуска.

использоватьPerformanceObserver + performance.measureРефакторинг предыдущего кода:

// 在 measure.js 中
const getMeasure = () => {
  const observer = new PerformanceObserver(list => {
    list.getEntries().forEach(({ name, duration }) => {
      console.log(name, duration)
      // 操作数据的逻辑
    })
  })
  // 只需要关注 measure 的数据
  observer.observe({
    entryTypes: ['measure'],
    buffered: true
  })
  return observer
}

// 项目入口文件的顶部
let observer
if (window.PerformanceObserver) {
  observer = getMeasure()
}

// 某一个合适的时间 不再需要监控性能了
if (observer) {
  observer.disconnect()
}

Таким образом, операция получения данных о производительности реализует DRY.

Меры предосторожности

Если вы уже готовитесь перед экраном, хотите попробовать, а затем сделать перерыв, обратите внимание на следующие меры предосторожности:

  • Возможности мониторинга производительности, предоставляемые двумя стандартами, ограничены не только интерфейсным кодом, но и сложными асинхронными интерфейсами.async + awaitМониторинг «производительности интерфейса» в форме (акцент на производительность, воспринимаемую пользователем, поскольку на производительность интерфейса могут существенно влиять сетевое окружение, использование кеша, прокси-серверы и т. д.);
  • Если вы хотите сообщить данные, вам нужно подумать о том, нужно ли полностью отправлять соответствующий код? Лучшим способом может быть: (на основеUser Agent) оттенки серого;
  • не функцияmeasureодин раз. Следует сосредоточиться на сценариях, в которых могут возникнуть узкие места в производительности. И только когда узкое место действительно возникнет, попробуйте снова составить отчет о данных;
  • Цель этой статьи не в том, чтобы заменить различные библиотеки эталонных тестов. На самом деле базовое тестирование производительности по-прежнему должно выполняться библиотекой эталонных тестов. В этой статье основное внимание уделяется «мониторингу», то есть пользовательскому опыту, и обнаружению реальных сценариев узких мест в производительности.

Суммировать

Performance Timeline + User TimingОн предоставляет интерфейсным разработчикам инструмент для измерения производительности кода. Если проект, с которым вы столкнулись, «чувствителен к производительности», начните пробовать его как можно скорее~

Спасибо

Гао Фэн, Хуан Сяолу, Лю Боуэн, Ли Сунфэн и другие выдвинули ценные ревизионные мнения по структуре и деталям статьи, которые расположены в последовательном порядке (хронологическом порядке).

Ссылка в тексте

  1. Я 3 из .GitHub.IO/performance…

  2. Woohoo. Я 3.org/TR/user-topic…

  3. GitHub.com/my3from/perform…

О Еженедельнике Циу

«Qiwu Weekly» — это сообщество передовых технологий, управляемое профессиональной командой «Qiwu Troupe» компании 360. Подписавшись на официальный аккаунт, вы можете отправить нам статью, отправив ссылку прямо на фон.