56
悩める組込機器向けウェブコンテンツのパフォーマンス 2015年1月25日 HTML5 Conference 2015 @futomi futomi.hatano

HTML5 Conference 2015 悩める組込機器向けウェブコンテンツのパフォーマンス

Embed Size (px)

Citation preview

悩める組込機器向けウェブコンテンツのパフォーマンス

2015年1月25日

HTML5 Conference 2015

@futomi futomi.hatano

もくじ

• 組込機器とブラウザー

• 組込機器ブラウザーの問題

• パフォーマンスの定義

• ページのロード

• ペイント領域とGPUメモリー

• 画像のロード

• ビデオのロード

• メモリー消費

• prototype

• DOMアクセス

• まとめ

組込機器とブラウザー

SBC (Single Board Computer)

Raspberry Pi B+ BeagleBone Black ODROID-U3 ODROID-C1

Cubieboard 1/2 Marsboard RK3066 Radxa Rock Lite

HummingBoard i1/i2

Banana Pi KDDI Open Web Board

貧弱なCPUと少ないメモリ

SONY Xperia Z3

QualcommSnapdragon 801CPU: 4 Core (2.5GHz)RAM: 3GB

Raspberry Pi B+

BroadcomBCM2835CPU: 1 Core (700MHz)RAM: 512MB

Cubieboard 2

AllwinnerA20CPU: 2 Core (1GHz)RAM: 1GB

HummingBoard i1

Freescalei.MX6 SoloCPU: 1 Core (1GHz)RAM: 512GB

Alcatel One Touch Fire

QualcommSnapdragon MSM7227ACPU: 1 Core (1GHz)RAM: 256MB

近年のスマートフォンは超贅沢品

組込機器ブラウザーの問題

テレビブラウザーの系統

スマートフォン向けブラウザー事情に酷似ウェブアプリ開発の環境が整う

バージョンが固定

• なかなかブラウザーは更新されない

• 機器のライフサイクルが長い

• 古いバージョンが長期間残り続ける

• いつか勝手に解決するとは思ってはいけない...

http://www.bbc.com/news/technology-20957218

貧弱なCPUと少ないメモリー

• 処理時間オーバー• スマートフォンではサクッと終わる処理がなかなか終わらない

• アルゴリズムを考え直すか、分割して処理する必要がある

• メモリーリーク• スマートフォンでは気にならなかったメモリーリークがすぐに顕在化

• JSによるメモリーのお漏らしは厳重にチェックする必要がある

• ブラウザーのバグが顕著に出る• 組込機器はCPUが貧弱でメモリが少ないため、PCやスマートフォンでは顕在化しない問題が起こる

パフォーマンスの定義

パフォーマンスとは

• 速く• 起動を速く

• 処理を速く

• アニメーションを滑らかに

• 少なく• メモリ消費を少なく

• CPU処理を少なく

• 長く• 安定動作を長く

パフォーマンスとは

• 速く• 起動を速く

• 処理を速く

• アニメーションを滑らかに

• 少なく• メモリ消費を少なく

• CPU処理を少なく

• 長く• 安定動作を長く

すべてをかなえてくれる魔法の杖は...

パフォーマンスとは

• 速く• 起動を速く

• 処理を速く

• アニメーションを滑らかに

• 少なく• メモリ消費を少なく

• CPU処理を少なく

• 長く• 安定動作を長く

すべてをかなえてくれる魔法の杖は...

ありません!!!

パフォーマンスとは

• 速く• 起動を速く

• 処理を速く

• アニメーションを滑らかに

• 少なく• メモリ消費を少なく

• CPU処理を少なく

• 長く• 安定動作を長く

すべてをかなえてくれる魔法の杖は...

ありません!!!

何を捨てるのか、何を諦めるのか、を決めるのも重要

しかし、ちょっとした配慮でパフォーマンスを改善

ページのロード

ネットワーク処理は高コスト

• ダウンロードサイズとファイル数は少ないほど良い

• HTML/CSS/JavaScriptのMinify

• 極端に言えば、すべてHTMLにインラインに埋め込むのがベスト

• パフォーマンスと保守性のバランスを考えましょう

• 極度にMinifyツールを使うとJSが動作しない場合も

• CSS Spriteもほどほどに

Google Closure Compiler

https://developers.google.com/closure/compiler/

ペイント領域とGPUメモリー

Chromeデベロッパーツール

1

2

3

ペイント領域

FPS meter

ペイント領域とGPUメモリー

• 一般的に良く言われる手法

• ペイント領域は可能な限り小さく

• GPUアクセラレーションが有効な環境ならレイヤーを活用

• 動かすだけなら JavaScript のタイマーによる位置移動は避ける

• CSS Transitions と CSS Transforms の活用

• 組込デバイスだと

• GPUメモリーの上限が低い

• レイヤー活用は必要最小限に

• ペイント領域を可能な限り小さくするのは言うまでもない

コンテンツの移動

setTimeout() と left プロパティによるアニメーション

CSS Transitions と CSS Transforms の translateX() によるアニメーション

移動のたびにペイント処理が実行され不効率

レイヤー処理により移動がスムーズかつGPUメモリー消費が少ない

レイヤーの使いすぎに注意

• 組込機器のGPUメモリーはさほど多くない

• メインメモリーやGPUメモリーの速度もネックになる

• 特にフルHDなど広い解像度を扱うコンテンツでは要注意

• 使いすぎると返って不効率

値のリアルタイム更新

<p>経過時間:<span id="t">0</span>ミリ秒。</p>

var el = document.querySelector("#t");(function countUp(now) {

el.textContent = Math.round(now);window.requestAnimationFrame(countUp);

})();

数値のみを書き換えているのに、行全体が再ペイント

