Date post: | 30-Jun-2015 |
Category: |
Technology |
Upload: | sensui-shogo |
View: | 192 times |
Download: | 0 times |
Browser Computing Structure
Frontrend in Fukuoka @1000ch
@1000ch
Frontend Developer
目次変化するWebとJavaScriptの役割
ブラウザが何をしているかを知る
ブラウザが遅くなる原因を知る
最適化アプローチ
変化するWebとJavaScriptの役割
• テキストベースのWebサイト
• コンテンツの更新は遷移が中心
• 装飾は<marquee>とか<blink>とか…
静的なWeb
• 非同期通信によるコンテンツの更新(Googleマップ・Googleサジェスト等)
• JavaScriptがダイナミックなWebサイトの可能性をHTMLにもたらした
動的なWeb
• HTML5やCSS3といった、技術の標準化と飛躍的な進化
• ブラウザで出来ることが増えた(WebGL・WebAudio・WebSocket…)
リッチなWeb
JavaScriptなしに HTML5を語れない
対応の難しさベンダーの数×OSのバージョン に比例して増加する実装リスク
確か存在するコストマシンスペックに左右される上にモバイル端末だとなおさら差が顕著
パフォーマンスが 犠牲になりがち
Amazon100msの高速化
売上1%増加
Mozilla
2200msの高速化
ダウンロード15.4%増加
400msの高速化
検索回数0.59%増加
パフォーマンスは最も重要なUX指標パフォーマンスはWebサイトの品質である
かっこよくても遅いサイトにユーザーは満足しない
ブラウザが何をしているかを知る
データの要求とダウンロード URLの入力やページ遷移等
HTMLレンダーツリーのレイアウトとペイント
ユーザーアクションの応答クリックやスクロールのインタラクション
ユーザーアクションの応答クリックやスクロールのインタラクション
データの要求とダウンロードURL
HTMLやCSSの解析と描画 レンダーツリーのレイアウトとペイント
ユーザーアクションの応答 クリックやスクロールのインタラクション
データの要求とダウンロードURL
HTMLレンダーツリーのレイアウトとペイント
ユーザーアクションの応答 クリックやスクロールのインタラクション
データの要求とダウンロード URLの入力やページ遷移等
HTMLやCSSの解析と描画 レンダーツリーのレイアウトとペイント
データの要求とダウンロードURL
HTMLレンダーツリーのレイアウトとペイント
ユーザーアクションの応答クリックやスクロールのインタラクション
ページのロード開始から
表示されるまで
データの要求とダウンロードURL
HTMLレンダーツリーのレイアウトとペイント
ユーザーアクションの応答クリックやスクロールのインタラクション
ページが表示されてから
次の遷移までずっと
Render ComputeNetwork
パフォーマンスの3大要素
Render ComputeNetwork
ページ表示までのイニシャルコスト
→ファーストインプレッション
Render ComputeNetwork
スクロールの滑らかさやページ応答
→ユーザーの使い心地
Chrome DevTools
ブラウザ、そして最強の開発者ツール
FirebugもいいけどやっぱChrome
Command + option + i
Google Chrome Canary
Chromeの開発者向けビルド
DevToolの新機能をいち早く試せる
たまに不安定…
DevTools > Network
Network
HTTPリクエストの数
リソースサイズのチェック
バッドリクエストがないかどうか
ネットワーク周りの最適化
DevTools > Timeline
Render
FPSの値のチェックレイアウトやペイントのタイミング
イベントの発火やGCの実行形跡
ブラウザがどのような処理をしているか大体わかる
DevTools > Profiles
Compute
JavaScriptの実行コスト
ヒープ領域に置かれるオブジェクト
Timelineより細かいメモリの状態
スクリプト周りの調査
Compute
今日はComputingの話
Keep 60FPS• FPS = Frames Per Second
• ブラウザは1秒間に60回リフレッシュする
• 60FPSを維持するには1フレームの処理を16.67ms
に収める (16.67ms=1000ms / 60FPs)
• 一般的に30FPS出ていれば滑らかである
ブラウザの処理
Load Script Render Paint Load Script Render
Load Script Render PaintRender Paint Load Script
ブラウザの処理
16.67ms
Paint Paint
特に処理がないとき
16.67ms
20FPSのサンプルhttp://codepen.io/paulirish/full/nkwKs
60FPSのサンプルhttp://codepen.io/1000ch/full/KbLHh
ブラウザが遅くなる原因を知る
Case#1 重い処理時間がかかるとペイントも遅れる
Scriptが忙しいと…
Load Script Render PaintRender Paint Load
16.67msに収まらない
Case#2 GCScriptの実行を止めてしまう
Garbage Collection?• JavaScriptでは、メモリの割当と解放がJavaScriptのエンジンによって自動で行われる。その仕組みがGarbage
Collectionである
• 最近の高水準言語にほとんど備わっている
• いわばルンバのような存在 by @ahomu
GC発生のタイミング• ブラウザによって一定の周期で実行される他、メモリが少なくなってきたり、不要なメモリが出現すると実行される
• JavaScriptでは実行のタイミングを直接コントロールする術はない
Window
AB
D
CE
Window
AB
D
CEGCの対象になる
GCの間はScriptが止まる
Load Render PaintRender Paint GC Load
16.67msに収まらない
Script
Case#3 メモリリーク何かと悪影響を及ぼす
メモリリーク• メモリ領域が圧迫されると必然的にブラウザの実行速度は低下する
• メモリの回収はページ遷移時か、Garbage Collectionの実行により行われる
• メモリリークの代表的な原因は…?
Understand memory leakshttp://www.ibm.com/developerworks/web/library/wa-jsmemory/
コンソール循環参照
クロージャタイマー
コンソール循環参照
クロージャタイマー
function Timer() { this.timerId = setInterval(function() { console.log('This is timer log.'); }, 1000); } !!!var timer = new Timer(); timer = null;
function Timer() { this.timerId = setInterval(function() { console.log('This is timer log.'); }, 1000); } !!!var timer = new Timer(); timer = null;
呼び出し元を破棄しても
タイマーが実行され続ける
function Timer() { this.timerId = setInterval(function() { console.log('This is timer log.'); }, 1000); this.stop = function() { clearInterval(this.timerId); }; } !!!var timer = new Timer(); timer.stop(); timer = null;
function Timer() { this.timerId = setInterval(function() { console.log('This is timer log.'); }, 1000); this.stop = function() { clearInterval(this.timerId); }; } !!!var timer = new Timer(); timer.stop(); timer = null;
タイマーはちゃんと止めること!
クロージャタイマー
コンソール循環参照
function Closure() { var value = 1000; return function() { return value; }; }
function Closure() { var value = 1000; return function() { return value; }; }
valueは関数から
参照され続けてしまう
function Closure() { var value = 1000; return function() { var another = 2000; return another; }; }
function Closure() { var value = 1000; return function() { var another = 2000; return another; }; }
valueは参照されなくなる
のでGCに回収される
循環参照 コンソール
タイマー クロージャ
var family = []; !var child = { age: 10 container: family }; !var parent = { child: child, container: family }; !family.push(child); family.push(parent);
var grandparent = { child: parent, container: family }; !family.push(parent);
雪だるま式に増えるメモリ
A
C B
A
C B
参照が残ってしまいGCで回収されない
A
C B
参照がなくなったので
メモリが開放される
参照関係の整理を!一般的なフレームワークはこのような
イベントリスナのモデルに従っていることが多い
コンソール循環参照
タイマー クロージャ
var object = { foo: 1, bar: 2 }; console.log(object); object = null;
var object = { foo: 1, bar: 2 }; console.log(object); object = null;
コンソールから参照され
GCから回収されない
メモリとの仁義なき戦い• いくら最適化してもGCは自動で実行されてしまう。そのインパクトを如何に小さく出来るかが重要
• 特に気を使わなければいけないのはページ遷移をしない、かつ滑らかさが重要な場合(シューティングゲームとか)
• Scriptから発生するLoadやRenderも忘れない
最適化アプローチ
Profiles未開放のオブジェクトやメモリの使用状態
使い心地の悪さを見逃さない スクロールが引っかかる等
Timeline60FPS
Profiles未開放のオブジェクトやメモリの使用状態
使い心地の悪さを見逃さないスクロールが引っかかる等
Timelineでネックを見つける 60FPSと30FPSのボーダーを目印に
Profilesで詳しく解析する 未開放のオブジェクトやメモリの使用状態
使い心地の悪さを見逃さないスクロールが引っかかる等
Timeline60FPS
Collect JavaScript CPU Profile
JavaScriptの実行にかかった時間を調べることが出来る
Take Heap Snapshot現在のページで実行され、ヒープ領域に置かれた
JavaScriptのオブジェクトの数を調べることが出来る
Record Heap Allocations
JavaScriptの実行とともに使用されるメモリの
割当と解放の状態を調べることが出来る
JavaScriptの
高速化セオリーを知る
ArrayのforEach()よりforでループ アニメーションにはrequestAnimationFrame() “use strict”;を使う try/catch(例外の捕捉)は重い parseInt()より加減演算でキャストする DOMの探索や操作は最小限に 短い関数のインライン化 eval()やwith()を使わない new Date()よりdate.now() 評価順の工夫 正規表現のキャッシュ 重い処理の非同期化(WebWorker) ArrayのforEach()よりforでループ アニメーションにはrequestAnimationFrame() “use strict”;を使う try/catch(例外の捕捉)は重い parseInt()より加減演算でキャストする DOMの探索や操作は最小限に 短い関数のインライン化 eval()やwith()を使わない new Date()よりdate.now() 評価順の工夫 正規表現のキャッシュ 重い処理の非同期化(WebWorker) etc…
JavaScript Gardenhttp://bonsaiden.github.io/JavaScript-Garden/ja/
Don't Guess it, Test it!
- Paul Lewis
jsPerfhttp://jsperf.com
メモリリークのサンプルhttp://1000ch.net/memleak
GCを避ける2つの手法
オブジェクトプール使わなくなった既定の型のオブジェクトをプールし
その型のオブジェクトを再利用する
Object Pool
Object
Object
生成したオブジェクトを
再利用することで
メモリの再割当を抑える
Object Poolshttp://beej.us/blog/data/object-pool/
スタティックメモリ使用する全てのオブジェクトをあらかじめ初期化して
使う場合にはそのオブジェクト群から借用する
Object Pool
Object Object
Object Object
オブジェクトの生成回数を
減らすアプローチ
【番外編】V8エンジンの
最適化を阻害しない
Design Elementshttps://developers.google.com/v8/design
Fast Property AccessJITコンパイル時に静的なクラスを作成し JSのコードを動的に参照するのではなく
生成されたクラス(hidden class)を高速に参照する
Dynamic Machine Code Generation
1度実行したコードをマシンコードにコンパイルし 次回以降はキャッシュしたものを実行する
(中間バイトコードもインタープリタも使わない)
クラスの定義があったら…
function Point(x, y) { this.x = x; this.y = y; }
JITコンパイル時にクラスの静的な実態を生成する
function Point(x, y) { this.x = x; this.y = y; }Hidden Class
var point = new Point();次からコンパイル済の
クラスを参照するので高速
function Point(x, y) { this.x = x; this.y = y; } Point.name = 'Name';
Hidden Classを
再度生成してしまうHidden Class
定義を途中で変更すると…Point.name = 'Name';
ブラウザが実行時に最適化したコードを
なるべく変更しないようにする!
まとめ
パフォーマンスは必須
環境の多岐化も考慮する
ブラウザの気持ちを知ることが
最適化への第一歩
銀の弾丸は存在しない
全ては最適化の積み重ね
Thank you!@1000ch
http://github.com/1000ch
http://1000ch.net
Photo Credits• http://www.flickr.com/photos/66331098@N03/6041212579
• http://www.flickr.com/photos/danichro/7284517300
• http://www.flickr.com/photos/articnomad/16153058/
• http://www.flickr.com/photos/sfgirlbybay/2739327181/
• http://www.flickr.com/photos/30859306@N00/3331140550
• http://www.flickr.com/photos/45540741@N06/7365063522/
• http://www.flickr.com/photos/chrissinjo/5368405044
• http://www.flickr.com/photos/cloudy-day/5319042359
• http://www.flickr.com/photos/57490760@N04/7218896186