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
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
52
Instant Apps Eulogy
oldergod
1
43
36・15 Cash App
oldergod
1
260
Heuristics in Everyday Life
oldergod
1
51
Sweet Architecture
oldergod
1
470
Architecture at Scale (droidconNYC 2022)
oldergod
2
710
Managing gRPC with Wire
oldergod
2
650
Wire & Proto3
oldergod
0
120
Effective Reactive Architecture
oldergod
2
250
Other Decks in Programming
See All in Programming
Skillsは効率化、Agentsは"自分の拡張"——Builder時代のエージェント編成(CC Night 2026)
wemra
1
110
Claspは野良GASの夢をみるか
takter00
0
170
net-httpのHTTP/2対応について
naruse
0
450
AutonomyとControlのあいだ:Graflowで記述するAIエージェント協調
myui
0
110
Datadog × OpenTelemetry 入門と実践のあいだ
kn_to_maxpno
1
150
プロパティの順序で型推論が壊れる!? TypeScript6.0の修正からContext-Sensitivityの仕組みを追う
bicstone
2
1.3k
OSもどきOS
arkw
0
470
Oxcを導入して開発体験が向上した話
yug1224
4
290
Spec Driven Development | AI Summit Lisbon
danielsogl
PRO
0
170
AI時代の仕事技芸論 — ソフトウェア開発で「遊ぶように働く」職人的熟達のすすめ
kuranuki
1
630
Oxlintのカスタムルールの現況
syumai
6
1k
TypeScript+Orvalで実現する型安全かつ堅牢でスケーラブルなマルチチャネル通知基盤 / TSKaigi Night talks ~after conference~
d0riven
0
320
Featured
See All Featured
GraphQLとの向き合い方2022年版
quramy
50
15k
How to train your dragon (web standard)
notwaldorf
97
6.7k
The SEO identity crisis: Don't let AI make you average
varn
0
480
Large-scale JavaScript Application Architecture
addyosmani
515
110k
Fashionably flexible responsive web design (full day workshop)
malarkey
408
66k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
430
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Build your cross-platform service in a week with App Engine
jlugia
234
18k
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
210
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
320
My Coaching Mixtape
mlcsv
0
140
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
120k
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