Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
レンダリングパフォーマンス
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Benoît Quenaudon
September 19, 2016
Programming
120
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
レンダリングパフォーマンス
Benoît Quenaudon
September 19, 2016
More Decks by Benoît Quenaudon
See All by Benoît Quenaudon
36・15 Cash App
oldergod
1
56
Instant Apps Eulogy
oldergod
1
47
36・15 Cash App
oldergod
1
270
Heuristics in Everyday Life
oldergod
1
54
Sweet Architecture
oldergod
1
480
Architecture at Scale (droidconNYC 2022)
oldergod
2
710
Managing gRPC with Wire
oldergod
2
660
Wire & Proto3
oldergod
0
130
Effective Reactive Architecture
oldergod
2
260
Other Decks in Programming
See All in Programming
JAWS-UG横浜 #102 AWSサ終供養LT会 成仏できない AWS サービスたち 〜本日、三体供養します〜
maroon1st
0
200
えっ!!コードを読まずに開発を!?
hananouchi
0
210
継続モナドとリアクティブプログラミング
yukikurage
3
600
SREは、MCPとSRE Agentをこう使え!
kazumax55
0
150
Terraform標準の組織で AWS CDKをどう使うか
mu7889yoon
0
230
Hatena Engineer Seminar #37「言語モデルの活用に関する研究」
slashnephy
0
520
AI 輔助遺留系統現代化的經驗分享
jame2408
1
1.2k
鹿野さんに聞く!『TypeScriptコードレシピ集』で磨く実践力
tonkotsuboy_com
4
1.1k
なぜ型を書くのか? TSKaigi2026で改めて考える #tskaigi_smarthr
kajitack
0
370
霧の中の代数的エフェクト
funnyycat
1
390
Prismを使った型安全な暗号化_関数型まつり2026
_fhhmm
0
140
才能?センス?知らん、 続けたもん勝ちだ。-- 結婚・出産・癌を越えてなお、私がプロダクトを創り続ける理由
16bitidol
2
860
Featured
See All Featured
It's Worth the Effort
3n
188
29k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Joys of Absence: A Defence of Solitary Play
codingconduct
1
410
Making Projects Easy
brettharned
120
6.7k
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
730
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
0
350
ラッコキーワード サービス紹介資料
rakko
1
3.9M
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
190
Visualization
eitanlees
152
17k
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
870
Speed Design
sergeychernyshev
33
1.9k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
190
Transcript
Rendering Performance #perfmatters ケノドン ブノア @oldergod
None
60fps 閾値 • 主のデバイスは1秒に画面を60回リフレッシュしている ◦ 1 frame = 16.6ms(1000ms ÷
60) • アニメーション中にフレームを作る処理が16msを超えたらフ レームレートが低下し、画面はガタツキが発生する ⇒ ジャンク(Jank)と呼ぶ ◦ 多くなるとUXにマイナスな影響がでる
Layer って何?
• Javascript:視覚的に変化を起こす処理(javascriptに限らず) • Style:CSSルールがどの要素に一致するかを、各要素の最終的なスタイ ルを算出する • Layout(reflow):Styleによって各要素は、どのくらいのスペースが必要 か、画面内の位置、などを計算し始める • Paint:レイヤーのピクセルを書き込む処理
• Composite:全レイヤーを組み合わせ、正しくレンダリングする Pixel Pipeline
JS > Style > Layout > Paint > Composite •
パフォーマンス的に何ができるか? • どこでネックができたり、どう解決するか? • そもそもすべてのジョブを通す必要があるか? • JS / Style / Composite:必須 ⇒ Layout、Paintについては? ◦ 利用するスタイルによってスキップできる?
JS > Style > Layout > Paint > Composite 「レイアウト」プロパティの変更
⇒ • 幅、高さ、左または上の位置などに従って、ブラウザは他のす べての要素との影響を確認する必要がある • 影響する領域は再ペイントする必要がある
JS > Style > Paint > Composite 「ペイントのみ」プロパティの変更 ⇒ •
背景画像、テキストの色、影などの変更 • ページのレイアウトに影響を与えないものはスキップ • ペイントは行われる
JS > Style > Composite 「コンポジットのみ」プロパティの変更 ⇒ • ブラウザは合成を行うためにジャンプしる 任意の
CSS プロパティを変更すると上記の 3 つのバージョン のどれが実行されるかが知りたい方 ⇒ https://csstriggers.com/
Pixel Pipelineのベストを尽くす 各タスクに対して何ができるかを 細かくみてみよう
JavaScript
Animation with JavaScript • アニメーションに setTimeout、setInterval が使われる事はある があかん:いつ実行されるかわからない
Animation with JavaScript • 60fps よりはやい setInterval は無駄 • 60fps
より遅い setInterval はガタツキ • setInterval(16.6) はブラウザに同期しないとガタツキ • ヨーロッパは50Hzのデバイスがまだ多い ⇒ ブラウザのペースに合わせた requestAnimationFrame を使お う。
Window.requestAnimationFrame() function step() { // アニメーション処理, e.g. translateX += n;
等 // 更に次のフレームにも実行する処理を登録 requestAnimationFrame(step); } // 次のフレームの頭に実行しておきたい処理を登録 requestAnimationFrame(step);
requestAnimationFrame() サポート • IE10 ≧ OK • 他のブラウザは前からOK • Polyfillも書ける
⇒ requestAnimationFrame を使おう
Main Thread(又は UI Thread) • Main Thread:スタイル計算、レイアウト、ペイントと一緒にJSが 実行される • そのすべてが16ms以内に終わらない
⇒ ジャンク • JavaScript の実行中は他のタスク(UI系も含め)がブロックされ る
レンダリングとJavaScriptの関係 - 実験 http://oldergod.github.io/ui-rendering-opt/demos/main-thread/index.html
Main Thread(又は UI Thread) ⇒ DOMアクセスが必要のない処理は Web Worker に移動 ◦
ロード処理、検索、ソートとかに使える!
Web Worker ➕ 別スレッドで動く ➕ レンダリングに影響がでない ➕ XMLHttpRequest可能 ➖ DOMアクセス不可能
どうしてもメインスレッドで実行したい場合はバッチ的に小さいタス クを requestAnimationFrame で実行するのがおすすめ
Web Worker デモ http://oldergod.github.io/ui-rendering-opt/demos/web-workers/index.html
// worker の登録 const worker = new Worker('scripts/worker.js'); // manipulateImage
にて worker にメッセージを送信 worker.postMessage({ type, imageData }); // メッセージの受信時 worker.onmessage = function(message) { const imageData = message.data; toggleButtonsAbledness(); ctx.putImageData(imageData, 0, 0); }; // worker の終了 worker.terminate(); Web Worker デモ function onmessage(data); function postMessage(data); Web Worker Thread
// worker の登録 const worker = new Worker('scripts/worker.js'); // manipulateImage
にて worker にメッセージを送信 worker.postMessage({ type, imageData }); // メッセージの受信時 worker.onmessage = function(message) { const imageData = message.data; toggleButtonsAbledness(); ctx.putImageData(imageData, 0, 0); }; // worker の終了 worker.terminate(); Web Worker デモ function onmessage(data); function postMessage(data); Web Worker Thread
// worker の登録 const worker = new Worker('scripts/worker.js'); // manipulateImage
にて worker にメッセージを送信 worker.postMessage({ type, imageData }); // メッセージの受信時 worker.onmessage = function(message) { const imageData = message.data; toggleButtonsAbledness(); ctx.putImageData(imageData, 0, 0); }; // worker の終了 worker.terminate(); Web Worker デモ function onmessage(data); function postMessage(data); Web Worker Thread
// worker の登録 const worker = new Worker('scripts/worker.js'); // manipulateImage
にて worker にメッセージを送信 worker.postMessage({ type, imageData }); // メッセージの受信時 worker.onmessage = function(message) { const imageData = message.data; toggleButtonsAbledness(); ctx.putImageData(imageData, 0, 0); }; // worker の終了 worker.terminate(); Web Worker デモ function onmessage(data); function postMessage(data);
// worker の登録 const worker = new Worker('scripts/worker.js'); // manipulateImage
にて worker にメッセージを送信 worker.postMessage({ type, imageData }); // メッセージの受信時 worker.onmessage = function(message) { const imageData = message.data; toggleButtonsAbledness(); ctx.putImageData(imageData, 0, 0); }; // worker の終了 worker.terminate(); Web Worker デモ function onmessage(data); function postMessage(data); Web Worker Thread
Web Worker サポート • IE10 ≧ OK • 他のブラウザは前からOK
Layout
複雑なLayout、Layout Thrashing • Layoutのスコープは基本、documentのすべて※ ◦ 一つだけ弄れば、全部が再計算される ◦ アイテムが多ければ多いほどコストが高まる ⇒ 可能な限りレイアウトを回避
◦ Layoutを実行しないスタイルプロパティを利用する ◦ DevToolsで調査して最善を尽くす
Layout Thrashing デモ Layout Thrashing:強制的な同期レイアウトを数多く実行する事 http://oldergod.github.io/ui-rendering-opt/demos/layout-thrashing/index.html
強制的な同期レイアウトの原因 while (i--) { paragraphs[i].style.width = greenBar.offsetWidth + 'px'; }
強制的な同期レイアウトの原因 paragraphs[i + 1].style.width = greenBar.offsetWidth + 'px'; paragraphs[i].style.width =
greenBar.offsetWidth + 'px';
強制的な同期レイアウトの原因 offsetWidth = greenBar.offsetWidth + 'px'; paragraphs[i + 1].style.width =
offsetWidth; offsetWidth = greenBar.offsetWidth + 'px'; paragraphs[i].style.width = offsetWidth;
強制的な同期レイアウトの原因 offsetWidth = greenBar.offsetWidth + 'px'; paragraphs[i + 1].style.width =
offsetWidth; offsetWidth = greenBar.offsetWidth + 'px'; paragraphs[i].style.width = offsetWidth;
強制的な同期レイアウトの原因 // 最後に計算された layout 情報 offsetWidth = greenBar.offsetWidth + 'px';
paragraphs[i + 1].style.width = offsetWidth; offsetWidth = greenBar.offsetWidth + 'px'; paragraphs[i].style.width = offsetWidth;
強制的な同期レイアウトの原因 // 最後に計算された layout 情報 offsetWidth = greenBar.offsetWidth + 'px';
paragraphs[i + 1].style.width = offsetWidth; offsetWidth = greenBar.offsetWidth + 'px'; paragraphs[i].style.width = offsetWidth;
強制的な同期レイアウトの原因 // 最後に計算された layout 情報 offsetWidth = greenBar.offsetWidth + 'px';
// style を変更 ⇒ layout が無効化 paragraphs[i + 1].style.width = offsetWidth; offsetWidth = greenBar.offsetWidth + 'px'; paragraphs[i].style.width = offsetWidth;
強制的な同期レイアウトの原因 // 最後に計算された layout 情報 offsetWidth = greenBar.offsetWidth + 'px';
// style を変更 ⇒ layout が無効化 paragraphs[i + 1].style.width = offsetWidth; offsetWidth = greenBar.offsetWidth + 'px'; paragraphs[i].style.width = offsetWidth;
強制的な同期レイアウトの原因 // 最後に計算された layout 情報 offsetWidth = greenBar.offsetWidth + 'px';
// style を変更 ⇒ layout が無効化 paragraphs[i + 1].style.width = offsetWidth; // layout が無効なため、再計算 offsetWidth = greenBar.offsetWidth + 'px'; paragraphs[i].style.width = offsetWidth;
強制的な同期レイアウトの原因 // 最後に計算された layout 情報 offsetWidth = greenBar.offsetWidth + 'px';
// style を変更 ⇒ layout が無効化 paragraphs[i + 1].style.width = offsetWidth; // layout が無効なため、再計算 offsetWidth = greenBar.offsetWidth + 'px'; paragraphs[i].style.width = offsetWidth;
強制的な同期レイアウトの原因 // 最後に計算された layout 情報 offsetWidth = greenBar.offsetWidth + 'px';
// style を変更 ⇒ layout が無効化 paragraphs[i + 1].style.width = offsetWidth; // layout が無効なため、再計算 offsetWidth = greenBar.offsetWidth + 'px'; // style を変更 ⇒ layout が更に無効化、etc. paragraphs[i].style.width = offsetWidth;
強制的な同期レイアウトの原因 // 最後に計算された layout 情報 offsetWidth = greenBar.offsetWidth + 'px';
// style を変更 ⇒ layout が無効化! paragraphs[i + 1].style.width = offsetWidth; // layout が無効なため、再計算 offsetWidth = greenBar.offsetWidth + 'px'; // style を変更 ⇒ layout が無効化! paragraphs[i].style.width = offsetWidth;
強制的な同期レイアウトの原因 while (i--) { paragraphs[i].style.width = greenBar.offsetWidth + 'px'; }
let offsetWidth = greenBar.offsetWidth + 'px'; while (i--) { paragraphs[i].style.width = offsetWidth; }
強制的な同期レイアウトの原因 • Layout系の情報は最後のフレーム時のデータ • Style を更新すると、Layoutの情報が無効となり、Layout再計算 が必要となる ⇒ Style、Layoutの取得を一回のみ、最初にしておく事 ⇒
コードを書く時にPixel Pipelineの順序を考慮しよう
Paint
ペイントをシンプルにする • ペイントはパイプライン内のすべてのタスクのうちで最も長く実 行される
• ブラウザは複数のレイヤー、必要な場合はコンポジ層だけに対 してペイントを行うことができる ⇒ 再ペイントされるもの、移動するものを、他のコンポネントに影 響を与えないで処理できる! ペイントの対象を絞る レイヤーはどう生成できる?
Composite
レイヤーの作る方法 1. 最良: will-change: transform; 2. ハック:transform: translateZ(0); ◦ will-changeをサポートしていないブラウザに利用
コンポジットのみプロパティを使用 ⚠ 条件:プロパティを変更するに要素が自身コンポジタのレイ ヤーでなければならない! ⇒ その要素をプロモートし、レイヤーを作る
コンポジットのみプロパティを使用 • Position transform: translate(xpx, ypx); • Scale transform: scale(n);
• Rotation transform: rotate(ndeg), • Skew transform: skew(X|Y)(ndeg), • Matrix transform: matrix(3d)(...), • Opacity opacity: 0...1;
レイヤーの作る方法 デモ http://oldergod.github.io/ui-rendering-opt/demos/layout-management/index.html
will-change: transform; か transform: translateZ(0); を使う事で要素 がプロモートされる * { will-change:
transform; transform: translateZ(0); } コンポネントのプロモート // 管理とメモリコストが // 余計に発生するからNG
will-change サポート • IE、Edge:アウト • 他のブラウザは超最近でOK
Rendering Performance まとめ:上 • 改修する前に必ず測る事 • 問題ないものを直すのが無駄 ⇒ 測ってから改修するのが第一
Rendering Performance まとめ:下 • Less work • Scheduled work •
Isolated work • Smooze work ⇒ Happy User
Fin #perfmatters 格好良いエンジニア募集中! http://goo.gl/9GK409
リファレンス • グーグルによるレンダリングパフォーマンス ◦ https://developers.google.com/web/fundamentals/performance/rendering/?hl=ja • 無料ブラウザレンダリング改善コース ◦ https://www.udacity.com/course/browser-rendering-optimization--ud860 •
デモのレポジトリ: ◦ https://github.com/oldergod/ui-rendering-opt • CSSプロパティ詳細 ◦ https://csstriggers.com/ • 効率良いCSS ◦ https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Writing_efficient_CSS • BEM方針 ◦ https://en.bem.info • アニメーションFLIP方法 ◦ https://github.com/googlechrome/flipjs