<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:series="http://organizeseries.com/"
	>

<channel>
	<title>パフォーマンスチューニング &#8211; HTML5Experts.jp</title>
	<atom:link href="/series/performance-tech/feed/" rel="self" type="application/rss+xml" />
	<link>https://html5experts.jp</link>
	<description>日本に、もっとエキスパートを。</description>
	<lastBuildDate>Sat, 07 Jul 2018 03:14:05 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.7.19</generator>
	<item>
		<title>Navigation Timingだからできる、Webアプリを俯瞰したパフォーマンス計測(3/3)</title>
		<link>/furoshiki/6335/</link>
		<pubDate>Fri, 09 May 2014 00:00:31 +0000</pubDate>
		<dc:creator><![CDATA[川田寛]]></dc:creator>
				<category><![CDATA[システム開発]]></category>
		<category><![CDATA[Navigation Timing]]></category>
		<category><![CDATA[パフォーマンス]]></category>

		<guid isPermaLink="false">/?p=6335</guid>
		<description><![CDATA[連載： パフォーマンスチューニング (11)いよいよHTMLドキュメントのダウンロードと、画面上への表示です。この動作の仕組みから、TATの「終了」がいつなのかを追ってみましょう。「終了」についてはかなり奥が深く、Web...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/performance-tech/" class="series-149" title="パフォーマンスチューニング" data-wpel-link="internal">パフォーマンスチューニング</a> (11)</div><p>いよいよHTMLドキュメントのダウンロードと、画面上への表示です。この動作の仕組みから、TATの「終了」がいつなのかを追ってみましょう。「終了」についてはかなり奥が深く、Web標準でもそのプロセスを「<a href="http://www.w3.org/TR/html5/syntax.html#the-end" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">The End</a>」として定義はしていますが、それだけでは語りきれない難しさがあります。</p>

<p><a href="https://html5experts.jp/wp-content/uploads/2014/05/05.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2014/05/05.png" alt="05" class="alignnone size-medium wp-image-6353" srcset="/wp-content/uploads/2014/05/05.png 640w, /wp-content/uploads/2014/05/05-300x262.png 300w, /wp-content/uploads/2014/05/05-207x180.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></a></p>

<h2>4. TCP接続、5. HTTPリクエスト/レスポンス</h2>

<p>名前解決で得られたIPアドレスを使って、HTMLドキュメントが置かれたサーバへ「4. TCP接続」します。いわゆる、<a href="http://ja.wikipedia.org/wiki/3%E3%82%A6%E3%82%A7%E3%82%A4%E3%83%BB%E3%83%8F%E3%83%B3%E3%83%89%E3%82%B7%E3%82%A7%E3%82%A4%E3%82%AF" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">3ウェイハンドシェーク</a>というものです。サーバとの接続交渉が終わると、HTTPリクエストをサーバへ送信し、HTTPレスポンスを受け取ります。</p>

<p>HTTPレスポンスの結果からリダイレクトであることが発覚した場合、「2. キャッシュ確認」からやり直しです。この、リダイレクト動作にかかった時間もNavigation Timingでは取得でき、「<a href="http://www.w3.org/TR/navigation-timing/#dom-performancetiming-redirectstart" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">redirectStart</a>」「<a href="http://www.w3.org/TR/navigation-timing/#dom-performancetiming-redirectend" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">redirectEnd</a>」の各プロパティを通じて取得することができます。</p>

<p>HTTPレスポンスから得られたデータは、いったん蓄積され、この後のDOMの生成で利用されることになります。</p>

<h2>6. DOM生成</h2>

<p>さて、ここからがブラウザの一番の見せ場、サーバから受信したHTMLドキュメントからDOMを生成し、描画を行うフェーズです。</p>

<p>HTML5のスペック「<a href="http://www.w3.org/TR/html5/syntax.html#parsing" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">8.2章 Parsing HTML documents</a>」に内部的な動作が定義されており、これに従い計測値が取得されます。このフェーズで意識すべきなのが、「①. HTMLドキュメント自体の読み込みを行っている段階(domLoading)」と「②. HTMLドキュメントの読み込みが完了し非同期に取得できる画像などのリソースを読み込んでいる段階(domInteractive)」の、2段階のステージです。</p>

<p>1段回目、domLoadingの段階。HTMLドキュメントの読み込みには、8KBというマジックナンバーが隠されています。これは、ブラウザがHTMLドキュメントの内容をパースする際に、一括で読み込みを行う単位と考えると良いでしょう。ブラウザはサーバから受信したHTMLドキュメントを、8KBずつ逐次パースし、DOM Contentを作っていきます。</p>

<p>JavaScript/CSSのような即時で反映する必要があるファイルの参照があった場合、HTMLドキュメントの受信は継続しますが、HTMLパース処理は停止し、JavaScript/CSSのコンパイルと実行を優先します。</p>

<p>DOM Contentの生成が終わった状態を、HTML5のスペックでは「interactive」と定義しています。JavaScriptにて「<a href="http://www.w3.org/TR/html5/index.html#events-0" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">DOMContentLoadedイベント</a>」を登録していた場合は、このタイミングで発火します。</p>

<h2>7. リソース読み込み</h2>

<p>ここからは、2段階目の説明に入ります。HTMLドキュメントが全て読み込まれ、画像などの非同期で読み込めるリソースを読み込んでいる段階です。先ほども説明しましたが、Web標準ではこの段階のことをinteractive(対話可能)と呼んでおり、名前の通り、画像の読み込み完了を待たずしてユーザからの入力を受付けることができます。</p>

<p>実は、ブラウザはHTMLドキュメントを読み込みつつリアルタイムに画面上へ表示する(ChromeはHTMLドキュメントのダウンロード完了時に表示する)という実装なため、「6. DOM生成」の段階でも既に入力可能だったりします。どちらをTATの開始と呼ぶべきか、非常に悩ましいところです。</p>

<p>画像の読み込みが完了し、Webページが完成したタイミングで「complete」という状態に推移します。completeへ推移すると、JavaScriptのloadイベントが発火します。ここで再び、ブラウザはJavaScriptの処理を動かすために、入力をブロックさせたりします。場合によっては、この段階でユーザからの入力を受け付けるために必要な、初期化処理を行なったりもします。例えば、jQueryの有名なプラグインであるDatePickerなんかは、loadイベント内で設定することが多く、ここに来るまでフォーカスを当ててもカレンダが表示されません。interactiveなんて言われていますが、実際にはloadイベントを実行するまではTATの終了とは言えないような気もしますね。ここまでくると、もはや終了の概念そのものをどう定義すべきかが、だんだんわからなくなってきました。</p>

<p>loadイベントの処理が完了すると、Navigation Timingで計測できる全ての処理が完了です。</p>

<p>なお、ここまでの過程で、JavaScriptやCSS、画像などの外部ファイルの読み込みを行っていますが、これら単独リソースごとの性能計測は、Web標準として少し扱いが異なります。詳しくは、「<a href="http://www.w3.org/TR/2014/CR-resource-timing-20140325/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Resource Timing</a>」を参照すると良いでしょう。</p>

<h2>何をもってTATとするか？</h2>

<p>ブラウザの仕組みについて一通り紹介したところで、TATを構成する要素が以下に分類できることが理解頂けたでしょう。この中のどこかに開始を作り、どこかに終了を作れば、あなたの求めるTATになるはずです！</p>

<p><a href="https://html5experts.jp/wp-content/uploads/2014/05/08.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2014/05/08.png" /></a></p>

<p>開始候補、終了候補、なんて言葉を使ってみましたが、ここまでくるともはや、TATを取得することに意味があるのか？なんて思われても仕方ありません。実際のところ本当にその通りで、TATだけをピンポイントで取得しても、あまり役に立たなかったりします。</p>

<p>Navigation Timingはそもそも、ナビゲーションの動作の中から、性能のボトルネックを探すためのものです。生きた計測値を得るためのものです。ブラウザはナビゲーションを開始すると、様々なキャッシュを駆使して最小限で動作することを追求し、そして終了に向けて、DOM生成やリソースの取得、JavaScriptの初期実行を行い、最大限のポテンシャルを発揮しようとします。こうしたブラウザのメカニズムの中から、ボトルネックを探さなくてはいけないのですから、Navigation TimingはWebページの特性に合わせて、有用な情報を拾えるようにしなくてはいけません。</p>

<p>リソースの取得については「<a href="http://www.w3.org/TR/2014/CR-resource-timing-20140325/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Resource Timing</a>」、JavaScriptの性能に影響を与えるような処理に対しては、「<a href="http://www.w3.org/TR/hr-time/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">High Resolution Time</a>」のnow()なんかを利用して、計測を行うことが求められるでしょう。</p>

<p>パフォーマンスの改善は、まさに総合競技です。JavaScriptやCSS単独の高速化手法だけ知っててもダメだし、DBのパラメータチューニングだけできてもダメです。JavaやASP.NETやRuby On Railsも、全体から見ればほんの一部でしかありません。DNSやWebサーバ、APミドルなどのインフラも重要な構成要素ですし、私のようにイントラ向けのシステム開発を行なっている方はネットワーク装置の一台まで面倒を見ていることもあるでしょう。最近はクラウドも絡んできたりして、バックプレーンを共有する他社のWebアプリが性能に影響を与えるなんてのも聞いたことがあります。複雑化が進んでおり、ますます全体を見る目が求められているように思えます。</p>

<p>ところが、パフォーマンスの問題にぶつかると、どうしても自分の専門分野で見てしまい、DBだけを良くしようとしたり、JavaScriptだけを高速化しようと先走ってしまうことがあります。</p>

<p>Navigation Timingを入り口にして、生の測定値に基づき分析し、ブレークダウンしてみて下さい。きっとそれは、改善の近道となり、費用対効果の高い手段を見つけるヒントとなるでしょう。そしてWebアプリは、最高のパフォーマンスを得ることができるはずです。</p>

<ul>
<li><a href="https://html5experts.jp/furoshiki/6242/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Navigation Timingだからできる、Webアプリを俯瞰したパフォーマンス計測(1/3)</a></li>
<li><a href="https://html5experts.jp/furoshiki/6299/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Navigation Timingだからできる、Webアプリを俯瞰したパフォーマンス計測(2/3)</a></li>
</ul>
]]></content:encoded>
		
		<series:name><![CDATA[パフォーマンスチューニング]]></series:name>
	</item>
		<item>
		<title>Navigation Timingだからできる、Webアプリを俯瞰したパフォーマンス計測(2/3)</title>
		<link>/furoshiki/6299/</link>
		<pubDate>Thu, 08 May 2014 00:00:27 +0000</pubDate>
		<dc:creator><![CDATA[川田寛]]></dc:creator>
				<category><![CDATA[システム開発]]></category>
		<category><![CDATA[Navigation Timing]]></category>
		<category><![CDATA[パフォーマンス]]></category>

		<guid isPermaLink="false">/?p=6299</guid>
		<description><![CDATA[連載： パフォーマンスチューニング (10)前回の記事でもお伝えしたとおり、ブラウザのメカニズムが、TATの計測を複雑にしています。その事情について、まずTATの「開始」をどこにするかについて考え、紐解いてみましょう。W...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/performance-tech/" class="series-149" title="パフォーマンスチューニング" data-wpel-link="internal">パフォーマンスチューニング</a> (10)</div><p><a href="https://html5experts.jp/furoshiki/6242/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">前回の記事</a>でもお伝えしたとおり、ブラウザのメカニズムが、TATの計測を複雑にしています。その事情について、まずTATの「開始」をどこにするかについて考え、紐解いてみましょう。Web標準で定義するところのブラウザの動作について、少しだけデフォルメさせた処理モデルを使って、全体の流れを解説します。以下の図の赤字はtimingオブジェクト配下にあるプロパティ、数字が振ってある矢印は処理の流れです。</p>

<p><a href="https://html5experts.jp/wp-content/uploads/2014/05/c9e0e6e4f4b94208f34ad39a0948069f.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2014/05/c9e0e6e4f4b94208f34ad39a0948069f.png" alt="スクリーンショット 2014-05-02 5.23.34" class="alignnone size-medium wp-image-6320" srcset="/wp-content/uploads/2014/05/c9e0e6e4f4b94208f34ad39a0948069f.png 640w, /wp-content/uploads/2014/05/c9e0e6e4f4b94208f34ad39a0948069f-300x165.png 300w, /wp-content/uploads/2014/05/c9e0e6e4f4b94208f34ad39a0948069f-207x114.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></a></p>

<h2>0. ナビゲーション開始</h2>

<p>Webページの読み込み動作を、「ナビゲーション」と呼びます。ブラウザは、ネットワーク上のHTMLドキュメントにアクセスすると、Webページを構成する様々な情報を削除(アンロード)します。そして、新しくHTMLドキュメントを読み込み、DOMを構成し、JavaScriptやCSS、画像などのリソースを取得しながらWebページの描画を進めていきます。この一連の動作が、ナビゲーションです。</p>

<p>近年は、「<a href="http://en.wikipedia.org/wiki/Single-page_application" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">SPA(Single-page Application)</a>」と呼ばれるアーキテクチャがあり、DOM操作により画面遷移を擬似的に表現する手法もありますが、この仕組みではナビゲーションを開始できません。あくまで、JavaScript APIやプラグインの力を借りず、ブラウザの機能を使ってWebページを遷移させることで、ナビゲーションは開始されます。</p>

<p>ナビゲーションは、ハイパーリンクのクリックもあれば、Formを使ってサブミットすることでも発生します。戻る/進むボタンやリロードボタンを押下した際も、同様です。こうした、様々なナビゲーションを発生させるアクションは、ブラウザのキャッシュの動作、ナビゲーション全体の動作への影響が大きいため、<a href="http://www.w3.org/TR/navigation-timing/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Navigation Timing</a>を扱う際に理解することが求められます。</p>

<p>Web標準では、こうしたナビゲーションの種類を「<a href="http://www.w3.org/TR/navigation-timing/#sec-navigation-info-interface" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">PerformanceNavigation</a>」として以下のように分類しています。まずは、この定義を中心としてその違いを把握してみましょう。</p>

<ul style="border: silver 1px solid;padding:10px">
<li><a href="http://www.w3.org/TR/navigation-timing/#dom-performancenavigation-typenavigate" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">TYPE_NAVIGATE</a> : ハイパーリンクのクリック、ブックマーク等。</li>
<li><a href="http://www.w3.org/TR/navigation-timing/#dom-performancenavigation-typereload" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">TYPE_RELOAD</a> : ブラウザのリロードボタン押下。</li>
<li><a href="http://www.w3.org/TR/navigation-timing/#dom-performancenavigation-typebackforward" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">TYPE_BACK_FORWARD</a> : ブラウザの戻る/進むボタン押下。</li>
<li><a href="http://www.w3.org/TR/navigation-timing/#dom-performancenavigation-typereserved" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">TYPE_RESERVED</a> : 未定義のWebページ読み込みアクション。</li>
</ul>

<p><a href="https://html5experts.jp/wp-content/uploads/2014/05/04a.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2014/05/04a.png" alt="04a" class="alignnone size-medium wp-image-6310" srcset="/wp-content/uploads/2014/05/04a.png 546w, /wp-content/uploads/2014/05/04a-300x141.png 300w, /wp-content/uploads/2014/05/04a-207x97.png 207w" sizes="(max-width: 546px) 100vw, 546px" /></a></p>

<p>「TYPE_NAVIGATE」は、ハイパーリンクをクリックした際や、フォームをサブミットした際に発生する、一般的なナビゲーション動作です。ナビゲーションバーへURLを入力しEnterキーを押下した際、ブックマークを開いた際もこの動作であり、基本はこのアクションが選択されると想定すべきでしょう。後に説明する、「<a href="http://www.ietf.org/rfc/rfc2616.txt" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">RFC 2616</a>」のHTTP Cacheが最大限に活用されるのも、このナビゲーション動作です。</p>

<p>「TYPE_BACK_FORWARD」は、基本的には「戻る/進む」ボタンを押下した際に発生します。Web標準で言うところの「<a href="http://www.w3.org/TR/html5/browsers.html#history-traversal" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">履歴移動(History Traversal)</a>」により発生したナビゲーションです。ただ、「<a href="http://www.w3.org/TR/html5/browsers.html#scroll-to-fragid" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">フラグメント識別子(Fragment Identifier)</a>」を利用したURLを扱った場合、ナビゲーションを伴わないWebページ内の移動を行うことがあります。また、Webページが一定の条件を満足すると、ナビゲーション発生時に、前のWebページ全体をそのままキャッシュさせることで、戻るボタンのパフォーマンスを改善しようとします。このような動作を行なった場合、戻るボタンを押下してもナビゲーションは発生しません。</p>

<p>「TYPE_RELOAD」は、「リロードボタン(再読み込み)」を押下した際に発生します。ブラウザによってCtrl/Shiftとリロードボタンを同時押しすることで、HTTP Cacheの一切を破棄しWebページの再読み込みを行う「スーパーリロード(強制再読み込み)」と呼ばれる機能があります。Navigation Timingではこれを区別することなく「TYPE_RELOAD」として扱いますが、実際のキャッシュの動作は大きく異なるものとなるため、明確に区別する必要があります。</p>

<p>「TYPE_RESERVED」については未定義であり、ブラウザでWebページを扱っている範囲では、あまり意識する必要はありません。</p>

<h2>1. アンロード</h2>

<p>ナビゲーションが開始され、標準的なブラウザはまず「1. <a href="http://www.w3.org/TR/html5/browsers.html#unloading-documents" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">アンロード</a>」という処理を行います。ブラウザは、今表示しているWebページのリソースを破棄し、次のWebページを表示する準備をするわけです。DOMの情報を持つDOM Contentや、JavaScriptの機能の初期化を行います。</p>

<p>Web標準では、この処理を「<a href="http://www.w3.org/TR/html5/webappapis.html#handler-window-onunload" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">unloadイベント</a>」として捕捉でき、Navigation Timingを利用してイベント発生前後の時間を取得することができます。ただ、<em>このタイミングは常に一定しているとは限りません。<sup>(※ <a href="#01" data-wpel-link="internal">注１</a>)</sup></em>表面的には、アンロードはこのタイミングで発生しているように見せてはいますが、実装ではブラウザのレンダリングエンジンによってバラツキがあり、この処理の負荷が、この後説明するDOM読み込みのタイミングに大きな影響を与えることがあります。</p>

<p>また、「戻る/進む」ボタンを押下した場合の動作も独特です。先ほども少し説明しましたが、Webページは一定の条件を満足すると、アンロードを行わずメモリ上にWebページをまるごとキャッシュさせます。この動作は、例えばIE11だと<a href="http://msdn.microsoft.com/ja-jp/library/ie/dn265017(v=vs.85).aspx" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">デベロッパーセンター</a>、Firefoxだと<a href="https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">MDN</a>のドキュメントに仕組みが掲載されており、ブラウザごとにやや異なる思想を持っていることも把握できるはずです。HTML5の「<a href="http://www.w3.org/TR/html5/browsers.html#event-pageshow" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">pageshow</a>/<a href="http://www.w3.org/TR/html5/browsers.html#event-pagehide" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">pagehide</a>」イベントで捕捉でき、ある程度の制御が可能なわけですが、基本的にこの動作、ナビゲーションが発生しないのですから、Navigation Timingのプロパティの値も更新されません。</p>

<h2>2. キャッシュ確認</h2>

<p>標準的なブラウザがアンロードを終わらせると、「2. キャッシュ確認」を行います。Webページの表示に必要なHTMLドキュメントを、キャッシュの中から探すわけです。キャッシュと言っても、ブラウザのみで有効性判断ができるものあれば、サーバに問い合わせを行わないと判断できないものもります。ここでチェックされるのは、あくまで前者のみです。HTML5の「<a href="http://www.w3.org/TR/2011/WD-html5-20110525/offline.html" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">AppCache</a>」や、「<a href="http://www.ietf.org/rfc/rfc2616.txt" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">RFC 2616</a>(13.5)」で定義されたHTTPヘッダのプロパティ「Expires(14.2)」「Cache-Control max-age(14.9)」によるHTTP Cacheなんかは古くから活用されているため、ご存知の方も多いでしょう。</p>

<p>AppCacheは、常にこの段階で読み込みが行われます。しかし、HTTP Cache(Expires)により得られるゆるいキャッシュは、TYPE_NAVIGATEの場合のみ、ブラウザ内のキャッシュを利用します。この仕様は動作チェックの際に問題となることがあり、誤ってリロードボタンを押下してテストしようものなら、テスト担当者は一日を無駄に過ごすことになります。(※ アドレスバーにフォーカスを当ててEnterキーを押下、がキャッシュのテストの鉄則です！)</p>

<p>Chromeだけは、やや動作が例外的です。Chromeは、HTMLドキュメント内から参照されるJS/CSS/画像ファイルのようなリソースでも無い限り、HTTP Cacheによるブラウザ内のキャッシュを利用してくれません。XHR(XMLHTTPRequest)についても同様で、Chromeだけは必ずサーバへのアクセスを伴います。そもそも、Webページはサーバサイドのプログラムで動的に生成するのが殆どなので、HTMLドキュメントのキャッシュは期待できないものと見なすべきでしょう。</p>

<h2>3. 名前解決</h2>

<p>標準的なブラウザは、有効なキャッシュを発見できなかった場合、HTMLドキュメントが置かれているサーバの場所を特定するため「3. 名前解決」を行います。</p>

<p>名前解決については、Linux/OS X/iOS上のブラウザについては、基本的にDNS名として扱い名前解決を行います。しかし、Windows OS上のブラウザは、微妙に同じ動作にはなりません。Windows OS上のブラウザは、ドメイン名をフルコンピュータ名というやや特殊な概念として扱い、<a href="http://technet.microsoft.com/ja-jp/library/cc728142" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">NetBIOS(WINS)名とDNS名の2つの名前の概念を扱うハイブリッドな名前解決を行います</a>。</p>

<p>これは、ブラウザの仕様というより、OSの持つTCP/IPモジュールの仕様の違いです。IEとFirefox/Chromeとで異なるAPIをコールしていますが、どちらも最終的には同じ<a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms740673(v=vs.85).aspx" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">WinSock2</a>の名前解決のメカニズムを利用します。名前解決を行うリゾルバにも、OSによる動作の違いがありますが、DNSを使っている限り、機能面において、ブラウザからはあまり意識する必要がありません。</p>

<p>インターネット向けでは、各プロバイダが提供するDNSサーバの性能が関わってくることもあります。DNSもやはり、キャッシュが要です。DNSを構成する各ノードのキャッシュの動作次第で、性能は大きく変化します。TTL設定のチューニング、DNS Prefetchを活用して対策してみると良いでしょう。</p>

<h2>キャッシュだらけ！</h2>

<p>ナビゲーションの動作のうち、どこをTATの開始とすべきか？もうお分かりと思いますが、キャッシュだらけで、前提条件から疑いにかからなくてはいけません！Webページ自体の(アンロード絡み)のキャッシュ、HTML5 AppCacheやRFC 2616によるブラウザ側のキャッシュ、名前解決絡みのキャッシュと、まだWebサーバにアクセスしていないというのに、これだけのキャッシュが絡んでいます。</p>

<p>最初にご紹介した「navigationStart」は、こうした前の画面の動作を含んだ計測値です。アンロードや、この後説明するリダイレクトを含めるとなると、開始をどこに置くのかというのもまた難しくなってきます。</p>

<p>TATの本当に難しさはここだけではありません。TATの「終了」の扱い、これも難しかったりします。ブラウザのメカニズムを掘り下げて追ってみましょう。</p>

<p>「Navigation Timingだからできる、Webアプリを俯瞰したパフォーマンス計測(3/3)」に続きます。(※5/9公開予定)</p>

<ul>
<li><a href="https://html5experts.jp/furoshiki/6242/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Navigation Timingだからできる、Webアプリを俯瞰したパフォーマンス計測(1/3)</a></li>
<li><a href="https://html5experts.jp/furoshiki/6335/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Navigation Timingだからできる、Webアプリを俯瞰したパフォーマンス計測(3/3)</a></li>
</ul>

<div style="line-height:110%"><a name="01" data-wpel-link="internal"></a><small>※注1 : 実装では、Chromeのアンロードはこのタイミングには行われていません。気になる方は、Chronium35であれば<a href="https://chromium.googlesource.com/chromium/blink/+/master/Source/core/loader/DocumentLoader.cpp" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">./core/loader/DocumentLoader.cpp(startLoadingMainResource)</a>を確認してみると良いでしょう。PerformanceTiming::markNavigationStart()の後にPerformanceTiming::markFetchStart()を呼び出しており、アンロードは遅延して実行していることが読み取れます。なお、Chromeのアンロードは、HTMLドキュメントのHTTP受信完了時に発生します。このあたりは、<a href="https://chromium.googlesource.com/chromium/blink/+/master/Source/core/loader/DocumentLoader.cpp" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">./core/loader/DocumentLoader.cpp(finishedLoading)</a>あたりが動作を理解するのに適しており、commitIfReady()の実装を掘っていくと、./core/loader/FrameLoader.cpp FrameLoader::commitProvisionalLoad→FrameLoader::closeURL…と進み、最終的にunloadイベント発火のタイミングをマークしている処理に到達します。</small></div>
]]></content:encoded>
		
		<series:name><![CDATA[パフォーマンスチューニング]]></series:name>
	</item>
		<item>
		<title>Navigation Timingだからできる、Webアプリを俯瞰したパフォーマンス計測(1/3)</title>
		<link>/furoshiki/6242/</link>
		<pubDate>Fri, 02 May 2014 03:00:11 +0000</pubDate>
		<dc:creator><![CDATA[川田寛]]></dc:creator>
				<category><![CDATA[システム開発]]></category>
		<category><![CDATA[Navigation Timing]]></category>
		<category><![CDATA[パフォーマンス]]></category>

		<guid isPermaLink="false">/?p=6242</guid>
		<description><![CDATA[連載： パフォーマンスチューニング (9)こんにちは、html5jパフォーマンス部、スタッフの川田です。5月8日ですが、私たちのコミュニティ主催で勉強会を開催することになりました！！ そして、残念なお知らせです。募集した...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/performance-tech/" class="series-149" title="パフォーマンスチューニング" data-wpel-link="internal">パフォーマンスチューニング</a> (9)</div><p>こんにちは、html5jパフォーマンス部、スタッフの川田です。5月8日ですが、私たちのコミュニティ主催で<a href="http://atnd.org/events/50159" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">勉強会</a>を開催することになりました！！</p>

<p><a href="http://atnd.org/events/50159" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2014/05/aeaf4d319ffa73eab4f8b831cc4b1850.png" alt="スクリーンショット 2014-05-02 3.49.38" class="alignnone size-medium wp-image-6268" srcset="/wp-content/uploads/2014/05/aeaf4d319ffa73eab4f8b831cc4b1850.png 640w, /wp-content/uploads/2014/05/aeaf4d319ffa73eab4f8b831cc4b1850-300x156.png 300w, /wp-content/uploads/2014/05/aeaf4d319ffa73eab4f8b831cc4b1850-1024x535.png 1024w, /wp-content/uploads/2014/05/aeaf4d319ffa73eab4f8b831cc4b1850-207x108.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></a></p>

<p>そして、残念なお知らせです。募集した直後に、席が埋まってしまいました！せっかく、これだけ多くの方にパフォーマンスへ興味を持っていただいたのに、このままじゃもったいない！</p>

<p>なので本記事では、この勉強会で語られない、別の切り口からパフォーマンスについて語ってみようかと思います。内容は、私が技術評論社「<a href="http://gihyo.jp/magazine/SD" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Software Design 2014年5〜7月号</a>」にて連載しております、「Web標準技術で行う、Webアプリのパフォーマンス改善」で扱っているテーマの一部を、Web向けに書き直したものです。</p>

<h2>Webアプリのパフォーマンス計測は簡単なのか？</h2>

<p>パフォーマンスは、メモリを節約するものからCPUコストを下げるものまで、様々な種類があります。本記事では「ターンアラウンドタイム(TAT)」という指標値について考えてみましょう。</p>

<p><a style="float:left;margin-right:30px" href="/wp-content/uploads/2014/05/00.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2014/05/00-300x168.png" alt="00" width="300" height="168" class="alignnone size-medium wp-image-6245" srcset="/wp-content/uploads/2014/05/00-300x168.png 300w, /wp-content/uploads/2014/05/00-207x116.png 207w, /wp-content/uploads/2014/05/00.png 502w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>

<p>TATとは、アプリケーションの応答速度を表す指標の一つです。厳密には、あるアプリケーション・サーバに対して、ユーザが何かしらの操作・入力を行ってから、アプリが反応し終わり、ユーザが再び入力を受け付けることができるような状態になるまでの時間、と定義されています。Webページが遷移する時間を、定量的に評価できる指標値ということになります。</p>

<p>このTATという考え方、そんなにマニアックなものというものでもなく、レスポンスタイムやスループット、アベイラビリティなどと同様に、古くから存在する非機能要求の項目です。IPAの情報処理試験でもよく見かける、有名なキーワードに思えるでしょう。ところが、これをWebを検索しても、なかなかしっくりくる計測手段が見つかりません！この問題、特にWebに限定した話というものでもないはずです。GUIレベルでの性能計測は、昔からある悩みの一つで、アナログにストップウォッチの力を借りるなんてことも割とよく聞く話です。筆者もIE6全盛のWebアプリ開発で、同じ悩みを抱えたことがあります。</p>

<p>今回、そんなTATをめぐる悩みに対して、ちょっとしたヒントをご紹介します。ブラウザの仕組みを探り、WebアプリのTATとは何かを探り、そして標準的な手段の中からその計測方法について探ってみましょう。「そもそも、そんな指標で評価することに価値があるのか！？」なんて議論も生じていたりするのですが、その点は一番最後に触れるとして、今回はTATという切り口からWebページの性能について考えてみます。</p>

<h2>TATの計測で、Webサーバのログはアテになるのか？</h2>

<p>SPDYなどの新しいプロトコルのことは一旦忘れて、シンプルなHTTP1.Xを前提に考えてみて下さい。HTTP1.Xは、HTMLドキュメント取得し、HTMLドキュメント内で参照されている必要なJavaScript/CSS/画像などの外部リソースを、逐次取得し表示していくという仕組みです。サーバサイドで動的にHTMLを生成するようなアプリだと、画面を遷移する都度、最低でもHTMLドキュメント程度はサーバ側から取得することになります。</p>

<p>この動きに注目し、計測で非常によく利用されるのが、Webサーバログの「リクエスト処理時間(Time taken to process the request)」です。Apache HTTP Serverのアクセスログのフォーマットオプション「<a href="http://httpd.apache.org/docs/2.2/mod/mod_log_config.html" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">%D</a>」あたりは有名でしょう。このオプションは、HTTPリクエストを受け付けた瞬間から、サーバで処理が行われ、データを出力し終わるまでの時間を、マイクロ秒単位で計測する機能です。<a href="https://tomcat.apache.org/tomcat-8.0-doc/config/valve.html#Access_Log_Valve/Attributes" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Tomcat</a>や<a href="http://docs.jboss.org/jbossweb/7.0.x/config/valve.html" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">JBoss</a>のようなJavaミドル、Microsoftの<a href="http://msdn.microsoft.com/ja-jp/library/cc338080.aspx#W3CExtendedLoggingDefinitions" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">IIS(Internet Information Services)</a>でも、リクエスト処理時間をミリ秒で計測できる機能を実装しており、大抵のWebアプリでは活用できるコモディティ化した機能と言えます。</p>

<p>リクエスト処理時間は、実際にサーバ上の問題を発見するのに価値が高い計測値です。運用時には、キャパシティプランニングの目安や、障害発生時の解析に活用されたりします。また、お客様と品質保証の明確な基準を決める「<a href="http://ja.wikipedia.org/wiki/%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E6%B0%B4%E6%BA%96%E5%90%88%E6%84%8F" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">SLA(Service-Level Agreement)</a>」でも、アクセスの何％を保障するかという「<a href="http://ja.wikipedia.org/wiki/%E5%88%86%E4%BD%8D%E6%95%B0#.E3.83.91.E3.83.BC.E3.82.BB.E3.83.B3.E3.82.BF.E3.82.A4.E3.83.AB" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">パーセンタイル</a>」を配慮した形で活用することがあります。どこにでも目にするこの計測値、一見すると使い道が広く、万能な値に見えるでしょう。</p>

<p>ところが残念なことに、リクエスト処理時間は、キャッシュされていないごく一部のファイル/プログラム単位の性能しか評価できません。サーバがどの程度のリクエストを捌けるのかを判断する「<a href="http://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%AB%E3%83%BC%E3%83%97%E3%83%83%E3%83%88" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">スループット(＝処理能力)</a>」の指標値として扱うには高い価値を発揮しますが、TATの指標値として扱うには、対象が局所的で、不足が大きすぎます。サーバから見ると性能は高いのに、クライアントで見ると画面はいつになっても白いまま！なんてことになりかねません。</p>

<p><a href="https://html5experts.jp/wp-content/uploads/2014/05/01.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2014/05/01.png" alt="01" class="alignnone size-medium wp-image-6289" srcset="/wp-content/uploads/2014/05/01.png 640w, /wp-content/uploads/2014/05/01-300x146.png 300w, /wp-content/uploads/2014/05/01-1024x501.png 1024w, /wp-content/uploads/2014/05/01-207x101.png 207w" sizes="(max-width: 640px) 100vw, 640px" /></a></p>

<p>では、TATを計測するにはどうすればよいのでしょうか？画面を表示する際のもっさり感の原因は、どのように探ればよいのでしょうか？このあたり、最近のWeb標準がかなり力を入れています。2012年12月にW3C勧告となった「<a href="http://www.w3.org/TR/navigation-timing/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Navigation Timing(ver.1)</a>」がまさにその代表的なスペックです。</p>

<p>Navigation Timingを含むWebのパフォーマンスに関する仕様は、2010年にW3Cで設立された「<a href="http://www.w3.org/2010/webperf/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Web Performance Working Group(WebPerf WG)</a>」で議論され、IE9+、Firefox、Chromeなどのブラウザにその成果が反映されています。Facebookのザッカーバーグ氏がHTML5に失望し、同社のアプリをネイティブに書き換えるという有名な事件がありますが、そんな彼らも同WGの設立直後すぐに反応し、WGの議論に参加しています。2013年には「Web vs ネイティブ」という議論が盛り上がりましたが、WebPerf WGの取り組みは、現在のWebが抱える性能という問題を見つめていく上で、注目すべき重要なポイントと言えるでしょう。</p>

<h2>Navigation Timingの使い方、TATの計測方法</h2>

<p>Navigation Timingでは、ハイパーリンクをクリックした瞬間からWebページが利用できるようになるまでを、ブラウザの内部的な動作である「<a href="http://www.w3.org/TR/navigation-timing/#processing-model" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">処理モデル(Processing Model)</a>」として定義し、その経過時間を取得することができます。</p>

<p>ブラウザ自体にも「開発者ツール」として計測のためのツールが備わっていますが、ブラウザごとに考え方も違うし、活用できる場も開発時のみと非常に限定的です。Navigation TimingはWeb標準なので、ブラウザに依存しない共通の考え方で計測値を扱え、また運用時にもJavaScript上で生のデータを収集することができる点で、開発者ツールより高いポテンシャルを得ています。</p>

<p>実際のJavaScriptの仕様を見ながら確認してみましょう。</p>

<p><a href="https://html5experts.jp/wp-content/uploads/2014/05/4e7e8507cc1b623c22b6842557767406.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2014/05/4e7e8507cc1b623c22b6842557767406.png" alt="02" width="300" height="99" class="alignnone size-medium wp-image-6295" /></a></p>

<p>windowオブジェクト配下にある「performance」は、パフォーマンス関連のJavaScript APIにアクセスするための入り口となるオブジェクトです。そのさらに下に、WebPerf WGの成果である様々なスペックの実装が分類別で並んでいます。そのうちのひとつである「timing」の配下には、ハイパーリンクをクリックしてから応答するまでの間に発生する、様々な動作発生時のタイムスタンプを取得できるプロパティが並んでいます。</p>

<p>TATを取得しコンソールに表示させる、シンプルなコードを確認し、使い方を掴んでみましょう。</p>

<p></p><pre class="crayon-plain-tag">/* timingオブジェクトの取得 */
var timing = ( window.performance || {} ).timing;
/* TATの計測 */
timing &amp;&amp; window.addEventListener( "load", function() {
    setTimeout( function() {
        var tat = timing.loadEventEnd - timing.navigationStart;
        console.log( "TAT = " + tat );
    },1);
},false);</pre><p></p>

<p>「timing.navigationStart」はハイパーリンクがクリックされた直後の時間、「timing.loadEventEnd」はWebページが表示されJavaScriptのloadイベントの処理が完了した直後の時間が取得できるプロパティです。サンプルでは、この2つの単純な差分を、仮にTATとしてみました。setTimeoutで実行時間を遅延させているのは、loadEventEndがloadイベント中だと未計測の状態「0」を返してしまうという問題への対策です。</p>

<p>このサンプルで得られる計測値が、TATとして「ほぼ正解」の値ということになります。ただ、ほぼ正解であって、完全な正解とは言えません。このあたりの理由については、ブラウザの内部のメカニズムによるところが大きかったりします。具体的には何が原因なのか、これから見ていきましょう。</p>

<ul>
<li><a href="https://html5experts.jp/furoshiki/6299/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Navigation Timingだからできる、Webアプリを俯瞰したパフォーマンス計測(2/3)</a></li>
<li><a href="https://html5experts.jp/furoshiki/6335/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Navigation Timingだからできる、Webアプリを俯瞰したパフォーマンス計測(3/3)</a></li>
</ul>

<p><em>※ 修正 2014.5.11：<a href="https://html5experts.jp/myakura/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">矢倉 眞隆</a>さんからのご指摘により、サンプルのwindow.performanceのベンダプレフィクスは削除しました。IE9+を含む近年のブラウザでは、3年近く前にベンダプレフィクスが消えており、無意味であると判断できたためです。</em></p>
]]></content:encoded>
		
		<series:name><![CDATA[パフォーマンスチューニング]]></series:name>
	</item>
		<item>
		<title>Webアニメーションを高速化するために知っておくべき10のこと（後編）</title>
		<link>/cssradar/2669/</link>
		<pubDate>Thu, 26 Sep 2013 22:00:07 +0000</pubDate>
		<dc:creator><![CDATA[斉藤 祐也]]></dc:creator>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[アニメーション]]></category>
		<category><![CDATA[パフォーマンス]]></category>

		<guid isPermaLink="false">/?p=2669</guid>
		<description><![CDATA[連載： パフォーマンスチューニング (8)前編から引き続き、後編でも最適化のために知っておきたいレンダリングプロセス、計測方法、そして最適化を妨げるよくあるアクシデントとその回避方法について紹介していきます。 アニメーシ...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/performance-tech/" class="series-149" title="パフォーマンスチューニング" data-wpel-link="internal">パフォーマンスチューニング</a> (8)</div><p><a href="https://html5experts.jp/cssradar/2027/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">前編</a>から引き続き、後編でも最適化のために知っておきたいレンダリングプロセス、計測方法、そして最適化を妨げるよくあるアクシデントとその回避方法について紹介していきます。</p>

<h2>アニメーションを高速化するために知っておきたいレンダリングプロセス</h2>

<p>ブラウザがどのようにウェブサイトを表示しているのかを知ることは、アニメーションだけに限らず、Webのパフォーマンス全体の高速化を行うために大切なステップです。
イスラエルの開発者であるTali Garsiel氏が公開した『<a href="http://taligarsiel.com/Projects/howbrowserswork1.htm" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">How Browsers Work</a>』は、<a href="http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">HTML5 Rocksに転載</a>され、複数の日本語訳も提供されている、ブラウザの内部動作を学ぶために読んでおきたいリソースの1つです。
そのリソースを参考に、レンダリングエンジンのメインフローについて見ていきましょう。</p>

<p>ブラウザはまずHTMLドキュメントを解析し、HTMLタグをコンテント・ツリーと呼ばれるDOMノードに変換します。そして外部CSSやスタイル要素に含まれるスタイルに関するデータを解析し、レンダーツリーを作成します。<br />
このレンダーツリーが色や大きさのような情報を持つ矩形を持っています。 </p>

<p>ここから先が特にアニメーションのパフォーマンスに関わりの深いフローになりますが、レンダーツリーが作成された後、レイアウト(リフロー)と呼ばれるプロセスに入り、そしてペイントがそれに続きます。</p>

<p>ここでは非常に大まかな流れしか解説しませんが、Paul Irish氏が言うとおり、ブラウザがHTMLやCSSを画面に表示するまでのフローを理解することは、開発におけるベストプラクティスの背後にある根拠を理解する手助けとなります。少々慣れない用語が多いかもしれませんが、ぜひ先ほど紹介した記事を一読ください。</p>

<h2>アニメーション・ボトルネック: レイアウト(リフロー)</h2>

<p>Google ChromeやSafariなどのWebKit系ブラウザではレイアウト、FirefoxなどのGecko系ブラウザではリフローと呼ばれるプロセスは、パフォーマンス向上において大きな障害となるプロセスの1つです。<br />
Satoshi Ueyama氏が<a href="http://gyu.que.jp/sjs2007/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">2007年に『出張 Shibuya.js 24』にて発表した</a>際に使用した以下の動画で見ることができます。様々な大きさの矩形が徐々に配置されていくプロセスがレイアウト/リフローです。</p>

<p><a href="http://youtu.be/ZTnIxIA5KGw" title="Gecko Reflow Visualization" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Gecko Reflow Visualization</a></p>

<p>このレイアウトのプロセスを、どのように最小限に抑えることができるかが、アニメーションの高速化において大切なポイントになります。
レイアウトを引き起こす原因について、詳しくはTony Gentilcore氏による「<a href="http://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">How (not) to trigger a layout in WebKit</a>」を参考にしてください。<br />
記事のタイトルにもある通り、あくまでWebKit系ブラウザのみに限定されていますし、2011年の記事でもありますので、必ずレイアウトの発生を確認できるツールを利用して確認することをおすすめします。<br />
（執筆時点ではGoogle Chrome、Safariの開発者ツール、そして紹介記事から得た情報でしかありませんが、IE11の開発者ツールでもレイアウトの発生を確認できます）</p>

<h2>アニメーション・ボトルネック:ペイント</h2>

<p>やや乱暴ではありますが、ブラウザで何かを表示するということは、HTMLやCSSから得た情報を画像化して表示しているのと大きな差はないと言えます。　</p>

<p>CSS3がプロダクションレベルで利用されはじめた今、角丸やシャドウなどを画像を利用せずに表現することが当たり前になってきているかと思います。読み込む画像が少なくなれば、その画像を制作するコストも減り、画像の読み込みにかかるコストも減りますが、その代わりにブラウザがそのコストを背負ってくれているわけです（もちろん1対1の割合ではありませんが）。<br />
この後の段でアクシデントとして紹介しますが、すべてのスタイルが同じコストでペイントを行うわけではなく、また組み合わせによってコストが甚大になるケースもあるので注意が必要になります。</p>

<p>ブラウザは、表示領域をいくつかのタイルとしてレンダリングを行います。それぞれの個別のタイルごとにレンダリングをし、その結果をキャッシュしていきます。<br />
そのため、大きな領域でペイントを行うことを避ける必要があります。レイアウトと同様に、ペイントを行う範囲を最低限にしておくことがポイントになります。</p>

<h2>アニメーションのパフォーマンスの計測、デバッグツール/ワークフロー</h2>

<p>パフォーマンス最適化において、最も大事なことは計測することです。私自身もよく陥る失敗ですが、どこかで読んだから、以前はうまく回避できた問題だからと盲目的にそれらの情報を頼りにするのではなく、計測する方法をきちんと学び、計測を日々続けることがベストプラクティスです。</p>

<p>開発者ツールについての詳しい記事は今後本サイトで紹介される予定ですので、ここでは私が実際にアニメーションのパフォーマンスについて計測するのに利用しているツールとワークフローを、ほんの一部ですが紹介していきます。</p>

<p>パフォーマンス計測を行う上で欠かすことができないのが、Google Chrome Canaryの開発者ツールです。Chromeではなく、日々更新を続けていくCanaryを利用する理由は、計測ツールとしての機能の多さと深さがChromeとは異なるからです。</p>

<p>アニメーションの最適化という文脈においてタイムラインパネルは、エディタとブラウザの描画エリアと同じくらい見る画面です。 </p>

<p><img src="/wp-content/uploads/2013/09/devtool1.png" alt="devtool" width="668" height="425" class="aligncenter size-full wp-image-2803" srcset="/wp-content/uploads/2013/09/devtool1.png 640w, /wp-content/uploads/2013/09/devtool1-300x190.png 300w, /wp-content/uploads/2013/09/devtool1-207x131.png 207w" sizes="(max-width: 668px) 100vw, 668px" /></p>

<p>まずは上図の1、Framesタブに切り替えて、上図2のRecordボタンをクリックし、計測を行いたいアニメーションを実行します。 </p>

<p><img src="/wp-content/uploads/2013/09/devtool.recording.gif" alt="devtool.recording" width="896" height="491" class="aligncenter size-full wp-image-2671" /></p>

<p>すると、上図のような棒グラフが記録されていきます。その下にある表は棒グラフをより詳細にしたものです。<br />
棒グラフは、背が高ければ高いほど処理に時間がかかっているということになります。計測対象にもよりますが、グラフ上にある2本の線は30FPSと60FPSを示しています。60FPSを目指すのであれば、それ以上の高さになっている棒グラフの周りをドラッグすることで、下にある表にその選択したタイミング内の詳細情報を表示させることができるようになります。</p>

<p>表にある横グラフは、マウスオーバーさせることでその特定の処理についての詳細を見ることができます。<br />
レイアウトやペイントがどの範囲で行われているか、そしてそれにかかっている時間がどのくらいか、などの情報を確認することが可能です。<br />
これらの情報と最前にも紹介したレンダリングの仕組みの情報を踏まえて、トライアル&amp;エラーを繰り返し、最適化を図っていくのが大まかなワークフローとなります。</p>

<h2>アニメーションを遅くするよくあるアクシデント</h2>

<p>最後にアニメーションのパフォーマンス最適化を妨げる、よくある3つのアクシデントを紹介します。</p>

<p>1つ目は<a href="https://html5experts.jp/cssradar/2027/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">本連載の前編</a>でも少し触れたGPUにまつわるアクシデントです。<br />
GPUを使ってRenderLayerを作成し、そのレイヤー上でアニメーションを行うことそのものはベストプラクティスと言えます。しかしCPUからGPUへのデータ転送にはコストがかかり、特にモバイルデバイスなどCPUもGPUも非力である場面で問題になります。</p>

<p>この問題については、Phamtom.jsの作者としてよく知られるAriya Hidayat氏によるデモが非常にわかりやすいです。デモそのものは<a href="http://codepen.io/ariya/full/xuwgy" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">こちらのcodepen</a>で実際に動作しているので、参考にしてください。 </p>

<p><a href="http://vimeo.com/75182001" title="Ariya Hidayat氏によるGPUへのデータアップロードデモ" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Ariya Hidayat氏によるGPUへのデータアップロードデモ</a></p>

<p>それぞれのボックスの右上に数字があり、カウントされているのが確認できたでしょうか？　このカウントがCPUからGPUへのデータの転送数を示しています。このデータ転送が少ないほうがパフォーマンスはよくなります。<br />
ですが、実際にデータ転送を行わないプロパティは非常に少なく、Ariya Hidayat氏によれば、opacity、transform、filterの3つのみと言われています。<br />
アニメーションの表現としてデータ転送が回避できない場面が多いのも事実ですが、同時にデータ転送には相応のコストがかかることを知っておくことも大切です。</p>

<p>GPU関連でもう1つの落とし穴があります。GoogleのJake Archibald氏による<a href="http://jsbin.com/efirip/5/quiet" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">デモ</a>をご覧ください。 </p>

<p><img src="/wp-content/uploads/2013/09/accidental.layer_.creation.gif" alt="accidental.layer.creation" width="1200" height="660" class="aligncenter size-full wp-image-2672" /></p>

<p>オレンジ色の矩形が、GPUによって処理されるComposited Layerを示しています。実際にComposited Layerを生成したかったのは左上の緑のボックスだけなのにも関わらず、どういうわけか見出しにもComposited Layerが生成されてしまいます。デスクトップ上であれば、このComposited Layerの生成にかかるコストは気にするほどのことでもありませんが、モバイルデバイスにおいては大きなコストになってしまいます。<br />
この問題を回避するのにはシンプルな解決があります。</p>

<p>先ほどのデモ内においてはアニメーションする要素そのものに、z-index: 1を付与することで回避できます。<br />
このアクシデントは、カルーセルのようなUIパターンによく見られます。自作する際も、すでにあるライブラリを利用する際も、思っていない場面でComposited Layerが生成されていないか確認し、z-indexを使って回避できるか試してみてください。</p>

<p>Ariya Hidayat氏によるW3Confでのプレゼンテーション『<a href="http://www.youtube.com/watch?v=gTHAn-nkQnI" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Fluid User Interface with Hardware Acceleration</a>』はハードウェア・アクセラレーションを使った最適化を行うのにあたり、見ておくべきリソースの1つです。またThe Chromium Projectsにてドキュメントされている<a href="http://www.chromium.org/developers/design-documents/gpu-accelerated-compositing-in-chrome" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">GPU Accelerated Compositing in Chrome</a>も参考になりますので、ぜひ一読してください。</p>

<p>ここで紹介する最後のよくあるアクシデントが、CSSプロパティの組み合わせによるペイントコストの増大です。このアクシデントはモバイルデバイスでよく見かけるものです。例えば、角丸とシャドウを組み合わせたボックス、非常によくあるスタイルにも関わらず、これらのスタイルがあると、とたんにスクロールが引っかかるように感じてしまうことがあります。この「引っかかる感じ」の原因の多くがペイントです。</p>

<p>どのプロパティの組み合わせがどれくらいのペイントコストとなるかを計測するためのツールは存在はしますが、ここでは紹介しきれませんので割愛します(チャレンジしてみたい方は<a href="http://dev.chromium.org/developers/how-tos/trace-event-profiling-tool" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">こちらのドキュメント</a>を参考にしてください。また取得方法は異なりますが、Colt McAnlis氏によるHTML5 Rocksの記事『<a href="http://www.html5rocks.com/en/tutorials/speed/css-paint-times/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">CSS Paint Times and Page Render Weight</a>』も合わせて参考にしてください。)。しかし前段で紹介したタイムラインパネルでもペイントコストを確認できますので、トライ&amp;エラーを繰り返すことで、最適化を進められるでしょう。</p>

<p>前後編にわたってWebアニメーションの最適化について紹介してきました。<br />
いずれブラウザや端末そのものも賢くなり、これらの知識やテクニックも早晩時代遅れになっていくだろうと思います。しかし、我々はその過渡期にいる最中です。「使いやすさ」を向上する上で欠かすことができない最適化は多くの苦難があっても報われるものです。</p>
]]></content:encoded>
		
		<series:name><![CDATA[パフォーマンスチューニング]]></series:name>
	</item>
		<item>
		<title>モバイルブラウザでもサクサクに動作する最適化6つのノウハウ</title>
		<link>/sou/2413/</link>
		<pubDate>Thu, 19 Sep 2013 22:00:56 +0000</pubDate>
		<dc:creator><![CDATA[城戸 総史]]></dc:creator>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[パフォーマンス]]></category>
		<category><![CDATA[モバイル]]></category>
		<category><![CDATA[高速化]]></category>

		<guid isPermaLink="false">/?p=2413</guid>
		<description><![CDATA[連載： パフォーマンスチューニング (6)「Webサイト・アプリ高速化テクニック徹底解説」第8回は、モバイルブラウザに向けた最適化について紹介します。 モバイル端末はPCに比べ、CPUやネットワークなどの性能面で劣ること...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/performance-tech/" class="series-149" title="パフォーマンスチューニング" data-wpel-link="internal">パフォーマンスチューニング</a> (6)</div><p>「Webサイト・アプリ高速化テクニック徹底解説」第8回は、モバイルブラウザに向けた最適化について紹介します。
モバイル端末はPCに比べ、CPUやネットワークなどの性能面で劣ることからボトルネックの影響が出やすく、またゲーム開発など突き詰めたチューニングを行う場面では、特殊なノウハウも必要になります。</p>

<p>しかしきちんと最適化を行えば、その効果もその分著しく、比較的低スペックな端末や3G回線であっても、サクサク軽量なサービス提供が可能です。今回の記事では、その勘所をお伝えしたいと思います。
</p>

<p><span id="more-2413"></span></p>

<h2 id="">1. ボトルネックを取り除く</h2>

<p><a href="https://html5experts.jp/wp-content/uploads/2013/09/html5experts_image_Mobile.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/09/html5experts_image_Mobile.png" alt="html5experts_image_Mobile" width="207" height="156" class="alignright size-full wp-image-2451" /></a></p>

<p>
まずはやってしまいがちなボトルネックの事例について、挙げていきたいと思います。前述のようにモバイル端末では、その性能からPCよりも顕著に、未最適化箇所が体感に影響を及ぼします。</p>

<p>
仕事がらそこそこの数のアプリケーションを見てきましたが、モバイル向けに特化したチューニングができていないのではなく、一般に求められる最適化がなされておらず、それがボトルネックとなって遅いといった事例をよく目にします。</p>

<p>
以下では、特にモバイルで問題となる一般的な最適化項目を挙げてみました。個々の詳細な解説については、本連載シリーズの中でより詳細にレポートしていきます。ぜひ、目を通してみてください。</p>

<p>よくありがちな例としては、</p>

<ul>
<li>JavaScriptファイルの取り扱いが最適化されておらず、不要なブロッキングが発生している</li>
<li>適切なレスポンスヘッダが設定されておらず、ブラウザキャッシュが機能していない（もしくはConditional GETのリクエストを飛ばしている）</li>
<li>過剰な数のHTTPリクエストが発生している</li>
</ul>

<p>といったものがあります。</p>

<h3 id="javascript">JavaScriptファイルの取り扱いに注意する</h3>

<p>まず影響が大きいのは、JavaScriptファイルの取り扱いです。<br>
script要素はブラウザの他の処理をブロックします。HTML文中にscript要素があると、ブラウザはパース処理など他の仕事を停止し、script要素の評価を優先して実行します。<br>
普段は様々な仕事が並列で走っているのですが、script要素だけは特別扱いで、これはブラウザにとって最も重い仕事のひとつとなります。もちろん、モバイル端末においても顕著に影響を与えます。</p>

<p>script要素の数を減らすか、非同期化しましょう。<br>
defer、async属性を用いることで、上記のブロッキングがなくなります。<br>
非同期化を行わない（行えない）場合はブロッキングの影響を最小限に抑えるよう配慮します。<br>
具体的にはファイル結合を行い、script要素の数を減らし、またbody要素の最下部に置くなど、ブラウザの処理をできるだけ阻害しないようにします</p>

<h3 id="">ブラウザキャッシュを有効に機能させる</h3>

<p>また、ブラウザキャッシュも重要です。<br>
画像、CSSファイルなどの全アセットがキャッシュに入り、DOMContentLoadedまでにHTTPリクエストが全く発生しない状況では体感速度が大幅に向上します。サクッとファーストビューが表示されるなど、明らかに体感が変わります。
「HTTPリクエストが全く発生しない」ことが重要です。Conditional GET（304）を見かけた場合もきっちり摘み取りましょう。
</p>

<p>
またサーバ側で画像合成を行うなど、動的なアセットを持つ場合にレスポンスヘッダの付け忘れが起こりやすいようです。
こういった動的なアセットはファーストビューに含まれることも多く、キャッシュの可否で体感速度が変わってきます。
反対に毎回動的に生成するようなものは、もしファーストビューなど重要な箇所に含まれないようなものは遅延ロードに回してやるのもよいでしょう。
</p>

<p>
例: cache validator（この場合はLast-Modified）が設定されているので正しくキャッシュされる。<br>
<img src="/wp-content/uploads/2013/09/header_cacheable.png" alt="キャッシュ可能" />
</p>

<p>
例: cache validator に相当するヘッダがなく、キャッシュ不可として扱われる。<br>
<img src="/wp-content/uploads/2013/09/header_uncacheable.png" alt="キャッシュ不可" />
</p>

<h3 id="">HTTPのリクエスト数を削減する</h3>

<p>モバイル端末では、PCよりも顕著にHTTPのリクエスト数が性能面に現れます。</p>

<p>理想はファーストビューの表示までに20本未満程度だと思います。<br>30本以上のリクエスト数を見ると、多いなと感じます。これらにはリクエスト本数を削減するにはスプライトシートを使う、base64 encodeで文字列化して埋め込む、遅延ロードを行う等々、その時々に応じた方法があります。
</p>

<p>上記に挙げたボトルネックはどれもGoogle Page Speed等のツールを用いたチェックが可能です。<br>
特殊なノウハウではなく、検出もしやすく改善もしやすいので、ぜひ活用＆対応してみましょう。
</p>

<h2 id="">2. チューニングの為の知識と準備</h2>

<h3 id="">大まかな流れを掴む</h3>

<p>JavaScriptヘビーなアプリケーションなど通常の最適化だけでは十分に軽量化できないものがあります。<br>
そのような場合は、より突っ込んだ測定と最適化のチューニング作業が必要です。</p>

<p>モバイルでのチューニング作業は、ざっくり例えると買い物に近い感覚です。<br>
まずコンテンツの表示にかけてよい時間、つまり予算を定め、DOMContentLoadedまでの所要時間やJavaScriptの処理時間といったパートごとのコスト計測を行い、予算内にそれらが収まるよう、値切ったり（高速化）、予算外に回したり（遅延評価）といったやりくりを行う、といったノリです。
</p>

<p>まず予算を決めます。<br>
予算を決めるには、そのコンテンツやアプリケーションがユーザの目から見て使えるようになったと思えるポイント、待ち時間が終わったと感覚的に思えるポイントを定めます。<br>
これはコンテンツによって様々です。ファーストビューが表示されるまでの場合もあれば、メインアプリケーション部分をユーザが操作可能になるまでの場合、アニメーションが再生を開始するまでの場合など、そのアプリケーションの特性によって決まります。<br>
ここまでに要する時間をもって、予算として定めます。
</p>

<p>
経験則的には、アセットがブラウザキャッシュに収まった状態で、この感覚的待ち時間が大体800ms以下に収まるとサクサクである、読み込みが軽量であると利用者にも感じてもらえることが多いようです。<br>
大体の予算感の参考にしてください。
</p>

<p>
さて、次に計測を行うのですが、
計測にあたってはターゲット環境の選定が重要になります。<br>
これは端末、OS、ブラウザによって実効性能や動作特徴が変わるので、結果として高速化のために解決するべき問題が環境にひも付いて変わるためです。<br>
ここでは端末、OS、ブラウザ、回線種別の組み合わせを指して「環境」と呼んでいます。特に端末とブラウザの違いについて、下記で見ていきましょう。
</p>

<h3 id="">ターゲット端末を定める</h3>

<p>
モバイルで発生する大半の問題は実はCPUやGPU、もしくはネットワーク性能の向上で簡単に解決できてしまいます。
</p>

<p>
端末性能は日々向上を見せており、今日の課題は明日の課題となり得ないことがあります。<br>
端末の買い換えサイクルは2年程度とよく言われますが、実際にベンチマークを取ると2年前の端末と現在の端末では5～8倍近い性能差が発生します（さらに極端な例もあります）。<br>
本当に解決すべき問題を見定めるためには、その時の状況に即したターゲット端末の選定が重要になります。
</p>

<p>
本原稿の執筆時点は、低スペック端末が世間から消えるか消えないかのちょうど過渡期です。<br>
シェアを広げつつある性能の良い端末に向けて体験も豊かに補強しつつ、多少年数の経った低スペック端末でも円滑にサービスが動作するような、両方取りのチューニングが求められている時期だと思います。</p>

<p>
今回は低スペック端末向けのチューニングも視野に入れましょう。<br>
低スペック端末の代表格としては、例えばiPhone 4があります。iPhone 4はディスプレイサイズに比べてCPU/GPUの性能が十分と言えない、問題を見つけやすいチューニング向きの端末です。<br>
iPhone 4は発売から年数も経過していますが、iOS7がカバーしていることもあり、まだサポートが必要だと思います。<br>
また比較的新しい端末であっても、例えばGalaxy S3など一昨年に発売されたものでも、CPU/GPUの負荷軽減処理が効果的にはたらく低スペック端末に近い挙動のものがあります。<br>
やはりまだ、高速化には端末性能を考慮した作り込みが必要となる、低スペック端末への配慮が必要な時期だと言えるでしょう。
</p>

<h3 id="">ブラウザ間の差異について</h3>

<p>モバイルブラウザは様々な種類が存在しますが、ここではシェアの高いMobile SafariとAndroid Browser、Chrome for Androidといったブラウザに絞って話を進めます。</p>

<p>
Mobile SafariとAndroid BrowserはOSのバージョンによっても挙動に違いがあるため、選定時には気をつけてください。
</p>

<p>
まずブラウザキャッシュの取り扱いは各ブラウザで異なり、体感や最適化に影響を与えます。
</p>

<p>
Mobile Safariはオンメモリのキャッシュしか持たず、このためOSの都合で頻繁なキャッシュアウトが発生しやすく、また本体やブラウザの再起動など、プロセスの寿命に伴ってキャッシュが破棄されます。
</p>

<p>
キャッシュ領域のサイズ自体は100MB前後だと言われていますが、状況によってブレがあり、確定した値を持っていません。<br>
ひとつのサイトを訪れ継続して操作を行う場合はキャッシュ効果を期待できますが、サイトを再訪した時など、時間を置いて訪れた際は、以前のキャッシュはもうないものと思った方がよいでしょう。
</p>

<p>
Android 2.2や2.3といった2系のAndroid Browserでは、キャッシュ領域が8MBとそもそも小さく、キャッシュ効果自体があまり期待できません。<br>
吉報としてはAndroid2系のシェアが下がりつつあることでしょうか。
</p>

<p>
Android 4系のAndroid BrowserやChrome for Androidになると、85MBのファイルキャッシュ（Persistent Cache）を持つため、比較的良好な効果が期待できます。<br>
max-ageやETagといったキャッシュ寿命の設定も、この辺りから効果を持ち始めます。
</p>

<p>
このようにモバイル端末では、キャッシュの取り扱いに諸々問題があります。<br>
より詳しい説明が <a href="http://www.guypo.com/uncategorized/mobile-browser-cache-sizes-round-2/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Guy&#8217;s Podのエントリ</a>に掲載されているため、興味のある方は目を通されるとよいでしょう。</p>

<p>
ほかにもHTTPの同時接続数やJavaScriptの実行パフォーマンスなど細かな点が変わります。<br>
例えばiOS5とiOS6では、局所的なJavaScriptの実行パフォーマンスの違いからアニメーション描画に差異があり、体感的な差異をもたらすといったことが実際にありました。<br>
また、iOS7がリリースされたため、既にiOS5の性能劣化はあまり重要な問題とは言えなくなりました。<br>
このように細かなチューニングや性能測定を行う際はOSバージョン、ブラウザによる性能変化に注意し、その時に適切なものを選ぶよう注意が必要です。
</p>

<h3>計測に際して</h3>

<p>
計測を行う場合はざっくり、以下の三つのパートから計測を開始することが多いでしょうか。<br>
尚、計測を行う際はMobile SafariのWeb Inspectorなど、結果に影響を与えるデバッガ類を走らせないよう注意が必要です。
</p>

<ul>
<li>DOMContentLoadedまでの所要時間を計る</li>
<li>JavaScriptのメインルーチンが呼び出されるまでの所要時間を計る</li>
<li>感覚的待ち時間に該当する関数の実行完了までの所要時間を計る</li>
</ul>

<p>
それぞれの計測区結果を基に、さらに絞り込み問題の検出と改善を進めます。<br>
上記それぞれの所要時間に応じた具体的なチューニング方法を次に解説します。
</p>

<h2 id="domcontentloaded">3. DOMContentLoadedまでを高速化する</h2>

<p>
意外と重くなりがちなのが、DOMContentLoadedまでの所要時間です。<br>
経験則ですが、divやul、liなどHTMLタグの入れ子を深めると、その部分の処理が重くなる傾向があります。<br>
疑わしい部分はコメントアウトして計測してみる、といった方法で特定を行います。<br>
特に低スペック端末が影響を受けやすく、複雑なHTMLで組まれたメニュー部分を排除すると80ms近くDOMContentLoadedが改善した例もあります。
</p>

<p>
対応としてはHTMLの記述を見直すか、対象箇所を遅延評価にくくり出すのがよいでしょう。
遅延評価の方法はいくつかあり、XHRで取得する、または&lt;script type=&#8221;text/htmlfragment&#8221;&gt; のような評価されないscript要素で囲み、後でinnerHTMLに放り込むといった方法があります（文字列をHTMLとして評価する場合は、XSSなどセキュリティ面に十分気をつけてください）。</p>

<p>このようにファーストプライオリティに含まれないもの、例えばファーストビューに無関係な画像やHTMLブロックを遅延評価に追い出す方法は効果的でよく用いられます。</p>

<p>
例えば、画像の遅延ロードがあります。<br>
img要素のsrc属性を一旦data-src属性に記述しておき、優先すべき処理が終わってから、JavaScriptでdata-srcを拾って画像を改めて読み込むといったことを行います。<br>
重要でない仕事を後ろにずらすことで端末はその貴重なCPU、ネットワークといったリソースを重要な仕事に集中して利用することができます。<br>
このような遅延評価の手法、考え方はモバイルでは有効でチューニング作業全般においてよく登場します。
</p>

<h2 id="">4. メインルーチン実行までを高速化する</h2>

<p>
この場合はJavaScriptの読み込みにボトルネックが残っているか、読み込まれたJavaScriptの初期実行で時間を食っているケースを疑います。
</p>

<p>
各種JavaScriptライブラリも遅延要因になることがあります。<br>
これを計測するには、大変ベタな方法ですが、script要素内にライブラリのJavaScriptコードを貼り付け、前後でDate.nowを取ると計測できます。
</p>

<p><code>
var t = Date.now();
// ここにライブラリなど、評価したいJavaScriptコードを挿入する
// (function() { .... })
alert(Date.now() - t);
</code></p>

<p>
比較的重めのライブラリの例としてはjQueryがあります。<br>
jQueryはiPhone 4で80ms前後、Galaxy S3で100-200msの時間を初期実行に要します。<br>
これはファイルのダウンロードではなく上記のように純粋にJavaScriptの評価実行に基づいて発生する時間です。<br>
（jQuery1.8.3カスタムビルドで最小時にiPhone4+iOS6.1で54-61ms、フルビルドで80ms前後、2.0.0b1で同じく80ms前後）
</p>

<p>
仮に手持ちの予算が800msであるとすると、この所要時間は安くない買い物になります。<br>
またアプリケーションが依存するライブラリのコストは、遅延評価やチューニングといった緩和手段が取れず、必ず払うコストになります。
</p>

<p>
jQueryを例に挙げましたが、この場合はZepto.jsやtt.jsといったより軽量なライブラリに乗り換える手段があります。<br>
もしAndroidとiOSに絞れるコンテンツであれば、ライブラリを使わないのもよいでしょう。<br>
特にゲームやアニメーションを主体としたコンテンツではDOM操作の頻度も減るため、その分のライブラリを削ってゲームエンジンやデータの読み込みに予算を充てることが多くなります。<br>
目的にあったライブラリ選択を行い、予算を節約しましょう。<br>
またよく使うライブラリは、事前にコストを計測しておくとよいでしょう。</p>

<p>
他にはCPUやGPUに過剰な負荷がかかっている、といった場合が考えられます。<br>
特にDOMContentLoaded前後の時間帯は計算資源の空きが少なく、過剰なJIT処理の発生や重いアニメーションの再生を重ねると全体が遅延する、描画が一瞬固まるといったことなどがあります。<br>
HTMLの読み込み直後はできるだけCPU、GPUに優しく、負荷の高い処理は遅延させると体感が高速化することがあります。
</p>

<h2 id="">5. メインルーチン実行自体を高速化する</h2>

<p>
メインルーチンの実行自体に時間がかかる場合は、フレームワークやコンテンツ固有の特徴にひも付く問題が多くなります。<br>
フレームワークやゲームエンジン個々の話になると、その利用者に限られた内容となってしまうため、ここでは割愛します。<br>
ただ普段自分がよく使うライブラリ、フレームワークがモバイル端末できちんと動作するか実行性能を知り、できれば経験を重ねてチューニングに慣れておくと楽になります。<br>
プロファイラのような各種ツール群もこの辺りから活躍が始まります。</p>

<p>
ゲームのように大きなデータの扱いやタイミング制御の伴う画像等のアセットを扱う場合は、並列化、先読み処理といったテクニックもよく使われます。<br>
例えば先読みであれば次のように行います。
</p>

<p><code>
function preload() {
    // 先読みを行う
    loadImage('path');
}</p>

<p>function playScene() {
    // 画像を必要とする処理を行う
    loadImage('path', function() {
        play();
    });
}</p>

<p>function loadImage(src, onSuccess, onError) {
    var img = new Image();
    if (onSuccess) {
        img.onload = onSuccess;
    }
    if (onError) {
        img.onerror = onError;
    }
    img.src = src;
    return img;
}
</code></p>

<p>
並列化や先読みを行う場合、また少し複雑なアプリケーションになると非同期制御の問題がついて回ります。<br>
筆者もいままで様々な非同期制御ライブラリを使ってきましたが、現在はuupaa氏作のFlow.js（<a href="http://uupaa.hatenablog.com/entry/2013/03/12/185555" title="Flow.js" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">紹介記事</a>）に落ち着いています。<br>
Flow.js の長所としては、単純シンプルがゆえに動作が軽量であることが挙げられ、特に速度面を求める場合は Flow.js は良い選択肢となるでしょう。<br>
例えば Flow.js を使って複数画像の読み込み制御を行う場合は、次のようになります。
</p>

<p><code>
function playScene() {
    // 複数画像を必要とする処理を行う
    var paths = ['path-a', 'path-b', 'path-c'];
    var flow = new Flow(paths.length, function() {
      // 全ての画像の読み込みが終わると実行される
      play();
    });</p>

<pre><code>// 画像を読み込む
loadImage(paths[0], flow);
loadImage(paths[1], flow);
loadImage(paths[2], flow);
</code></pre>

<p>}</p>

<p>function loadImage(src, flow) {
    var img = new Image();
    if (flow) {
        img.onload = function() {
            flow.pass();
        }
    }
    img.src = src;
    return img;
}
</code></p>

<p>また連続した操作部分やサイト全体を、AJAX化するとサクサク感が増します。<br>
ブラウザはページの初期化に際して様々な処理を行っており、新たなページに遷移するだけで相応のCPU負荷がかかります。<br>
AJAX化することでこれを避けて、CPUに空きを作ってやることができ、ゲームなどの重いアニメーション、CPUを酷使するような仕事をファーストビュー領域内で行う場合などに有効です。</p>

<h2 id="javascript">6. JavaScript全体の実行速度を向上させる</h2>

<p>カリカリにチューニングを施す場面では、JavaScriptコード自体の最適化が求められます。</p>

<p>
例えば三項演算子を使い、cond ? A : B と書くより (cond &amp;&amp; A) || B と書いた方が高速です（ただしA≒false があり得るときに注意）。<br>
配列に要素追加を行うのであれば、arary.push(value)ではなくarray[array.length] = valueと書いたほうが速い、オブジェクトのプロパティに2回以上アクセスする場合は変数に代入した方が速いなど、このようなTIPSがJavaScriptには数多く存在します。
</p>

<p>
塵も積もれば何とやらで、CPUリソースが貴重なモバイル端末、特にゲームやミドルウェアなどの突き詰めた性能が必要となる場面はこのようなJavaScriptコードの最適化が効果を発揮します。
</p>

<p>
これを行うには<a href="http://jsx.github.io/" title="JSX" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">JSX</a>のようなJavaScriptの実効性能の最適化を特徴としたツールの導入が効果的です。<br>
上記TIPS程度であれば人手でまかなえますが、
「関数の呼び出し実行自体が遅く、関数をインライン展開した方が高速」などプログラマの手で生産的にまかなえる範囲を超えたノウハウも存在し、生産的にコードを書きたいのであればアプローチを変える必要があります。<br>
またJavaScriptエンジンが変わるとパフォーマンスの現れ方も変わるため、実行速度最適化の観点から考えるとプログラマが直接JavaScriptを書くのでなく、optimizerやaltJSなどを通じて最適化をかけることが望ましいといった結論に達します。
</p>

<p>
JavaScriptアプリケーション自体も複雑度が増してきており、本格的なアプリケーション開発においてエンジニアが直接JavaScriptを書く時代も終わりつつあるのかも知れません。<br>
そろそろaltJSの時代だな、と思います。
</p>

<h2 id="">最後に</h2>

<p>以上、ざっくりとモバイルでの最適化手法をお伝えしてきました。</p>

<p>
上手に最適化を施してやれば、通常のWebページ、Webアプリケーションはもとより、ゲームやアニメーションといった重量級のコンテンツでも、十分にサクサク快適に動作します。
</p>

<p>
そのために計測を行い、どこにコストを費やしているのか、問題を正確に把握するアプローチが大切です。<br>
「重い」と言われた場合、軽量化を求められた場合はどの端末で、どういった操作を行った時に重いのか、改善を必要としているのか、まず環境の特定から入り測定を行いましょう。
</p>

<p>
時々「ページの体感が重いから、画像を含むアセット量を500KB以下に定める」といったアプローチも聞きますが、それでは問題の一部しか解決できず、サクサクな体感に至らないケースがままあります。<br>
HTMLの読み込みコスト、JavaScriptによるブロッキング、フレームワークやゲームエンジンに紐付いた実行遅延、、CPUやネットワーク性能に限りがあるモバイル環境下では様々な要因が絡んで「もっさり」が発生します。<br>
サクサク化への効果的なアプローチと問題解決に、本稿がお役に立てることを願っています。
</p>
]]></content:encoded>
		
		<series:name><![CDATA[パフォーマンスチューニング]]></series:name>
	</item>
		<item>
		<title>Webアニメーションを高速化するために知っておくべき10のこと（前編）</title>
		<link>/cssradar/2027/</link>
		<pubDate>Thu, 12 Sep 2013 22:00:53 +0000</pubDate>
		<dc:creator><![CDATA[斉藤 祐也]]></dc:creator>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[CSS3]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[アニメーション]]></category>
		<category><![CDATA[パフォーマンス]]></category>

		<guid isPermaLink="false">/?p=2027</guid>
		<description><![CDATA[連載： パフォーマンスチューニング (7)アニメーション／トランジションは身の回りに当たり前にあるものです。 むしろ普段の生活では「0」が「1」に変化するものの方が珍しいでしょう。 アニメーション／トランジションはデジタ...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/performance-tech/" class="series-149" title="パフォーマンスチューニング" data-wpel-link="internal">パフォーマンスチューニング</a> (7)</div><p>アニメーション／トランジションは身の回りに当たり前にあるものです。<br>
むしろ普段の生活では「0」が「1」に変化するものの方が珍しいでしょう。<br>
アニメーション／トランジションはデジタルなWebに対して自然な変化を提供する大切なツールです。<br>
今回はそんなアニメーション／トランジションをより自然にスムーズに動作させるために知っておきたいことを前・後編の2回に分けて紹介していきます。</p>

<p><span id="more-2027"></span></p>

<h2>アニメーションを高速化する理由</h2>

<p>アニメーションは先ほど書いたように普段の生活にも存在しています。だからこそ、我々はスムーズではないアニメーションを見つけるのが得意です。</p>

<p>アニメーションに限定した話ではありませんが、FacebookのShane O&#8217;Sullivan氏が、ページロード後のレンダリングパフォーマンスが一定でない場合は「いいね！」や「シェア」などのアクション率が低下すると、昨年Londonで開催されたEdge Conferenceで話していました。</p>

<p><img src="/wp-content/uploads/2013/09/edgeconf-performance-300x158.png" alt="edgeconf-performance" width="300" height="158" class="aligncenter size-medium wp-image-2028" srcset="/wp-content/uploads/2013/09/edgeconf-performance-300x158.png 300w, /wp-content/uploads/2013/09/edgeconf-performance-207x109.png 207w, /wp-content/uploads/2013/09/edgeconf-performance.png 480w" sizes="(max-width: 300px) 100vw, 300px" /></p>

<p>特にモバイルデバイスは触って操作するだけあって、ユーザが無意識に自分の感覚と近いものを求めることは当然とも言えるでしょう。</p>

<h2>アニメーションの速さを計る「FPS」について</h2>

<p>これまでパフォーマンスを計る単位はページロード速度を計るミリ秒が主でしたが、最近ではページロード後のレンダリングやアニメーションのパフォーマンスを計る単位としてフレームレート(単位時間あたりいくつフレームが処理されるかを表す単位)が利用され始めていて、WebにおいてはFPS(Frames Per Second)、つまりフレーム/秒という単位で表すのが一般的です。</p>

<p>ここでいう「フレーム」は映画などで指す「コマ」と同じものと考えて差し支えないでしょう。例えば映画の世界では24FPSが標準的に使われていて、Webにおいては60FPSがたどり着くべき目標とされています。</p>

<h2>JavaScriptとCSSではどちらがアニメーションに最適なのか？</h2>

<p>Web上でアニメーションを表現するには多くの方法がありますが、ここではJavaScriptとCSSを使った手法にフォーカスします。  </p>

<p>CSS3を使ってのアニメーションの以前はJavaScriptによる実装しか選択肢がありませんでした。jQueryの.fadeIn()や.slideUp()などを使ったアニメーションの実装の経験は多くの人が持っているでしょう。</p>

<p>Web開発におけるすべての問題への正しい解答はいつだって「時と場合による」ものですから、どちらが最適かという問いに対する答えも同じです。しかし、本誌の<a href="https://html5experts.jp/yoshikawa_t/1016/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">ユーザーの体感速度を高めるためのJavaScriptチューニング（後編）</a>にある通り、JavaScriptはシングルスレッドで動作するもので、JavaScriptがアニメーションを行っている間はそれ以外の処理を行うことができません。  </p>

<p>JavaScriptは現在のWebサイト/アプリにおいて非常に多くの処理を行う傾向にあるため、CSS3にその処理を代わって実行させることができるようになったことは、パフォーマンス観点では大きな一歩と言えるでしょう。 </p>

<p>しかしCSS3のアニメーションを使ったからといって、60FPSの目標を自動的にクリアできるものでは決してありません。</p>

<h2>ハードウェア・アクセラレーションは銀の弾丸ではない</h2>

<p>CSS3を使ったアニメーション実装が進むにつれ、思った以上にパフォーマンスが出ない場合にハードウェア・アクセラレーションを強制的に利用するテクニックが紹介され始めました。  </p>

<p>ブラウザが行う処理はこれまでCPUで行ってきましたが、アニメーションをスムーズに実行するにはGPU(グラフィックス プロセッシング ユニット)を使う方が効率的です。</p>

<p>ハードウェア・アクセラレーションを利用すると言った場合、端的に言ってしまえば、transform: translate3d(0,0,0)あるいは、transform: translateZ(0)という3Dに関連するプロパティを追加すると、実際に値がゼロであってもブラウザはGPUを利用し、そうすることでアニメーションがスムーズに実行される。というように解説されることが多いです。</p>

<p>どんなツール/テクニックにも当てはまることですが、得るものがあれば、必ず失うものもあります。ハードウェア・アクセラレーションを利用することで得られることは、GPUが得意とするグラフィック処理を行うレイヤーを生成(後述)し、そのレイヤー上で処理が完結できること、そして失うものはそのレイヤーを生成し、管理するコストです。</p>

<p>例えば、ハードウェア・アクセラレーションが効果的だからと言って、body * { transform: translateZ(0) } というような記述をすると、本来であればレイヤーが必要ない要素に対してもレイヤーを生成してしまうことになります。これはレイヤーを生成し、管理するためのメモリを余分に費やすことになり、サイト全体で見るとパフォーマンスが低下してしまうことになります。</p>

<p>ハードウェア・アクセラレーションは必要なタイミングで必要な要素にだけ利用するようにするべきです。また、transform: translateZ(0)というような記述でブラウザにハードウェア・アクセラレーションさせるのはあくまでもハックとして考えておいた方がいいでしょう。ブラウザのアップデートとともにハックが利用できなくなるかもしれません。</p>

<h2>CSSのプロパティによってアニメーションのパフォーマンスはどう変わるのか</h2>

<p>ハードウェア・アクセラレーションはtransform: translateZ(0)だけで利用できるようになるわけではありません。  </p>

<p>例えば、ある要素の位置をA地点からB地点に移動させるアニメーションを実装する際に:</p>

<ol><li>transform: translate()を利用する</li><li>position:absoluteで要素の位置を指定しtopやleftの値を利用する</li></ol>

<p>というケースが考えられます。  </p>

<p>Paul Irish氏はこの<a href="http://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">2つケースを実装し、両者のパフォーマンスを比べています</a>。  </p>

<p><img src="/wp-content/uploads/2013/09/Why-moving-elements-with-translate-is-better-than-pos-abs-top-left-Paul-Irish-300x158.png" alt="Why moving elements with translate   is better than pos abs top left   Paul Irish" width="300" height="158" class="aligncenter size-medium wp-image-2029" srcset="/wp-content/uploads/2013/09/Why-moving-elements-with-translate-is-better-than-pos-abs-top-left-Paul-Irish-300x158.png 300w, /wp-content/uploads/2013/09/Why-moving-elements-with-translate-is-better-than-pos-abs-top-left-Paul-Irish-207x109.png 207w, /wp-content/uploads/2013/09/Why-moving-elements-with-translate-is-better-than-pos-abs-top-left-Paul-Irish.png 480w" sizes="(max-width: 300px) 100vw, 300px" /></p>

<p>結論から言うと、1)のtransform: translate()を利用するほうがパフォーマンスはよくなります。  </p>

<p>Paul Irish氏は2)で実装する場合、アニメーションする要素はCPUを利用し、かつ敷かれている背景に対して移動する度に描画を行う必要があるのと比べて、1)の場合、要素はGPUが生成するRenderLayerと呼ばれる専用のレイヤー上を移動し、敷かれている背景などへの影響がないため、スムーズに移動することができると解説しています。</p>

