From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- .../scenarios/animating_css_properties/index.html | 156 ++++++++++++++ files/ja/tools/performance/scenarios/index.html | 10 + .../scenarios/intensive_javascript/index.html | 231 +++++++++++++++++++++ 3 files changed, 397 insertions(+) create mode 100644 files/ja/tools/performance/scenarios/animating_css_properties/index.html create mode 100644 files/ja/tools/performance/scenarios/index.html create mode 100644 files/ja/tools/performance/scenarios/intensive_javascript/index.html (limited to 'files/ja/tools/performance/scenarios') diff --git a/files/ja/tools/performance/scenarios/animating_css_properties/index.html b/files/ja/tools/performance/scenarios/animating_css_properties/index.html new file mode 100644 index 0000000000..106a08aecc --- /dev/null +++ b/files/ja/tools/performance/scenarios/animating_css_properties/index.html @@ -0,0 +1,156 @@ +--- +title: CSS プロパティのアニメーション +slug: Tools/Performance/Scenarios/Animating_CSS_properties +translation_of: Tools/Performance/Scenarios/Animating_CSS_properties +--- +
{{ToolsSidebar}}
+

CSS プロパティのアニメーションにかかるパフォーマンスのコストは、プロパティにより異なります。また、高コストな CSS プロパティのアニメーションは、ブラウザがスムーズなフレームレートを確保しようと努力するために jank が発生する場合があります。

+ +

フレームレートウォーターフォールは CSS アニメーションにおいてブラウザが何を行っているかを明らかにして、パフォーマンスの問題の診断を支援します。

+
+ +

CSS アニメーションでは複数のキーフレームを指定して、それぞれのキーフレームではアニメーションの特定の段階における要素の外見を定義するために CSS を使用します。ブラウザは、それぞれのキーフレームから次のキーフレームへ遷移することでアニメーションを作成します。

+ +

JavaScript を使用して要素のアニメーションを行うのに比べて、CSS アニメーションは簡単に作れます。またブラウザはいつフレームを描画するかをより制御でき、また必要に応じてフレームを破棄できますので、パフォーマンスが高くなります。

+ +

しかし CSS プロパティを変更するためのパフォーマンスコストは、プロパティにより異なります。高コストな CSS プロパティのアニメーションは、ブラウザがスムーズなフレームレートを確保しようと努力するために jank が発生する場合があります。

+ +

CSS レンダリングのウォーターフォール

+ +

CSS が変更されたときにブラウザがページを更新するためのプロセスは、以下のステップで構成されるウォーターフォールで説明できます:

+ +

+ +
    +
  1. スタイルを再計算: 要素の CSS プロパティが変更されるたびに、ブラウザは算出スタイルを再計算しなければなりません。
  2. +
  3. レイアウト: 続いて、要素の位置や形状を計算するために算出スタイルを使用します。この操作は "レイアウト" と名付けられていますが、"リフロー" とも呼ばれます。
  4. +
  5. 描画: そして、ブラウザはスクリーンに要素を再描画しなければなりません。最後のステップはこの流れで示していません。ページは複数のレイヤーに分割され、それぞれを独立して描画した後に、"コンポジション" と呼ばれるプロセスで合成されます。
  6. +
+ +

この流れが完了しなければスクリーンを更新できませんので、ひとつのフレーム内に一連の操作を収めなければなりません。毎秒 60 フレームが、アニメーションがスムーズに見えるレートとして広く受け入れられています。毎秒 60 フレームのレートのために、ブラウザが一連の操作を実行する時間として 16.7 ミリ秒が与えられます。

+ +

CSS プロパティのコスト

+ +

レンダリングのウォーターフォールにおいて、一部のプロパティは他のプロパティに比べて特にコストが高くなります:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
プロパティの種類コスト
要素の形状位置に影響を与えるプロパティ。これらはスタイルの再計算、レイアウト、再描画を発生させる。 +

left
+ max-width
+ border-width
+ margin-left
+ font-size

+
+

形状や位置への影響はないが、個別のレイヤーでは描画されないプロパティ。レイアウトは発生しない。

+
+

color

+
個別のレイヤーで描画されるプロパティは、再描画が発生しない。更新はコンポジションで制御される。 transform
+ opacity
+ +
+

CSS Triggers の Web サイトで、各 CSS プロパティでどれだけのウォーターフォールが発生するかをまとめています。WebKit 固有の情報ですが、ほとんどはすべての最新ブラウザで同じです。

+
+ +

例: margin と transform

+ +

本章では、ウォーターフォールmargin を使用したアニメーションと transform を使用したアニメーションの違いを、どのように明らかにできるかを示します。

+ +

