技術探し

JavaScriptを中心に記事を書いていきます :;(∩´﹏`∩);:

NodeにPerformance Timing APIが追加される

NodeへPerformance Timing APIの初期実装が入ります。

資料

developer.mozilla.org

W3C

Performance Timeline

High Resolution Time Level 2

github.com

NodeへのPR

github.com

まだ、masterやリリースラインに入っていないのでドキュメントはPRから見てください。
今回は、PerformanceFramePerformanceNodeTiming の説明は書かないので詳しくはドキュメントへ。

Nodeのイベントループの時間測定を測定するのも実装されている。

Performance Timing API

マイクロ秒で経過時間を取得するAPI である。

Dateを使い計測を行うことがあると思うが、以下のような場合がある。

const markStart = Date.now();

(() => {})();

const duration = Date.now() - markStart;

console.log(duration); // 0

実際は、正の値, 負の値, 0の値になり得てしまう。

実装

Nodeで使ってみる

processperformanceというメソッドが追加された。

> process.performance
{ timeOrigin: 14194760.508743,
  nodeTiming:
   PerformanceNodeTiming {
     duration: 2735.97479,
     startTime: 14194760.508743,
     type: 'node',
     name: 'node',
     arguments: 14194763.6652,
     initialize: 14194764.104207,
     inspectorStart: 14194769.714933,
     loopStart: 14194854.360176,
     loopExit: 0,
     loopFrame: 14196891.511942,
     bootstrapComplete: 14194854.356646,
     third_party_main_start: 0,
     third_party_main_end: 0,
     cluster_setup_start: 0,
     cluster_setup_end: 0,
     module_load_start: 14194813.331148,
     module_load_end: 14194813.33181,
     preload_modules_load_start: 14194813.332309,
     preload_modules_load_end: 14194813.356287 },
  nodeFrame:
   PerformanceFrame {
     prior: 0.012497,
     type: 'frame',
     name: 'frame',
     duration: 605.560913,
     startTime: 14196891.511942 } }
>

手順

基本的には、

  1. 開始区間のマーキング(mark)
  2. 終了区間のマーキング(mark)
  3. その区間の名前を定義して保存されます。(measure)
  4. すべて保存されたリストから3で定義した区間の名前を指定し取得(getEntriesByName)

また、同じ区間の名前をつけたときはスタックしていきます。
なのでgetEntriesByNameで取得した時に配列になっています。
これは新しいのから順にpushされます。(つまりタイムライン順)

区間を逆(未来 to 過去)にした場合、数値がすごいことになるのは仕様なのかな・・・?

少し長いですが、以下のコードを読めばわかると思います。

コード

const fs = require('fs');

const perf = process.performance;

perf.mark('A');

(() => {})();

perf.mark('B');

perf.measure('A to B', 'A', 'B'); // tagname, startMark, endMark

const measure1 = perf.getEntriesByName('A to B')[0]; // measure1's length is 1
console.log(measure1.duration); // 0.009461

/* ----------------------------------------------------- */

perf.mark('C');

fs.readFileSync('./node');

perf.mark('D');

perf.measure('C to D', 'C', 'D');

const measure2 = perf.getEntriesByName('C to D')[0]; // measure2's length is 1
console.log(measure2.duration); // 28.502531

/* ----------------------------------------------------- */

perf.measure('A to B', 'C', 'D'); // push to `A to B`

const measure3 = perf.getEntriesByName('A to B'); // measure3's length is 2
console.log(measure3); // display the performance timeline of `A to B`
/*
  [ PerformanceMeasure {
      duration: 0.009461,
      startTime: 15995656.619165,
      type: 'measure',
      name: 'A to B' },
    PerformanceMeasure {
      duration: 28.502531,
      startTime: 15995675.467252,
      type: 'measure',
      name: 'A to B' } ]
*/

console.log(perf.getEntries()); // display the performance timeline(combined `mark` and `measure` into the timeline)
/*
  [ PerformanceNodeTiming {
      duration: 107.495859,
      startTime: 15995598.683741,
      type: 'node',
      name: 'node',
      arguments: 15995602.156617,
      initialize: 15995602.750924,
      inspectorStart: 15995609.924404,
      loopStart: 0,
      loopExit: 0,
      loopFrame: 0,
      bootstrapComplete: 0,
      third_party_main_start: 0,
      third_party_main_end: 0,
      cluster_setup_start: 0,
      cluster_setup_end: 0,
      module_load_start: 15995653.258513,
      module_load_end: 15995653.313254,
      preload_modules_load_start: 15995653.313785,
      preload_modules_load_end: 15995653.341459 },
    PerformanceFrame {
      prior: 0,
      type: 'frame',
      name: 'frame',
      duration: 15995706.323224,
      startTime: 0 },
    PerformanceMark {
      duration: 0,
      startTime: 15995656.619165,
      type: 'mark',
      name: 'A' },
    PerformanceMark {
      duration: 0,
      startTime: 15995656.628626,
      type: 'mark',
      name: 'B' },
    PerformanceMeasure {
      duration: 0.009461,
      startTime: 15995656.619165,
      type: 'measure',
      name: 'A to B' },
    PerformanceMark {
      duration: 0,
      startTime: 15995675.467252,
      type: 'mark',
      name: 'C' },
    PerformanceMark {
      duration: 0,
      startTime: 15995703.969783,
      type: 'mark',
      name: 'D' },
    PerformanceMeasure {
      duration: 28.502531,
      startTime: 15995675.467252,
      type: 'measure',
      name: 'C to D' },
    PerformanceMeasure {
      duration: 28.502531,
      startTime: 15995675.467252,
      type: 'measure',
      name: 'A to B' } ]
 */

API

common

now()

現在の高分解能時刻タイムスタンプを返します。

mark(name: string)

パフォーマンスタイムラインへ新しいPerformanceMarkを追加します。
performanceEntry.typeは常にmarkです。

measure(name: string, startMark: string, endMark: string)

パフォーマンスタイムラインへ新しいPerformanceMeasureを追加します。
performanceEntry.typeは常にmeasureです。

name区間の名前です。
startMarkは開始地点の名前を設定します。もし名前が存在しない場合、timeOriginが基準となります。
endMarkは終了地点の名前を設定します。もし指定しない場合は、エラーが投げられます。

get

getEntries()

startTimeを基準にすべてのPerformanceEntry Objectのリストを時間順に返します。
mark 及び measure のタイムラインとなります。

getEntriesByType(type: string)

performanceEntry.typeと同じtypeを持つすべてのPerformanceEntry ObjectのリストをstartTimeを基準に時間順に返します。
古い物から先頭となります。

getEntriesByName(name: string, type?: string)

performanceEntry.typeと同じnameを持つすべてのPerformanceEntry ObjectのリストをstartTimeを基準に時間順に返します。
古い物から先頭となります。

clear

clearMarks(name?: string)

名前を指定すれば、そのマークを消し、
指定しなければ、すべてのマークをパフォーマンスタイムラインから消します。

clearMeasures(name?: string)

名前を指定すれば、そのメジャーを消し、
指定しなければ、すべてのメジャーをパフォーマンスタイムラインから消します。

仕様分類

High Resolution Time Level 2

High Resolution Time Level 2

toJson()

High Resolution Time

High Resolution Time

now()

Navigation Timing

timing, navigation

Performance Timeline Time Level 2

Performance Timeline Level 2

getEntries()

Performance Timeline

Performance Timeline

getEntries(), getEntriesByType(), getEntriesByName()

Resource Timing Level 1

Resource Timing Level 1

clearResourceTimings(), setResourceTimingBufferSize(), onresourcetimingbufferfull

User Timing

User Timing Level 2

User Timing

mark(), clearMark(), measure(), clearMeasure()

まとめ

Performance Timing APIに関する初期実装がもうすぐ入りそう。
まだまだ未実装な部分が多いので、今後に期待。