<p>RenderLayerについてはブラウザの内部的な話になってしまうので、ここでは詳しくは触れませんが、2)のposition:absolute + top/leftを使った場合のアニメーションは、ブラウザを白い壁に例えると要素が移動するたびに要素を白く塗りつぶし、移動先に書き直す、というような処理になりますが、1)のtransform: translate()を使った場合は、白い壁に透明のシート(RenderLayer)を被せ、そのシート上に要素を描くことで、移動はシートを動かすだけで実現できるようになる、という様に考えてみるとわかりやすいでしょう。もちろんアニメーションによってはRenderLayerが得意な表現だけを利用できるわけではありませんし、あくまでも現時点のブラウザの仕様に依存する部分も多くあります。 </p>

<p>次回後編ではレンダリングプロセス全体について簡単に解説し、アニメーションのボトルネックとなるレイアウトとペイントの2つのプロセスについて、そして、より詳しい計測、デバッグのワークフロー、最後によくあるアクシデントと回避方法について紹介します。</p>
]]></content:encoded>
		
		<series:name><![CDATA[パフォーマンスチューニング]]></series:name>
	</item>
		<item>
		<title>DOM操作の最適化によるJavaScriptチューニング（後編）</title>
		<link>/yoshikawa_t/1932/</link>
		<pubDate>Sun, 08 Sep 2013 22:00:47 +0000</pubDate>
		<dc:creator><![CDATA[吉川 徹]]></dc:creator>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[パフォーマンス]]></category>
		<category><![CDATA[ブラウザ]]></category>
		<category><![CDATA[高速化]]></category>

		<guid isPermaLink="false">/?p=1932</guid>
		<description><![CDATA[連載： パフォーマンスチューニング (5) 連載「Webサイト・アプリ高速化テクニック徹底解説」の第5回は、前回の「DOM操作の最適化によるJavaScriptチューニング（前編）」に続く後編です。後編では、create...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/performance-tech/" class="series-149" title="パフォーマンスチューニング" data-wpel-link="internal">パフォーマンスチューニング</a> (5)</div><p><style>.codecolorer-container+h3{margin-top:1.5em;}</style>
連載「Webサイト・アプリ高速化テクニック徹底解説」の第5回は、前回の「<a href="https://html5experts.jp/yoshikawa_t/1888/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">DOM操作の最適化によるJavaScriptチューニング（前編）</a>」に続く後編です。後編では、createElement()などのDOM操作メソッドを使ったさまざまなテクニックや、パフォーマンスを劣化させるよくあるパターンについて詳しく解説します。</p>

<div style="border: 1px solid gray; margin: 1em; padding: 1em;">
<article>
<h1>CodeIQとの連動企画！</h1>
この記事で学べるJavaScriptチューニングのテクニックを、実際に<a href="https://codeiq.jp/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">CodeIQ</a>の問題で試すことができます。もう既に自信がある方は腕試しに、もしくは理解度チェックのための復習として是非ご活用ください！<a href="https://codeiq.jp/ace/yoshikawa_t/q452" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">こちら</a>から問題にチャレンジ！
</article>
</div>

<!-- more -->

<h1>DOM操作の最適化によるJavaScriptチューニング（後編）</h1>

<p>前回は、DOM操作が遅い原因と仕組みについて簡単に説明し、チューニングのサンプルをいくつか解説しました。その中で、innerHTMLを利用したコードをサンプルにあげていますが、innerHTMLを利用する場合、いくつかの点で注意が必要です。</p>

<ul>
<li>HTMLの文字列を扱うため単純な子要素の追加ができない（子要素をすべて書き換えることになる）</li>
<li>追加する要素が多くなると、文字列連結のコストやHTMLをパースするコストが膨れ上がる</li>
<li>HTMLを直接扱うため、きちんとエスケープしないとクロスサイトスクリプティングの脆弱性を作りこむ可能性がある</li>
</ul>

<p>以上の点から、特に理由がなければinnerHTMLよりもDOM操作メソッドを使ったほうが良いでしょう。今回は、DOM操作メソッドを使ったさまざまなテクニックや、パフォーマンスを劣化させるよくあるパターンを紹介し、そのチューニング方法を見ていきます。</p>

<p>また、前回の記事のinnerHTMLを使ったサンプルでは、上に挙げたような複数の要因でパフォーマンスに影響与えていることから、レイアウト・レンダリングの遅さのみを理由とするには、あまり適切ではありませんでした（チューニング自体には問題はありませんので心配しないでください）。折を見て改訂したいと思います。ご指摘して頂いた方々、ありがとうございました。
本連載では、より良い情報を皆さんに届けるため、ご指摘や疑問点などがあれば是非フィードバックを頂ければと思います。フィードバックを受け、できるかぎり記事を更新していきたいと思います。</p>

<h2>DOM操作メソッドを使ったテクニック</h2>

<p>createElement()やappendChild()などのDOM操作メソッドを使った方法では、単純にinnerHTMLによるコードを置き換えるだけでなく、さまざまなテクニックがあります。ここでは、DOM操作メソッドを使う際に覚えておいた方がよいテクニックをいくつか紹介します。</p>

<h3>複数の要素をまとめて追加する</h3>

<p>複数の要素を追加する場合、構造によってはそのままでは一度に追加できないことがあります。例えば、次のサンプルのようにul要素にli要素を追加していくようなケースです。</p>

<pre><code>:javascript:
// サンプル1: ul要素にli要素を追加していく（低速）
var ul = document.querySelector('#output');
for ( var i = 0; i &lt; data.length; i++ ) {
  var li = document.createElement('li');
  li.textContent = data[i];

  // ループのたびにli要素を追加
  ul.appendChild(li);
}
</code></pre>

<p>このサンプルでは、ループのたびにli要素を追加していますので、DOMツリーへ更新が何度も発生します。こういった場合には、DocumentFragmentを利用して複数のli要素をまとめて追加することができます。次のコードは、DocumentFragmentを利用して書き換えたコードです。</p>

<pre><code>:javascript:
// サンプル1: ul要素にli要素をまとめて追加（高速）
var ul = document.querySelector('#output'),
    fragment = document.createDocumentFragment();

for ( var i = 0; i &lt; data.length; i++ ) {
  var li = document.createElement('li');
  li.textContent = data[i];

  // いったんDocumentFragmentに追加する
  fragment.appendChild(li);
}

// 最後にDocumentFragmentをul要素に追加する
ul.appendChild(fragment);
</code></pre>

<p>DocumentFragmentは、従来のDOMツリーとは分離された独立した小さなDOMツリーです。createDocumentFragment()メソッドを使って作成します。DocumentFragmentに追加された要素は、そのままでは見た目に影響を与えないため、まずはDocumentFragmentにli要素を追加していきます。そして、最後にul要素にそのDocumentFragmentを追加し、反映します。こうすることで、元のDOMツリーへの更新が1回だけで済みます。</p>

<h3>繰り返し同じような要素を追加する（テンプレート化）</h3>

<p>同じような要素を繰り返し追加する場合、要素をまた一から作成していくのは面倒です。また、要素が多くなってくるとパフォーマンス的にもあまり良くありません。例えば、ボタンが押されるたびに、次のような構造を持つli要素を追加していくことを考えてみましょう。</p>

<pre><code>:html:
&lt;li&gt;
  &lt;artcile class="item"&gt;
    &lt;h1 class="title"&gt;アイテム&lt;/h1&gt;
    &lt;p class="detail"&gt;詳細&lt;/p&gt;
  &lt;/artcile&gt;
&lt;/li&gt;
</code></pre>

<p>これを素直にDOM操作メソッド使って構築すると次のようにボタンが押されるたびに毎回要素を生成して構築する形になります。</p>

<pre><code>:javascript:
// ボタンがクリックされる度に複雑な要素を追加
button.addEventListener('click', function(){

  // 各要素を生成
  var li = document.createElement('li'),
      article = document.createElement('article'),
      h1 = document.createElement('h1'),
      p = document.createElement('p');

  // 各要素のプロパティを設定し、組み立てる（省略）

  ul.appendChild(li);
}, false);
</code></pre>

<p>サンプルでは、コードが多い部分を省略していますが、多くのDOM操作をしています。このような場合、構築される要素をテンプレート化して、それをcloneNode()を使ってコピーするようにすれば、DOM操作の数を減らすことができます。cloneNode()を利用すると次のようなコードになります。</p>

<pre><code>:javascript:
// テンプレートとしてli要素を構築
var template = document.createElement('li'),
    article = document.createElement('article'),
    h1 = document.createElement('h1'),
    p = document.createElement('p');

// 各要素のプロパティを設定し、組み立てる（省略）

// ボタンがクリックされる度にテンプレートから要素を追加
button.addEventListener('click', function(){

  // テンプレートから要素をコピー
  var li = template.cloneNode(true);

  // 一部書き換えて追加
  li.querySelector('.title').textContent = 'アイテム';
  li.querySelector('.detail').textContent = 'アイテムの詳細'
  ul.appendChild(li);
}, false);
</code></pre>

<p>こうすると、ボタン押すたびにテンプレートからコピーするだけなので効率的です。構築する要素が複雑になればなるほど、cloneNode()を利用する方が高速になります。cloneNode()の引数は、子要素を一緒にコピーするかどうかなので、trueを指定しておきます。また、cloneNode()ではイベントリスナーをコピーすることはできませんので注意してください。</p>

<h3>複数の要素をまとめて置き換える</h3>

<p>既に表示されているインターフェースに対して最新の情報を反映するような場合があるかと思います。その際に、表示するデータが細かいとDOM操作も細かくなってしまうことがあります。例えば、次のようなHTMLの各p要素の内容を書き換えることを考えてください。</p>

<pre><code>:html:
&lt;div class="results"&gt;
  &lt;p&gt;結果1&lt;/p&gt;
  &lt;p&gt;結果2&lt;/p&gt;
  &lt;p&gt;結果3&lt;/p&gt;
&lt;/div&gt;
</code></pre>

<p>これを単純に書き換える場合は、次のようなコードになりがちです。</p>

<pre><code>:javascript: 
var elements = document.querySelectorAll('.results p');
elements[0].textContent = '結果a';
elements[1].textContent = '結果b';
elements[2].textContent = '結果c';
</code></pre>

<p>この場合、3つのp要素を順に書き換えているので、DOMツリーへの更新が3回発生してしまいます。このような場合、replaceChild()を使って一度に置き換えることができます。</p>

<pre><code>:javascript:
var origin = document.querySelector('.results'),
    clone = origin.cloneNode(true);

// コピーした要素を更新する
var elements = clone.querySelectorAll('p');
elements[0].textContent = '結果a';
elements[1].textContent = '結果b';
elements[2].textContent = '結果c';

// 元の要素とコピーした要素を入れ替える
origin.parentNode.replaceChild(clone, origin);
</code></pre>

<p>この方法であれば、DOMツリーへの更新は1回で済みます。また、先ほどのサンプルと同様にcloneNode()がイベントハンドラーをコピーできないことに注意しましょう。</p>

<h2>パフォーマンスを劣化させるよくあるパターン</h2>

<p>ここからは、パフォーマンスを劣化させるよくあるパターンや、そのチューニング方法を紹介します。</p>

<h3>複数のスタイルの書き換え</h3>

<p>ある要素のスタイルを複数書き換える場合に、ついやってしまいがちなのが次のコードです。</p>

<pre><code>:javascript:
// 複数のスタイルの書き換え（低速）
element.style.background = 'gray';
element.style.border = '1px solid black';
</code></pre>

<p>この場合も、レイアウトが複数発生する可能性があるので、次のように記述しましょう。</p>

<pre><code>:javascript:
// 複数のスタイルの書き換え（高速）
// style属性で一度にすべて指定する
element.setAttribute('style', 'background: gray; border: 1px solid gray;');
</code></pre>

<p>setAttribute()を使ってsytle属性を使って一度に複数のスタイルを指定しています。また、あらかじめ指定するスタイルがある程度決っているなら、可読性やメンテナンスを考えてclass属性を使っても良いでしょう。</p>

<pre><code>:javascript:
// classを指定して複数のスタイルを適用する
element.className = 'hoge';
</code></pre>

<h3>アニメーション</h3>

<p>ある要素の位置やサイズを動かしてアニメーションさせるような場合、スタイルでposition: absoluteやpositon: fixedを指定しておくと良いでしょう。これは、レイアウト・レンダリングの範囲を最少化するためです。position: absoluteなどを指定すると他の要素との位置関係やサイズ計算から切り離されるため、その要素に対する変更が他の要素に影響しなくなります。そのため、レイアウト・レンダリングのコストが小さくなり、パフォーマンスが向上します。アニメーションのチューニングについては、また別の記事で触れますので楽しみにしていてください。</p>

<h3>スタイル情報の取得</h3>

<p>多くのDOM操作によるスタイル情報の取得の中でも、次に挙げるプロパティ、メソッドについては特に注意が必要です。</p>

<ul>
<li>getComputedStyle()</li>
<li>offset*系のプロパティ</li>
<li>client*系のプロパティ</li>
<li>scroll*系のプロパティ</li>
</ul>

<p>（offset*系のプロパティとは、offsetという単語を含むoffsetTop、offsetLeft、offsetHeight、offsetWidthなどのプロパティです）</p>

<p>これらのプロパティ、メソッドは、現時点での最新の情報を返そうとするため、それまでに実行したDOM操作があれば、すぐさまレイアウト・レンダリングを実行します。本来であればブラウザが自動的に最適化し、非同期に実行しているものを強制的に実行してしまうため大きなボトルネックになります。例えば、あるブロックを単純に右に動かすだけのコードを見てみましょう。</p>

<pre><code>:javascript:
// あるブロックを右に動かす
setInterval(function(){
  block.style.left = block.offsetLeft + 1 + 'px';
}, 1000 / 60 );
</code></pre>

<p>ここでは、あるブロックのoffsetLeftを取得し、1を加えてスタイルのleftに代入しています。leftの値を更新した後に、またすぐにoffsetLeftを参照しているので、その時点でレイアウト・レンダリングが発生し、遅くなります。そのため、offsetLeftの値をキャッシュしておき、以降はそれを利用するように変更しましょう。</p>

<pre><code>:javascript:
// あるブロックを右に動かす
// offsetLeftの値を一度キャッシュして以降は使いまわす
var left = block.offsetLeft;
setInterval(function(){
  left++;
  block.style.left = left + 'px';
}, 1000 / 60 );
</code></pre>

<p>このように、上記であげたプロパティ、メソッドは、多くのDOM操作をしているような箇所では特にボトルネックになりやすいので注意しましょう。（サンプルでは、JavaScriptを使っていますが、もちろんCSSで記述しても良いでしょう。）</p>

<h3>要素の取得</h3>

<p>それほど気にするほどの差ではありませんが、要素を取得する際にquerySelector()やquerySelectorAll()よりも、getElementById()やgetElementsByTagName()、getElementsByClassName()を利用した方が若干速くなります。</p>

<pre><code>:javascript:
// div要素をすべて取得
var elements = document.querySelectorAll('div');

// querySelectorAll()よりも若干速い
var elements = document.getElementsByTagName('div');
</code></pre>

<p>その他の違いにも、getElementsByTagName()などは、結果として返るリスト（NodeList）が常に現在のDOMツリーを反映したものになるのに対して、querySelectorAll()などは取得した時点のものになります。例えば、これらのメソッドで要素を取得した後に、要素が追加された場合、getElementsByTagName()で取得したリストではリストにその要素が自動的に追加されますが、querySelectorAll()ではリストに変化はありません。そういった違いも覚えておくと良いでしょう。</p>

<h2>まとめ</h2>

<p>ここまで前編、後編にわたってDOM操作の最適化について解説しました。いくつかのサンプルやパターンを交えて、チューニング方法を紹介してきましたが、もちろんこれ以外にもたくさんのテクニックがあります。細かいテクニックをあげていくときりがありませんが、重要なのはDOM操作自体の回数を減らすことと、DOMツリーへの更新とレイアウトやレンダリングなどの範囲、回数を減らすことです。それさえ覚えておけば、自分でいろいろなコードに応用できるかと思います。自分自身で考えてチューニングしていきましょう。</p>
]]></content:encoded>
		
		<series:name><![CDATA[パフォーマンスチューニング]]></series:name>
	</item>
		<item>
		<title>DOM操作の最適化によるJavaScriptチューニング（前編）</title>
		<link>/yoshikawa_t/1888/</link>
		<pubDate>Wed, 04 Sep 2013 22:00:03 +0000</pubDate>
		<dc:creator><![CDATA[吉川 徹]]></dc:creator>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[パフォーマンス]]></category>
		<category><![CDATA[ブラウザ]]></category>
		<category><![CDATA[高速化]]></category>

		<guid isPermaLink="false">/?p=1888</guid>
		<description><![CDATA[連載： パフォーマンスチューニング (4)連載「Webサイト・アプリ高速化テクニック徹底解説」の第4回は、JavaScriptのチューニングのうち、ボトルネックになりやすいDOM操作の最適化について解説します。前編・後編...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/performance-tech/" class="series-149" title="パフォーマンスチューニング" data-wpel-link="internal">パフォーマンスチューニング</a> (4)</div><p>連載「Webサイト・アプリ高速化テクニック徹底解説」の第4回は、JavaScriptのチューニングのうち、ボトルネックになりやすいDOM操作の最適化について解説します。前編・後編にわたって、DOM操作が遅くなる原因と仕組み、その解決策について詳しく解説します。</p>

<div style="border: 1px solid gray; margin: 1em; padding: 1em;">
<article>
<h1>CodeIQとの連動企画！</h1>
この記事で学べるJavaScriptチューニングのテクニックを、実際に<a href="https://codeiq.jp/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">CodeIQ</a>の問題で試すことができます。もう既に自信がある方は腕試しに、もしくは理解度チェックのための復習として是非ご活用ください！<a href="https://codeiq.jp/ace/yoshikawa_t/q452" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">こちら</a>から問題にチャレンジ！
</article>
</div>

<!-- more -->

<h1>DOM操作の最適化によるJavaScriptチューニング（前編）</h1>

<p>DOM（Document Object Model）とは、HTMLをアプリケーション（ここではJavaScript）から利用するためのAPIです。JavaScriptによるユーザーインターフェースの構築やレスポンスの表示など、インタラクティブな部分はほとんどがDOM操作によるものでしょう。このように、非常に利用頻度の高いDOM操作ですが、同時に性能的にボトルネックになりやすい箇所でもあります。これらのDOM操作の最適化について、今回から前編・後編にわたって詳しく解説していきます。</p>

<h2>DOM操作は書き方によってスピードに数十倍の差が出る</h2>

<p>DOM操作は、書き方によって非常にパフォーマンスに差が出ます。例えば、次のようなコードを見てみましょう。id属性に&#8221;output&#8221;と指定された要素に、data配列の中身を単純にリスト表示するだけのものです。</p>

<pre><code>:javascript:
// サンプル1: パフォーマンスが悪い
var ul = document.querySelector('#output');
for ( var i = 0; i &lt; data.length; i++ ) {
  ul.innerHTML += '&lt;li&gt;' + data[i] + '&lt;/li&gt;';
}
</code></pre>

<p>これは、非常にパフォーマンスが悪い例です。これを次のように書き換えるだけで、環境にもよりますが速度は数十倍にもなります。</p>

<pre><code>:javascript:
// サンプル2: サンプル1に比べてパフォーマンスが良い
var ul = document.querySelector('#output');
var html = '';
for ( var i = 0; i &lt; data.length; i++ ) {
  html += '&lt;li&gt;' + data[i] + '&lt;/li&gt;';
}
ul.innerHTML = html;
</code></pre>

<p>このように、ボトルネックとなっているDOM操作をチューニングすれば、パフォーマンスが大きく改善する可能性があります。ここからは、DOM操作が遅くなる原因と仕組みを解説しながら、対応策を見ていきます。</p>

<h2>なぜDOM操作は遅いのか？</h2>

<p>DOM操作はなぜ遅いのでしょうか。それには、大きく2つの理由があります。1つ目は、DOMの実装方法によるものと、2つ目は、DOM操作によるレンダリングのコストが非常に高いということが挙げられます。この2つの理由と対応方法を覚えておけば、さまざまなコードに応用できます。それぞれ見ていきましょう。</p>

<h3>ブラウザの実装による遅さ</h3>

<p>元々DOMは、特定のプログラミング言語を対象としたものではなく、別々の言語から汎用的にアクセスできるような仕様になっています。そのため、多くのブラウザでは、HTMLのレイアウトや描画を担当するレンダリングエンジンと、JavaScriptを担当するスクリプトエンジンに分かれています。このようにスクリプト部分をモジュール化することによってDOM操作に別のプラグラミング言語を使うことも容易になっています。しかしながら、そのデメリットとして、JavaScriptからDOM操作を行うということは、モジュール間をブリッジするコストが発生することにもなります。そのため、DOM操作自体がプリミティブな操作に比べて非常に低速になっています。そういった観点から、HTMLの要素の単純なプロパティ参照なども含めて、極力DOM操作を減らすようにすることが重要です。</p>

<h3>レイアウト・レンダリングによる遅さ</h3>

<p>DOM操作の結果は、実際のHTMLに反映される際に、ブラウザでさまざまな処理が起こります。例えば、要素が追加された場合には、それによって発生する各要素の大きさや位置といったレイアウトを他の影響する要素を含めて、すべて再計算しなければなりません。また、レイアウトの再計算が終わったあとには、それらを画面にレンダリングする必要もあります。そして、これらの処理は非常にコストが高いものです。そのため、DOM操作によってレイアウトやレンダリングに影響がある範囲、回数をなるべく減らす必要があります。</p>

<p>例えば、冒頭に示したサンプル1のコードでは、次のようにli要素をループが回るたびに追加しているため、レイアウトとレンダリングが何回も発生して非常に遅くなってしまう可能性があります（レイアウトやレンダリングは非同期で処理されるため、実際にはブラウザがある程度、自動的に最適化してくれます）。</p>

<pre><code>:javascript:
// 複数回、レイアウト、レンダリングされる可能性がある
for ( var i = 0; i &lt; data.length; i++ ) {
  ul.innerHTML += '&lt;li&gt;' + data[i] + '&lt;/li&gt;';
}
</code></pre>

<p>innerHTMLを利用しているため、少なくてもHTMLのパースはループのたびに発生します。また、古いブラウザでは文字列の連結自体が問題になることもあります。このコードを、レイアウトとレンダリングが1回になるように書き換えると次のようになります。（その他にも、innerHTMLを「+=」演算子で余分に参照していることや、innerHTMLの中身を毎回すべて書き換えていることも遅さの原因になっていますが、次のコードではそれらも改善しています。）</p>

<pre><code>:javascript:
// 変数にHTMLを格納しておき最後に1回だけ書き換える
var html = '';
for ( var i = 0; i &lt; data.length; i++ ) {
  html += '&lt;li&gt;' + data[i] + '&lt;/li&gt;';
}
ul.innerHTML = html;
</code></pre>

<p>最終的に、結果が表示されれば良いので、li要素のHTMLは、いったん別の変数に格納しておき、最後に1回だけinnerHTMLへ代入しています。これによって、DOM操作の回数もレンダリングの回数も以前のコードに比べて1回で済みます。</p>

<h2>innerHTMLとDOM操作メソッド</h2>

<p>ここまでのサンプルでは、innerHTMLを使ってDOM操作を記述していますが、createElement()やappendChild()などのDOM操作メソッドを使う方法もあります。次のサンプルは、サンプル2をDOM操作メソッドを利用して記述した例です。</p>

<pre><code>:javascript:
// サンプル3: createElement()を使った例
var ul = document.querySelector('#output');
var fragment = document.createDocumentFragment();
for ( var i = 0; i &lt; data.length; i++ ) {
  var li = document.createElement('li');
  li.textContent = data[i];
  fragment.appendChild(li);
}
ul.appendChild(fragment);
</code></pre>

<p>innerHTMLとDOM操作メソッドは、それぞれ良い点がありますが、基本的にはDOM操作メソッドを使っていくほうがクロスサイトスクリプティングの脆弱性を作りこみにくいので安全です（innerHTMLは、文字列をパースするためユーザーの入力値を使う場合はエスケープする必要があります）。これらのDOM操作メソッドを利用する方法は、次回で詳しく取り上げていきます。</p>

<h3>innerHTMLとDOM操作メソッドのスピード</h3>

<p>innerHTMLとcreateElement()などのDOM操作メソッドは、どちらが速いかといった形でよく比較されることが多いですが、パフォーマンスという観点からはそれほど気にしなくても良いでしょう。モダンな環境では、お互いの速度差はそれほど大きくありませんし、ブラウザは常にバージョンアップされていますのでベンチマークの結果はすぐに変わります。特定の環境に特化したチューニングを行うのでなければ、細かい速度差を気にするよりは、機能の違いで使い分けていきましょう（ちなみに、現在はIE、FirefoxはinnerHTMLの方が若干速く、Chrome、SafariはDOM操作メソッドの方が若干速くなっています）。</p>

<h2>次回の内容について</h2>

<p>今回は、DOM操作が遅くなる理由と、その対処方法について簡単に解説しました。次回は、createElement()によるDOM操作と、チューニングのためのさまざまなTipsを紹介していく予定です。お楽しみに！</p>
]]></content:encoded>
		
		<series:name><![CDATA[パフォーマンスチューニング]]></series:name>
	</item>
		<item>
		<title>ユーザーの体感速度を高めるためのJavaScriptチューニング（後編）</title>
		<link>/yoshikawa_t/1016/</link>
		<pubDate>Mon, 22 Jul 2013 20:00:26 +0000</pubDate>
		<dc:creator><![CDATA[吉川 徹]]></dc:creator>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[Chrome]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[アプリ高速化]]></category>
		<category><![CDATA[パフォーマンス]]></category>

		<guid isPermaLink="false">/?p=1016</guid>
		<description><![CDATA[連載： パフォーマンスチューニング (3)連載「Webサイト・アプリ高速化テクニック徹底解説」の第3回は、前回の「ユーザーの体感速度を高めるためのJavaScriptチューニング（前編）」の続きです。この後編では、「ユー...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/performance-tech/" class="series-149" title="パフォーマンスチューニング" data-wpel-link="internal">パフォーマンスチューニング</a> (3)</div><p>連載「Webサイト・アプリ高速化テクニック徹底解説」の第3回は、前回の「<a href="https://html5experts.jp/yoshikawa_t/877/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">ユーザーの体感速度を高めるためのJavaScriptチューニング（前編）</a>」の続きです。この後編では、「ユーザーの操作を阻害しない」方法についてJavaScriptのシングルスレッドやイベントループを交えて解説し、HTML5のWeb Workersについても紹介していきます。</p>

<!-- more -->

<h1>ユーザーの体感速度を高めるためのJavaScriptチューニング（後編）</h1>

<p>前回は、ユーザーの体感速度を向上させるための方法として、3つのうち「ページを素早く表示する」と「ユーザーに素早くインタラクションを返す」を解説しました。今回は、最後の「ユーザーの操作を阻害しない」について詳しく解説していきます。</p>

<h2>ユーザーの操作を阻害しない</h2>

<p>JavaScriptによる処理が重くなると、いつまでも画面が更新されなかったり、ユーザーの操作が止まってしまったりということがあります。止まっている時間が長すぎると、ブラウザから応答がないという警告がでることもあります。それほどひどくなくても、ページのスクロールが途中でつっかかったり、アニメーションがとぎれとぎれになったりといったことは、皆さん経験があるのではないでしょうか。こういった状態は、ブラウザのユーザーインターフェース周りの処理とJavaScriptが同じシングルスレッドで動作していることに原因があります。このようなブラウザにおけるシングルスレッドとイベントループという仕組みを先に簡単に解説しておきましょう。</p>

<h3>シングルスレッドとイベントループ</h3>

<p>通常、ほとんどのブラウザはシングルスレッドで動作しており、JavaScriptのコードや画面の描画、ユーザーの操作（ページのスクロールやマウス操作など）を同時に処理することはできません。そのため、JavaScriptの処理に時間が掛かってしまうと、その間は画面の描画も、ユーザーの操作も止まってしまいます。これらの処理は、イベントループと呼ばれる仕組みで動作しています。</p>

<div id="attachment_1017" style="width: 1034px" class="wp-caption aligncenter"><a href="https://html5experts.jp/wp-content/uploads/2013/07/eventloop.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/07/eventloop.png" alt="イベントループのイメージ図" width="1024" height="768" class="size-full wp-image-1017" srcset="/wp-content/uploads/2013/07/eventloop.png 640w, /wp-content/uploads/2013/07/eventloop-300x225.png 300w, /wp-content/uploads/2013/07/eventloop-207x155.png 207w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><p class="wp-caption-text">イベントループのイメージ図</p></div>

<p>イベントループは、イベントを監視する無限ループを持ち、イベント（またはそのコールバック）が発生すると、発生順にそのイベントを処理していきます。ここでいうイベントには、JavaScriptのイベントに加えて、ブラウザのレイアウトイベントや描画イベントなども含まれています。そのため、JavaScriptの実行に時間がかかると、その間に発生した描画イベントなどの他のイベントが実行キューに登録され、いつまでも処理されないということになります。根本的な対応方法は各処理を短くすることですが、このイベントループの仕組みうまく利用すれば、大きな処理を細かく分割することもできます。</p>

<h3>setTimeout関数などによる擬似的な並列処理</h3>

<p>イベントループの仕組みを考えると、JavaScriptの処理に時間がかかる場合は、その処理をいったん終了させて、別のイベントで続きを実行すれば処理を分割できることになります。この分割した処理の間に、ブラウザの描画イベントなどが発生すれば、正常に画面が更新されることになります。</p>

<p>例えば、JavaScriptで時間のかかる処理の前に、先に見た目を更新するという方法（前回の「ユーザーに素早くインタラクションを返す」）を挙げましたが、次のようなコードでは画面がすぐには更新されず、JavaScriptの実行が終わるまで反映されません。</p>

<pre><code>:javascript:
// id属性に"output"を指定した要素にメッセージを表示するコード
var output = document.querySelector('#output');
output.textContent = 'メッセージ';

// ~なんらかの時間がかかる処理~
</code></pre>

<p>これを、メッセージがすぐに画面に反映されてから、時間がかかる処理をするように変更するには、次のように記述します。</p>

<pre><code>:javascript:
// id属性に"output"を指定した要素にメッセージを表示するコード
var output = document.querySelector('#output');
output.textContent = 'メッセージ';

setTimeout(function(){

  // ~なんらかの時間がかかる処理~
}, 0);
</code></pre>

<p>setTimeout関数を利用して、時間のかかる処理を実行しています。setTimeout関数は、一定時間後に指定したメソッドを実行する関数ですが、ここでは0ミリ秒後に指定しています。これは、実際にはすぐに実行されるわけではなく、イベントループの実行キューに登録されることになります。そのため、上で表示しているメッセージの描画イベントが先に実行され、その後にsetTimeout関数で指定した処理が実行されます。</p>

<p>このようにして、setTimeout関数を使って、明示的に大きな処理を分割し、擬似的な並列処理を実現することができます。あまり多用するとコードの可読性やメンテンナンス性などが落ちるので注意が必要です。また、setTimeout関数以外でもイベントやコールバックであれば良いので、XMLHttpRequestやその他のイベントハンドラでも同様のことができます。JavaScriptでは、もともとそういった記述が多いので、意図的ではなくても自然とそういったコードになっていることが多いと思います。しかしながら、こういった仕組みを知っておくことでうまく問題が解決できることもありますので、覚えておくと良いでしょう。</p>

<p>実は、このsetTimeout関数を使った特定の処理を実行キューの最後にスタックする方法は、setImmediate関数として標準化が議論されています（仕様は<a href="https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/setImmediate/Overview.html" title="setImmediate spec" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">こちら</a>）。現在は、IE9以降で実装されています。</p>

<h2>バックグラウンドでJavaScriptを実行する「Web Workers」</h2>

<p>HTML5には、setTimeout関数などによる擬似的な並列処理ではなく、本当の並列処理を行うための<a href="http://www.w3.org/TR/workers/" title="Web Workers" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Web Workers</a>という仕様があります。このWeb Workersでは、JavaScriptの処理をブラウザのメインスレッドとは別に、バックグラウンドで処理することができるようになります。そのため、画面の描画やユーザー操作を阻害せずに、時間のかかるJavaScriptを実行することができます。</p>

<p>早速、Web Workersの利用方法を見ていきましょう。Web Workersを利用する場合、バックグラウンドで処理するためのJavaScriptファイルを別に用意します。ここでは、表示するページで読み込むmain.js、バックグラウンドで動作するworker.jsの2つに分けています。それぞれのファイルの内容は、次の通りです。</p>

<p><strong>main.js</strong></p>

<pre><code>:javascript:
// ワーカーの作成
var worker = new Worker('worker.js');

// メッセージ受信イベントの登録
worker.addEventListener('message', function(e){
  console.log(e.data);
});

// メッセージ送信
var message = 'test';
worker.postMessage(message);
</code></pre>

<p>main.jsでは、バックグラウンドで動作するWorkerオブジェクト（以下、ワーカー）を引数にworker.jsを指定して作成します。そして、ワーカーとのデータの送受信には、お互いにメッセージをやり取りして行います。ワーカーのpostMessage()メソッドで加工するデータなどを送信し、messageイベントでワーカーから戻ってきたデータを受信します（データはコピー渡しとなります）。送受信できるデータは、基本的な型やオブジェクトなどが可能ですが、Errorオブジェクトや関数（Functionオブジェクト）、DOMノードなどはエラーとなります。</p>

<p><strong>worker.js</strong></p>

<pre><code>:javascript:
self.addEventListener('message', function(e){

  // メッセージを受信して、そのまま返す
  self.postMessage(e.data);
});
</code></pre>

<p>worker.jsでは、main.jsと同じようにmessageイベントとpostMessage()メソッドを利用してメッセージをやり取りします。ここでは、messageイベントでデータを受け取り、受け取ったデータをpostMessage()メソッドでそのまま返しています。例えば何らかの処理をする際には、ここでデータを加工して送り返すといったように変更すると良いでしょう。ワーカー自身にアクセスするには、self変数を利用します。また、ワーカー内では、windowオブジェクトやページのDOMツリーにアクセスすることができませんので注意してください。</p>

<p>このようにして、main.jsでは基本的にユーザーインターフェースの作成やワーカーでの処理結果を反映するだけにとどめ、ワーカーで実際の処理をバックグラウンドで行うようにすると、ユーザーの操作を阻害しないような作り込みができます。また、Web Workersを利用することで、自然とビジネスロジックなどのコードを切り分けることできるので、MVCモデルのような構成を取りやすいこともメリットのひとつでしょう。是非、チャレンジしてみてください。</p>

<p>以降は、Web Workersの対応状況や制限事項などを載せておきますので、参考にしてください。</p>

<h3>Web Workersの対応状況</h3>

<p>デスクトップ向けであれば、IE9を除くすべてのモダンブラウザ（IEはバージョン10より対応）で利用することができます。モバイル向けでは、iOSは対応していますが、残念ながらAndroidの標準ブラウザが未対応です。そのため、Androidに対応するためには<a href="https://code.google.com/p/fakeworker-js/" title="fakeworker-js" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">fakeworker-js</a>などのPolyfillを利用しましょう。</p>

<h3>メッセージで交換できるデータの種類</h3>

<p>メッセージで交換できるデータについては、Boolean、Number、Stringオブジェクトなどの基本的な型に加え、Date、RegExp、ImageData、File、Blob、FileList、Array、Objectオブジェクトなども可能です。逆に扱えないデータとしては、Error、FunctionなどのオブジェクトとDOMノード、RegExpオブジェクトのlastIndexプロパティなどです。また、setterやgetter、prototypeなどはコピーされません。詳しくは、<a href="http://dev.w3.org/html5/spec-LC/common-dom-interfaces.html#internal-structured-cloning-algorithm" title="Structured Clone Algorithm" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">Structured Clone</a>というアルゴリズムで定義されています。</p>

<h3>ワーカー内で利用できる機能</h3>

<p>ワーカー内では、DOMツリーやwindowオブジェクト、doucmentオブジェクト、parentオブジェクトにアクセスすることができません。ワーカー内で利用できる機能としては、navigatorオブジェクトやlocationオブジェクト（読み取り専用）、XMLHttpRequest、setTimeoutとsetInterval関数、Application Cacheなどです。また、さらに他のワーカーを作成することもできます。ワーカーで利用できる独自の機能には、外部スクリプトをインポートすることができるimportScripts関数などがあります。</p>

<h2>おわりに</h2>

<p>前編と後編にわたって、ユーザーの体感速度を向上させるJavaScriptのチューニング方法について詳しく解説しました。次回からは、引き続きJavaScriptの高速化をテーマに、DOM操作の最適化について解説していく予定です。お楽しみに！</p>

<p>★「<a href="https://html5experts.jp/yoshikawa_t/877/" target="_blank" data-wpel-link="external" rel="follow external noopener noreferrer">ユーザーの体感速度を高めるためのJavaScriptチューニング（前編）</a>」を読む★</p>
]]></content:encoded>
		
		<series:name><![CDATA[パフォーマンスチューニング]]></series:name>
	</item>
		<item>
		<title>ユーザーの体感速度を高めるためのJavaScriptチューニング（前編）</title>
		<link>/yoshikawa_t/877/</link>
		<comments>/yoshikawa_t/877/#respond</comments>
		<pubDate>Thu, 18 Jul 2013 20:00:09 +0000</pubDate>
		<dc:creator><![CDATA[吉川 徹]]></dc:creator>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[Chrome]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[アプリ高速化]]></category>
		<category><![CDATA[パフォーマンス]]></category>

		<guid isPermaLink="false">/?p=877</guid>
		<description><![CDATA[連載： パフォーマンスチューニング (2)連載「Webサイト・アプリ高速化テクニック徹底解説」第2回は、JavaScriptの高速化について、まずは前編、後編に渡ってユーザーの体感速度を向上させるための方法を紹介します。...]]></description>
				<content:encoded><![CDATA[<div class="seriesmeta">連載： <a href="https://html5experts.jp/series/performance-tech/" class="series-149" title="パフォーマンスチューニング" data-wpel-link="internal">パフォーマンスチューニング</a> (2)</div><p>連載「Webサイト・アプリ高速化テクニック徹底解説」第2回は、JavaScriptの高速化について、まずは前編、後編に渡ってユーザーの体感速度を向上させるための方法を紹介します。JavaScriptの同期・非同期の仕組みやscript要素のasync属性、defer属性について詳しく解説します。</p>

<!-- more -->

<h1>ユーザーの体感速度を高めるためのJavaScriptチューニング（前編）</h1>

<p>今回から複数回に分けて、JavaScriptの高速化をテーマに解説していきます。まずは、ユーザーの体感速度を高めるためのJavaScriptチューニングということで、単純なJavaScriptの構文によるスピードを比較するようなものではなく、主にユーザー視点からの高速化を主眼に解説します。その中で、同期・非同期といったJavaScriptの仕組みやscript要素のasync属性、defer属性などについても触れていきます。</p>

<h2>ユーザーの体感速度を向上させる</h2>

<p>一概にJavaScriptの高速化といっても、大きく分けて2つの手法があります。ひとつ目は、実行されるJavaScriptコードを最適化して、個々のコードを高速化することです。大きなボトルネックとなるコードがあれば、効果が高いこともありますが、基本的には数ミリ秒の最適化を積み重ねるものです。</p>

<p>そして、もうひとつは、ユーザーの体感速度を向上させる方法です。JavaScriptの実行速度自体は大きく変わりませんが、ユーザーから見るとあたかもページが早く表示されているように感じます。この方法では、ちょっとしたコードの修正や、基本的な設計変更がWebサイト・アプリの速度を劇的に改善する可能性があります。そのため、まずはユーザーの体感速度を向上させる方法を検討してみると良いでしょう。ユーザーの体感速度を向上させるには、例えば次の3つのものが挙げられます。</p>

<ul>
<li>ページを素早く表示する</li>
<li>ユーザーに素早くインタラクションを返す</li>
<li>ユーザーの操作を阻害しない（次回で解説します）</li>
</ul>

<p>以降は、それぞれについて詳しく解説していきます。</p>

<h2>ページを素早く表示する</h2>

<p>あたり前のことですが、ページを素早く表示することはとても大事です。最初にページにアクセスした際に、ずっと真っ白い画面のままの場合と、少しずつでもコンテンツが表示される場合とでは、ユーザーの体感速度は大きく違います。実は、これにはJavaScriptのコードが大きく影響していることがあります。次のコードを見てみましょう。</p>

<pre><code>:html:
&lt;!DOCTYPE html&gt;
&lt;html lang="ja"&gt;
&lt;head&gt;
    &lt;!-- … --&gt;
    &lt;script src="1.js"&gt;&lt;/script&gt;
    &lt;script src="2.js"&gt;&lt;/script&gt;
    &lt;script src="3.js"&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;!-- コンテンツ --&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>

<p>このコードでは、head要素にscript要素で3つのJavaScriptファイルを読み込んでいます。この場合、すべてのJavaScriptの実行が終わるまで画面の描画が行われず、これらのJavaScriptの実行に時間が掛ると、その分だけ何もコンテンツが表示されない時間が長くなります。このコードを実行した結果をChromeのデベロッパーツールで解析してみると次のようになります。</p>

<div id="attachment_878" style="width: 1034px" class="wp-caption aligncenter"><a href="https://html5experts.jp/wp-content/uploads/2013/07/js-01-01-c.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/07/js-01-01-c-1024x686.png" alt="Chromeのデベロッパーツールで解析したタイムライン（Timelineパネルで記録）" width="1024" height="686" class="size-large wp-image-878" srcset="/wp-content/uploads/2013/07/js-01-01-c-1024x686.png 1024w, /wp-content/uploads/2013/07/js-01-01-c-300x201.png 300w, /wp-content/uploads/2013/07/js-01-01-c-207x138.png 207w, /wp-content/uploads/2013/07/js-01-01-c.png 640w" sizes="(max-width: 1024px) 100vw, 1024px" /></a><p class="wp-caption-text">Chromeのデベロッパーツールで解析したタイムライン（Timelineパネルで記録）</p></div>

<p>図を見るとわかる通り、1.js～3.jsの読込みと実行が順番に行われ、それが終わるまでの間は、body要素以下の表示が行われません。これは、JavaScript内でdocument.write()メソッドによってHTMLの内容が書き換わる可能性があるため、JavaScriptの実行が終わるまでページの表示がブロックされるためです。では、どのようにして素早くページを表示させるのかというと、次のような位置にscript要素を記述します。</p>

<pre><code>:html:
&lt;!DOCTYPE html&gt;
&lt;html lang="ja"&gt;
&lt;head&gt;
    &lt;!-- … --&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;!-- コンテンツ --&gt;
    &lt;script src="1.js"&gt;&lt;/script&gt;
    &lt;script src="2.js"&gt;&lt;/script&gt;
    &lt;script src="3.js"&gt;&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>

<p>よく利用されるテクニックですが、このようにbody要素の最後にscript要素を記述すると、先に表示できるコンテンツを表示し、コンテンツの表示が終わったあとにJavaScriptを実行するようになります。このようにして、ページがすぐに閲覧できるようになり、ユーザーの体感速度を向上することができます。ただし、これらのJavaScriptでユーザーのクリック動作などを設定している場合は、コンテンツが表示されているのにクリックしても反応がない時間などができてしまうので、状況に合わせて使い分けていきましょう。</p>

<p>その他の方法として、script要素にdefer属性とHTML5で追加されたasync属性というものを利用する方法もあります（defer属性は、HTML4で標準化されています）。まずは、先ほどのコードとまったく同じ効果を持つdefer属性について解説します。defer属性は次のように記述します。</p>

<pre><code>:html:
&lt;!DOCTYPE html&gt;
&lt;html lang="ja"&gt;
&lt;head&gt;
    &lt;!-- … --&gt;
    &lt;script src="1.js" defer&gt;&lt;/script&gt;
    &lt;script src="2.js" defer&gt;&lt;/script&gt;
    &lt;script src="3.js" defer&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;!-- コンテンツ --&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>

<p>script要素にdefer属性をつけると、コンテンツの表示が終わったあとに、そのJavaScriptを実行するようになります（正確にはDOMContentLoadedイベントの前になります）。そのため、body要素の最後にscript要素を記述したコードとほぼ同じ結果になりますが、ファイルの読込みは非同期で行われるため、こちらのほうが若干早くなる可能性があります。このdefer属性は、すべてのモダンブラウザ（Chrome、Safari、Opera、Firefox、IEなど）で利用することができます。元々は、IEの独自実装であったため、IEはバージョン4の時代から利用できます。</p>

<p>また、もうひとつのasync属性では、JavaScriptの実行でページの表示をブロックせずに、JavaScriptを非同期で実行することができるようになります。script要素にasync属性をつけると、そのままscript要素以降のコンテンツが表示されていきますが、JavaScriptファイルのダウンロードが終わり次第、そのJavaScriptが実行されます。注意として、async属性をつけたscript要素は、実行順序が保障されていないため、他のJavaScriptと依存関係がないものに限って利用する必要があります。また、実行タイミングも保障されないため、まだ読み込まれていない要素に対するDOM操作をしてしまう可能性もありますので注意しましょう（その場合、onloadイベントなどを利用しましょう）。もし、サンプルで利用している1.js～3.jsがお互いに依存していない場合は、次のように記述することができます。</p>

<pre><code>:html:
&lt;!DOCTYPE html&gt;
&lt;html lang="ja"&gt;
&lt;head&gt;
    &lt;!-- … --&gt;
    &lt;script src="1.js" async&gt;&lt;/script&gt;
    &lt;script src="2.js" async&gt;&lt;/script&gt;
    &lt;script src="3.js" async&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;!-- コンテンツ --&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>

<p>1.js～3.jsはasync属性によって、同時にダウンロードが始まり（ブラウザの同時接続数などにもよります）、完了次第実行されます。そのため、高速に動作するのですが、実行される順番がバラバラになる可能性があります。依存関係があるJavaScript要素には、script要素をそのままで利用するか、defer属性を使いましょう。例えば、2.jsと3.jsに依存関係がある場合には、1.jsにのみasync属性を追加するようにします。async属性は、すべてのモダンブラウザ利用することができますが、IEは9以降の対応となります。といっても、async属性が無視されたとしてもJavaScriptはそのまま実行されるので、非対応のブラウザについてそれほど気にする必要はないでしょう。</p>

<p>async属性とdefer属性を付けたscript要素では、document.write()メソッドが利用できないことと、インライン（script要素に中に直接記述する）でJavaScriptを記述することができませんので注意しましょう。これらの、script要素の位置やasync属性、defer属性などを利用して、なるべくすばやくページが表示されるように気をつけてみましょう。</p>

<h2>ユーザーに素早くインタラクションを返す</h2>

<p>ユーザーの体感速度を向上させるには、ユーザーの行動に対して、素早く何らかのインタラクションを返すことが重要です。例えば、Gmailのメールスレッドにスターを付けるインターフェースを見てみましょう。</p>

<div id="attachment_879" style="width: 837px" class="wp-caption aligncenter"><a href="https://html5experts.jp/wp-content/uploads/2013/07/js-01-02-c.png" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer"><img src="/wp-content/uploads/2013/07/js-01-02-c.png" alt="Gmailのスター" width="827" height="273" class="size-full wp-image-879" srcset="/wp-content/uploads/2013/07/js-01-02-c.png 640w, /wp-content/uploads/2013/07/js-01-02-c-300x99.png 300w, /wp-content/uploads/2013/07/js-01-02-c-207x68.png 207w" sizes="(max-width: 827px) 100vw, 827px" /></a><p class="wp-caption-text">Gmailのスター</p></div>

<p>Gmailでは、メールスレッドにあるブランクのスターをクリックした瞬間にスターが黄色くなるようになっています。実際にスターを付ける処理は、バックグラウンドで非同期に実行されます。このように、他の操作に影響が少ない部分であれば、素早くレスポンスを表示することでアプリがサクサク動いているように見せることができます（もちろん、エラーがあった場合には、適切な通知を行うことが必要です）。</p>

<p>この例では、ユーザーの行動に対して先に結果を表示するというものでしたが、処理に時間がかかるものであれば、プログレスバーやローディングアイコンを表示することも、当然ユーザーに素早くインタラクションを返す一例になります。これは、ユーザーに何もレスポンスがないという状態をなるべく減らすということです。基本的な設計に関わる部分なのでユースケースに合わせて考えてみましょう。</p>

<h2>次回の内容について</h2>

<p>今回は、ユーザーの体感速度を向上させるための手法として「ページを素早く表示する」と「ユーザーに素早くインタラクションを返す」の2つを解説しました。次回は、残りのひとつ「ユーザーの操作を阻害しない」について、JavaScriptのシングルスレッドやイベントループを絡めつつ、HTML5のWeb Workersなどについても解説していきます。</p>

<p>[注]　defer属性は、HTML5ではなくHTML4で標準化されているというご指摘を受け、2013/7/22に修正しました。</p>

<p>「<a href="https://html5experts.jp/yoshikawa_t/448/" data-wpel-link="external" target="_blank" rel="follow external noopener noreferrer">Webサイト・アプリ高速化テクニック徹底解説 （第1回 Webサイト・アプリの高速化ファーストステップ）</a>」を読む</p>
]]></content:encoded>
			<wfw:commentRss>/yoshikawa_t/877/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<series:name><![CDATA[パフォーマンスチューニング]]></series:name>
	</item>
	</channel>
</rss>