このシナリオは、margin を使用したアニメーションは例外なく悪いアイデアであると納得させる意図はありません。サイトを描画するためにブラウザが何を行っているかをツールがどのようにして明らかにするか、およびパフォーマンスの問題の診断や解決のためにその情報をどのように適用できるかを示すものです。

+ +

自身でも試してみたい場合は、デモ Web サイトがこちらにあります。サンプルは以下のようなものです:

+ +

ここには 2 つのコントロールがあります。アニメーションを開始/停止するボタンと、margin のアニメーションまたは transform のアニメーションを選択するラジオボタンです。

+ +

ページ上には複数の要素を置いており、それらに linear-gradient の背景と box-shadow を追加しています。これは、双方のプロパティは描画のコストが比較的高いためです。

+ +

動画版のウォークスルーも用意しています:

+ +

{{EmbedYouTube("Tvu6_j8Qzfk")}}

+ +

margin を使用したアニメーション

+ +

"Use margin" を選択したままでアニメーションを開始して、パフォーマンスツールを開いて記録を始めましょう。記録時間は数秒だけでかまいません。

+ +

最初の記録を開きます。どのような結果になるかはマシンやシステム負荷に大きく依存しますが、おそらく以下のようになるでしょう:

+ +

+ +

ここでは 3 つの視点で示しています: (a) ウォーターフォールの概要、(b) フレームレート、(c) タイムラインの詳細 です。

+ +

ウォーターフォールの概要

+ +

+ +

これは、ウォーターフォールをコンパクトに表示したビューです。

+ +

緑色が圧倒的に多いことは、描画に多くの時間を費やしていることを示しています。

+ +

 

+ +

フレームレート

+ +

+ +

これは、フレームレートを表示します。ここでは平均フレームレートが 46.67fps であり、目標の 60fps をかなり下回っています。さらに悪いことにフレームレートが何度も 10 から 20 fps 台に低下しており、グラフがぎざぎざになっています。特にユーザとの対話が加わると、スムーズなアニメーションではなくなるでしょう。

+ +

ウォーターフォール

+ +

記録表示領域の残りの部分で、ウォーターフォールを表示します。ウォーターフォールをスクロールすると、以下のようなパターンが見られるでしょう:

+ +

+ +

これはレンダリングのウォーターフォールを表します。それぞれのアニメーションフレームで、すべての要素のスタイルを再計算してからレイアウト処理を 1 回実施して、再描画を行います。

+ +

ここでは、描画が特にパフォーマンスへ悪影響を与えていることがわかります。前出のスクリーンショットでは描画処理をハイライトしており、この処理は 13.11 ミリ秒かかっていることが右側のボックスでわかります。すべての処理に割り当てられた時間は 16.7 ミリ秒しかありませんので、高いフレームレートを維持できないのは驚くことではありません。

+ +

ここでインスペクタを使用して box-shadow を削除すると、描画時間にどのような影響があるかを実験できます。しかし次は、margin の代わりに transform を使用して高コストな描画を完全になくす方法を見ていきます。

+ +

transform を使用したアニメーション

+ +

Web ページのラジオボタンを "Use transform" に切り替えて、新たに記録してみましょう。すると、以下のようになるでしょう:

+ +

+ +

ウォーターフォールの概要

+ +

+ +

margin を使用した場合と比べて緑色がとても少なく、また桃色がとても多くなっています。桃色はレイアウトやスタイルの再計算でしょう。

+ +

フレームレート

+ +

+ +

margin を使用した場合と比べて、良好であるように見えます。平均値は 60fps に近く、また開始付近で 1 回落ち込んでいることを除けば高いフレームレートを維持しています。

+ +

ウォーターフォール

+ +

タイムラインビューで、フレームレートが向上した理由が示されています。margin を使用した場合と比べて、レイアウトや (この例ではさらに重要な) 描画に少しも時間を費やしていません:

+ +

+ +

この例では transform を使用することでサイトのパフォーマンスが著しく向上しており、またどのようにおよびなぜ向上したかを、パフォーマンスツールで示すことができました。

diff --git a/files/ja/tools/performance/scenarios/index.html b/files/ja/tools/performance/scenarios/index.html new file mode 100644 index 0000000000..0b1a33b018 --- /dev/null +++ b/files/ja/tools/performance/scenarios/index.html @@ -0,0 +1,10 @@ +--- +title: シナリオ +slug: Tools/Performance/Scenarios +tags: + - TopicStub +translation_of: Tools/Performance/Scenarios +--- +
{{ToolsSidebar}}
+ +

パフォーマンスシナリオ

diff --git a/files/ja/tools/performance/scenarios/intensive_javascript/index.html b/files/ja/tools/performance/scenarios/intensive_javascript/index.html new file mode 100644 index 0000000000..34cc1db672 --- /dev/null +++ b/files/ja/tools/performance/scenarios/intensive_javascript/index.html @@ -0,0 +1,231 @@ +--- +title: 集約的な JavaScript +slug: Tools/Performance/Scenarios/Intensive_JavaScript +translation_of: Tools/Performance/Scenarios/Intensive_JavaScript +--- +
{{ToolsSidebar}}
+

