вторник, 18 июня 2019 г.

PerformanceTiming API

      PerformanceTimingAPI - довольно занятное API, появившееся еще в версии 8.5 но почему-то обделенное вниманием js программистов. К примеру это API позволяет относительно легко (без погружения в C++ код) замониторить GC, ну и много чего еще.  Возможно причина тому - не очень хорошая документация, да и сам API нельзя назвать интуитивно понятным. Я считаю себя в общем-то не глупым парнем, но я разобрался в нем не с первого раза. Поэтому я и решил потратить время на написание этой статьи.
В основе интерфейса PerformanceTimingAPI (или perf_hooks как оно называется в исходникахлежит иерархия классов основанная на PerformanceEntry. PerformanceEntry - это нечто что ты можешь измерить. У PerformanceEntry есть поле entryType которое собственно показывает что же ты измерил. Для того чтобы получать PerformanceEntry тебе нужно с помощью PerformanceObserver подписаться на них. 
       Если у PerformanceEntry поле entryType="mark" то мы имеем дело с отметиной на timeline.  startTime = время когда произошло это событие, duration = 0, name = имя события. Единственный способ получить mark - самим создать его с помощью фукнции performance.mark("Start of something")
       Единственный сценарий использования performance mark - создать два performance.mark и измерить время между ними используя performance.measure(measureName, startMark, endMark). Этот вызов возвращает нам еще один PerformanceEntry с типом measure. В общем такая красивая и бестолковая обертка вокруг timestamp. 
    Далее следует PerformanceEntry с типом function. Этот тип PerformanceEntry можно получить если обернуть некоторую функцию в мониторинг (через performance.timerify(fn)). В этом случае каждый раз когда функция вызовется будет создан новый PerformanceEntry с типом function описывающий время выполнения функции. Функция timrify использует кастомные атрибуты Object.defineProperty() что негативно сказывается на скорости работы с объектом.
        PerformanceEntry с типом gc - описывает запуск сборщика мусора. В этом случае поле kind будет указывать на тип сборки- major, minor, incremental и тд. На самом деле PerformanceEntry  с типом gc это отдельный класс в C++ коде - GCPerformanceEntry. По каким-то причинам разработчики решили не экспортировать его в мир JS. 
        PerforamanceEntry с типом http2 предназначены для мониторинга http2 сессий и потоков. Если name = HTTP2Stream то этот PerformanceEntry описывает создание нового http2 stream. Если name = HTTP2Session - то соответсвенно сессии.
      Напоследок расскажу о самом сомнительном на мой взгляд архитектурном решении - PerformanceNodeTiming. Он наследуется от PerformanceEntry но у своего родителя он использует только  два поля - entityType = node и duration. Все остальные поля его собственные - они описывают процесс инициализации Node.js: сколько времени потратили на инициализацию процесса ноды(nodeStart), сколько потратили на инициализацию v8 (v8Start), сколько потратили на инициализацию environment-а(поле так и называется environment и оно почему-то не документировано),  сколько времени потратили на bootstrap (bootstrapComplete), сколько времени потребовалось для запуска event loop (loopStart). 
        В отличии от всех остальных PerformanceEntry, работа с PerformanceNodeTiming ведется  не через PerformanceObserver, а через статическое поле performance.nodeTiming. Если ты подпишешься на PerformanceObserver с типом "node" то ты не получишь ничего.  Второй момент - мне не совсем понятно как мониторить инициализацию воркеров. Ведь у них как минимум свой environment и свой eventLoop который также будет инициализироваться. А если я два воркера запускаю одновременно ? В общем это лишний раз доказывает что singleton это антипаттерн проектирования. 

Комментариев нет:

Отправить комментарий