Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
レンダリングパフォーマンス
Search
Benoît Quenaudon
September 19, 2016
Programming
130
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
65
Instant Apps Eulogy
oldergod
1
66
36・15 Cash App
oldergod
1
290
Heuristics in Everyday Life
oldergod
1
63
Sweet Architecture
oldergod
1
500
Architecture at Scale (droidconNYC 2022)
oldergod
2
740
Managing gRPC with Wire
oldergod
2
680
Wire & Proto3
oldergod
0
150
Effective Reactive Architecture
oldergod
2
270
Other Decks in Programming
See All in Programming
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
750
AWS CDKのカスタムリソースでContinuum(旧Security Agent)を実装した話 | Implementing Continuum (formerly Security Agent) via AWS CDK Custom Resource
akihisaikeda
2
150
當我們開始不讀 Code - AI Agent 時代,Code Review 與 Clean Code 還剩下什麼?
jame2408
1
170
ジョジョの世界をXRで再現する
mofurune
0
160
UnityでSystem.Net.WebSocketsなWebSocketサーバが動かないのでUnity Monoのコードを覗いてみた / about implementing websocket server with unity mono
drumath2237
1
610
Atomic Design, Enforced: Scaling Mobile Design Systems (next.app devCon / droidCon Berlin 2026)
steliosf
PRO
0
170
sbt 2.0: takeoff
eed3si9n
0
160
iOS 27でニュースアプリはどう変わる!? 〜日経電子版の新機能対応と、開発事例から〜
lynnswap
7
13k
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
1k
速習iPhone Duo対応
yuukiw00w
2
990
dotNET_vulnclicker_intro.pdf
deka_morita
0
140
Simple Storage Service(S3) is not simple
iwatsukayura
0
290
Featured
See All Featured
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
330
ラッコキーワード サービス紹介資料
rakko
1
5.3M
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
68
59k
Building an army of robots
kneath
307
47k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
630
Prompt Engineering for Job Search
mfonobong
0
510
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4.4k
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
320
Game over? The fight for quality and originality in the time of robots
wayneb77
1
300
Ethics towards AI in product and experience design
skipperchong
2
430
Into the Great Unknown - MozCon
thekraken
41
2.8k
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