JS

値のリアルタイム更新

<p>経過時間:<span id="t">0</span>ミリ秒。</p>

var el = document.querySelector("#t");(function countUp(now) {

el.textContent = Math.round(now);window.requestAnimationFrame(countUp);

})();

数値のみを書き換えているのに、行全体が再ペイント

JS

浮かせる/サイズを固定する

<p>経過時間:<span id="outer"><span id="t">0</span></span>ミリ秒。</p>

var el = document.querySelector("#t");(function countUp(now) {

el.textContent = Math.round(now);window.requestAnimationFrame(countUp);

})();

#outer {display: inline-block;width: 100px;height: 20px;position: relative;

}#t {

display: inline-block;width: 100px;position: absolute;text-align: right;

}

JS

CSS

表示と非表示の切替

<body><img id="logo" src="imgs/logo.png"> <footer>...</footer>

</body>

var el = document.getElementById("logo");var hidden = false;window.setInterval(function() {

hidden = !hidden;el.style.display =

hidden ? "none" : "block";}, 1000);

ロゴしか切り替えていないのにリフローの発生よりページ全体が再ペイントの対象に

実際に見た目にリフローがなくても、ブラウザーは再レンダリングを行おうとしてしまう。

JS

CSSで位置固定

#logo {position: absolute;

}

<body><img id="logo" src="imgs/logo.png"> <footer>...</footer>

</body>

var el = document.getElementById("logo");var hidden = false;window.setInterval(function() {

hidden = !hidden;el.style.display =

hidden ? "none" : "block";}, 1000);

JS

CSS

画像のロード

img要素のloadイベント

http://www.w3.org/TR/html5/embedded-content-0.html#the-img-element

HTML5仕様曰く:

If the download was successful and the user agent was able to determine the image's width and height, [...] fire a simple event named load at the img element.

ダウンロードが成功し、ユーザーエージェントが画像の幅と高さを判定できたら、[...] img要素で load という名前のシンプルなイベントを発出します。

loadイベントはレンダリング完了を表しているわけではない

組込機器では、loadイベントの発生と、実際にレンダリングが完了したタイミングの差が大きく出る

巨大な画像のロード直後の処理

• img要素のloadイベント発生直後に重い処理をするとペイント処理とかぶってしまいブラウザーが固まる場合がある

• ペイントが完了したというイベントは取れない

• 手を抜くなら、こうするしかない...

imgElement.onload = function() {window.setTimeout(function() {

// 何か次の処理}, 100);

};

JS

ビデオのロード

組込機器には大敵なビデオデータ

• 再生開始タイミングのパフォーマンスを気にするなら<video preload="auto">• たとえ再生されなくてもバッファリング分のメモリーを消費

• 複数のビデオを事前に用意するのは厳しい

• メモリーにやさしい方法を選ぶなら<video preload="none">• 再生開始のタイミングがかなり遅れる

• どうしても事前にビデオの寸法と尺を知りたいなら<video preload="metadata">

MP4の再生開始パフォーマンス

• ファイルの最後にメタ情報が格納されている• これによって再生開始が大幅に遅れる

• ブラウザーの挙動1. ファイルの先頭を読み取る

2. メタ情報がないと判定し、ファイルの末尾を読み取る

3. ファイルの先頭に戻ってバッファリング分のビデオデータをダウンロードする

• エンコード時にFast Startを有効にする

• メタ情報がファイルの先頭にセットされる

• ブラウザーの読み取り回数が1回で済む

• 再生開始パフォーマンスが向上する

Fast Start

メモリー消費

メモリーリークの確認

Heap、Documents、Nodes、Listenersすべての推移の確認が重要

ノコギリ型のメモリー消費

• ガベージコレクションは負荷が高いためアニメーションを妨げる

• 一般的にノコギリ型のメモリー消費は良くないと言われるが、それはデバイス環境とユースケース次第

• メモリーが少ない環境では、早めにガベージコレクションを発生させ、ピークを抑えるほうが良い場合もある

prototype

prototypeを使わない場合

var MyWallet = function(init_price) {this.price = init_price;this.earn = function(price) {

this.price += price;if(this.price > 1000) {

this.pay(this.price - 1000);}

};this.pay = function(price) {

this.price -= price;};this.look = function() {

return this.price;};

};

JS

prototypeを使った場合

var MyWallet = function(init_price) {this.price = init_price;

};MyWallet.prototype.earn = function(price) {

this.price += price;if(this.price > 1000) {

this.pay(this.price - 1000);}

};MyWallet.prototype.pay = function(price) {

this.price -= price;};MyWallet.prototype.look = function() {

return this.price;};

JS

メモリー消費に違いが出る(極端な例)

var list = [];for( var i=0; i<100000; i++ ) {

var w = new MyWallet(0);list.push(w);

}

prototypeを使わない場合 prototypeを使った場合

62MB 32MB

JS

DOMアクセス

JavaScriptライブラリー

• 使わない機能もテンコ盛り

• 使わなくてもメモリーに展開されてしまう

• できる限り機能を限定したライブラリーが良い

• おすすめのJSライブラリーは...

http://vanilla-js.com/

全機能にチェックを入れても、gzipでたったの25バイト展開したら、なんと0バイト

みなさん、もうお気づきですね

Vanilla JS はジョークサイトです

DOM 操作は素で書くのが最もパフォーマンスに優れます

まとめ

• 流行りのライブラリーやプログラミング手法が良いとは限らない• メモリーが潤沢な環境を前提としていないかを考えるべき

• レガシーな手法も見直そう• メモリーが少ない時代に考えられた手法は今なお組込デバイスでは有効

ご清聴ありがとうございました

@futomi futomi.hatano