デフォルトでブラウザはレイアウト、リフロー、ガベージコレクションだけでなく、ページ内のすべての JavaScript もひとつのスレッドで実行します。これは長い間実行する JavaScript がスレッドをブロックして、ページの不応答やユーザエクスペリエンスの悪化を招くおそれがあるということです。

+ +

フレームレートおよびウォーターフォールツールを使用して、いつ JavaScript がパフォーマンスの問題を起こしているかを知る、および特に注意が必要な関数を選び出すことができます。

+ +

本記事では長い間実行する JavaScript が応答性の問題を起こしているサンプルサイトを使用して、問題を修正するために 2 種類の方法を適用していきます。ひとつは長い間実行する JavaScript を複数の部品に分けて、それらのスケジューリングに requestAnimationFrame を使用する方法、もうひとつは web worker を使用して関数全体を別のスレッドに分ける方法です。

+
+ +

自身でも試してみたい場合は、デモ Web サイトがこちらにあります。

+ +

動画版のウォークスルーも用意しています:

+ +

{{EmbedYouTube("Pcc6jQX6JDI")}}

+ +

デモ Web サイトは以下のようなものです:

+ +

ここには 3 つのコントロールがあります:

+ + + +

ラジオボタンで "Use blocking call in main thread" を選択して、記録を始めましょう:

+ + + +

どのような結果になるかはマシンにより異なりますが、おそらく以下のようになるでしょう:

+ +

+ +

この画像の上半分はウォーターフォールの概要です。これはウォーターフォールをコンパクトに表示したビューであり、記録中にブラウザが行った処理は何かを示します。桃色はほとんどの場合 CSS の再計算、一部はリフローです。これは、プロファイルで終始実行している CSS アニメーションです。また連続したの橙色のブロックが 3 つありますが、これは JavaScript を実行していることを表します。それぞれ、ボタンを押したときです。

+ +

下半分はタイムラインの概要と時系列が合わせられており、フレームレートを示しています。記録中のほとんどはフレームレートが良好ですが、ボタンを押すたびに大きく落ち込んでいます。

+ +

それら 3 か所のうちひとつを選択して、メインのウォーターフォールビューで詳しく見ることができます:

+ +

+ +

ここではボタンを押したときに、ブラウザが JavaScript の関数をひとつまたは連続的に実行して、メインスレッドを 71.73 ミリ秒、言い換えるとフレーム 4 つ分の時間ブロックしています。

+ +

どの関数でしょう? フレームチャートビューに切り替えると、それがわかります:

+ +

+ +

これは、その時点で実行している JS のコールスタックを表示します。スタックの一番上は calculatePrimes() という関数であり、ファイル名や行番号がわかります。以下に掲載したコードで、直近の呼び出し元を見てみましょう:

+ +
const iterations = 50;
+const multiplier = 1000000000;
+
+function calculatePrimes(iterations, multiplier) {
+  var primes = [];
+  for (var i = 0; i < iterations; i++) {
+    var candidate = i * (multiplier * Math.random());
+    var isPrime = true;
+    for (var c = 2; c <= Math.sqrt(candidate); ++c) {
+      if (candidate % c === 0) {
+          // not prime
+          isPrime = false;
+          break;
+       }
+    }
+    if (isPrime) {
+      primes.push(candidate);
+    }
+  }
+  return primes;
+}
+
+function doPointlessComputationsWithBlocking() {
+  var primes = calculatePrimes(iterations, multiplier);
+  pointlessComputationsButton.disabled = false;
+  console.log(primes);
+}
+
+ +

ここではかなり大きな数に対して、(とても非効率な) 素数の判定を 50 回行っています。

+ +

requestAnimationFrame を使用する

+ +

この問題を解決するための最初の試みとして、関数をいくつかの自己充足した小さな関数に分割して、requestAnimationFrame() を使用してそれらをスケジューリングします。

+ +

requestAnimationFrame() は与えられた関数を、各フレームで再描画を行う直前に実行するようブラウザに指示します。それぞれの関数が適度に小さければ、ブラウザは実行時間を、フレーム間に与えられた時間内に収めることができるでしょう。

+ +

calculatePrimes() の分割はとてもシンプルです。別の関数で、それぞれの値が素数であるかの計算を行います:

+ +
function doPointlessComputationsWithRequestAnimationFrame() {
+
+  function testCandidate(index) {
+    // finishing condition
+    if (index == iterations) {
+      console.log(primes);
+      pointlessComputationsButton.disabled = false;
+      return;
+    }
+    // test this number
+    var candidate = index * (multiplier * Math.random());
+    var isPrime = true;
+    for (var c = 2; c <= Math.sqrt(candidate); ++c) {
+      if (candidate % c === 0) {
+          // not prime
+          isPrime = false;
+          break;
+       }
+    }
+    if (isPrime) {
+      primes.push(candidate);
+    }
+    // schedule the next
+    var testFunction = testCandidate.bind(this, index + 1);
+    window.requestAnimationFrame(testFunction);
+  }
+
+  var primes = [];
+  var testFunction = testCandidate.bind(this, 0);
+  window.requestAnimationFrame(testFunction);
+}
+ +

こちらのバージョンを試してみましょう。"Use requestAnimationFrame" と記載されたラジオボタンを選択して、新たにプロファイルを記録します。すると、記録は以下のようになるでしょう:

+ +

+ +

これはまさに、私たちが期待していたものです。一続きの橙色のブロックに代わり、ボタンを押すたびにとても短い橙色のブロックがたくさん並んでいます。橙色のブロックは 1 個ずつのフレームに分かれて現れており、またそれぞれのブロックが、requestAnimationFrame() から呼び出された関数 1 個を表しています。なお、このプロファイルではボタンを 2 回しか押していないことに注意してください。

+ +

関数の呼び出しは CSS アニメーションに由来する桃色のブロックの間に挟み込まれており、またそれぞれの関数は、全体のフレームレートを落とすことなく処理できるほど十分に小さくなっています。

+ +

ここでは requestAnimationFrame が応答性の問題の解決策として機能しましたが、潜在的な問題点が 2 つあります:

+ + + +

Web Worker を使用する

+ +

ここでは、Web Worker を使用して問題を解決します。Web Worker を使用すると、別のスレッドで JavaScript を実行できます。メインスレッドと Worker スレッドは互いに直接呼び出すことはできませんが、非同期メッセージ API を使用して通信できます。

+ +

メインスレッドのコードは以下のようになります:

+ +
const iterations = 50;
+const multiplier = 1000000000;
+
+var worker = new Worker("js/calculate.js");
+
+function doPointlessComputationsInWorker() {
+
+  function handleWorkerCompletion(message) {
+    if (message.data.command == "done") {
+      pointlessComputationsButton.disabled = false;
+      console.log(message.data.primes);
+      worker.removeEventListener("message", handleWorkerCompletion);
+    }
+  }
+
+  worker.addEventListener("message", handleWorkerCompletion, false);
+
+  worker.postMessage({
+    "multiplier": multiplier,
+    "iterations": iterations
+  });
+}
+ +

元のコードと比べたときの主な違いは、以下のものが必要であることです:

+ + + +

また、新たに "calculate.js" ファイルが必要であり、こちらは以下のようになります:

+ +
self.addEventListener("message", go);
+
+function go(message) {
+  var iterations = message.data.iterations;
+  var multiplier = message.data.multiplier;
+  primes = calculatePrimes(iterations, multiplier);
+
+  self.postMessage({
+    "command":"done",
+    "primes": primes
+  });
+}
+
+function calculatePrimes(iterations, multiplier) {
+  var primes = [];
+  for (var i = 0; i < iterations; i++) {
+    var candidate = i * (multiplier * Math.random());
+    var isPrime = true;
+    for (var c = 2; c <= Math.sqrt(candidate); ++c) {
+      if (candidate % c === 0) {
+          // not prime
+          isPrime = false;
+          break;
+       }
+    }
+    if (isPrime) {
+      primes.push(candidate);
+    }
+  }
+  return primes;
+}
+ +

Worker では処理の開始を指示するメッセージをリッスンする、および処理が完了したときに "done" メッセージを送ることが必要です。実際に計算を行っている部分のコードは、最初のバージョンのコードと完全に同じです。

+ +

このバージョンはどのように実行されるのでしょう? ラジオボタンを "Use a worker" に切り替えて、新たにプロファイルを記録してください。結果は以下のようになるでしょう:

+ +

+ +

このプロファイルでは、ボタンを 3 回押しています。ウォーターフォールの概要で元のバージョンと比べると、ボタンを押したときにはとても短い橙色のマーカーが 2 個あることがわかります:

+ + + +

これら 2 つの関数の間で Worker は素数の判定を行っていますが、メインスレッドの応答性には少しも影響を与えていないように見受けられます。これはあり得ないと思うかもしれませんが、Worker は別のスレッドで実行しますのでマルチコアプロセッサの利点を享受できます。これはシングルスレッドの Web サイトでは得られません。

+ +

Web Worker の主な制限は、Worker で実行するコードでは DOM API を使用できないことです。

-- cgit v1.2.3-54-g00ecf