VirtualDOM のパフォーマンステストについて,その後
はじめに
年末に書いたエントリーについて、あちこちで言及がありました*1。
師走の忙しさにかまけて勢いで書いてしまったところも多いので、少し補足をしたいと思います。
Virtual DOMってどんだけ早いの?測ってみた - webとかmacとかやってみようか R
まず、最初に断っておきますが、これは Virtual DOM のパフォーマンスを厳密に測定したものではありません。元ネタはこちらです。Blazing-Fast-Html(翻訳: 【翻訳】爆速HTML – Elmでの仮想DOM)
AltJS の一つである elm という Haskellっぽく FRP を実現できる関数型言語があります。それを作っている Evan Czaplicki 氏のBlog記事です。彼が作った TodoMVC のパフォーマンス比較が github に公開されているので、それを各ライブラリを最新版にして紹介したのが、上述のエントリーでした。
オリジナルのリポジトリが evancz/todomvc-perf-comparison です。すみません、前回リンクを張り忘れていましたね。。まぁ、テストを1回でも実行するとリンクが表示されるのですが。
何を測っているのか?コードリーディング
「このパフォーマンステストはそもそも何を測っているのか?」ということがよく聞かれます。DNSルックアップ?コネクション確立?ネットワーク転送?HTMLパース?JS評価?DOM構築?レンダリング? 現在のWebはページ一つ表示するだけでも様々な要因があります。テストコードが公開されてるのでそれを読めばいいのですが、今回はそこを詳しく見てみましょう。
このテストのメインページはindex.html
です。ここで読み込んでいるスクリプトは以下の3つ。
- resources/benchmark-runner.js
- resources/tests.js
- resources/manager.js
(※正確には結果のグラフを表示するためにjsapi
から GoogleCharts を読み込んでいますが、これはテストとは関係ないために割愛。)
最初にmanager.js
でテストの準備を行います。こちら見ると r
パラメータでテスト回数を指定できるようになっているようですね。
テスト対象はtests.js
で定義されています。Suites
配列にライブラリ単位でテストスイート追加し、TodoMVC の追加/完了/削除のステップ毎にBenchmarkTestStep
オブジェクトを設定しています。
テストの実行はbenchmark-runner.js
に定義されているBenchmarkRunner
が使われます。このstep
関数で_runTestAndRecordResults
関数を呼ぶことで各テストステップを実行するのですが、テストスイートの初回実行時は iframe にテスト用htmlを読み込み、iframe の onload を待ってから_runTestAndRecordResults
関数を呼び出します。
_runTestAndRecordResults
ではテストの各ステップを取り出し、_runTest
関数で実際のテストステップ(tests.js
で登録したBenchmarkTestStep
)を実行します。
_runTest
では、テストステップを呼び出し、その前後の時刻を取得し、実行時間を計測しています。時刻を取得するには Navigation Timing API の performance.now
が利用可能であればそれを使うようです。
このとき興味深いのが、計測する時間はテストステップ関数の実行時間だけでなく、その直後に 以下のようにsetTimeout
で delay 0 にして非同期の経過時間も取得していました。
var startTime = now(); setTimeout(function () { setTimeout(function () { var endTime = now(); callback(syncTime, endTime - startTime); }, 0) }, 0);
これはレンダリング時間を計測しようとしているのでしょうか?それともライブラリ内での遅延処理分を計測するためでしょうか。ちょっとよく分かりません。。教えて詳しい人!
とりあえずこのテストでは、この同期時間と非同期時間を足したものを処理時間として計測しているようです。テスト結果は、各ステップの処理時間の合計をグラフとして表示します。
各ライブラリのテストステップ
各ライブラリのテストステップではおおよそ次のようなことが実行されています。
アイテムの追加
input要素に値をセットして、keydown
(またはkeypress
、keyup
)イベントを keyCode = 13
で発火、というのを100回繰り返します。なお、EmberやAngularでは input要素に値を直接入れるのではなく、Viewモデル経由だったり、inputイベントを発火したりする。
発火するキーイベントや、値のセットがライブラリ毎に微妙に違うのは、おそらくそれぞれの TodoMVC の作り方が違うためだと思われます。
アイテムの完了
document.querySelectorAll('.toggle')
でチェックボックス要素を取得し、それらを全部click()
でクリックイベントを発火させます。
アイテムの削除
対象が '.destroy'
になっただけで完了とほぼ同じ。
各ライブラリの TodoMVC
各ライブラリの TodoMVC の実装がどうなっているのか見てみました。基本的には GitHub の tastejs/todomvc にあるものをそのまま使っている模様。
一応参考までにリストアップします。
- Backbone.js TodoMVC Example
- 公式
- AngularJS (Performance Optimized) TodoMVC Example
- こちらは、 examples/angularjs ではなく、パフォーマンスチューニングされたものらしい。
- Ember.js TodoMVC Example
- 公式
- Om TodoMVC Example
- lab扱いのサンプル
- mercury TodoMVC Example
- tastejs/todomvc ではなく、mercury開発者のリポジトリ内
- Elm TodoMVC Example
- おそらく、evancz/elm-todomvc をコンパイルしたもの。
まとめ
だいぶ疲れてきたので、箇条書きでまとめます。
- 計測しているのは、ネットワーク部分は関係なく、純粋に追加/完了/削除の処理時間のみ。
- 処理時間は同期処理と非同期処理の合計だが、非同期処理時間の計測にはやや疑問が残る。
- 各ライブラリのTodoMVC 自体は本家のものやパフォーマンス考慮されたもののため、大きな問題は無さそう。
- 処理時間はハード/OS/ブラウザの状態に大きく影響されるため、複数回計測した方が良い。
なお、元サイトの画像をよく見ると、
「Average time in milliseconds over 16 runs」 と書いてある。つまり16回やった平均値のグラフのようです。実行回数はr=10
のようにURLパラメータで指定します。
同じ VirtualDOM を使っているのに、Om、Mercury、Elm はなぜ React よりも早いのか?についても調べたかったのですが、時間切れのためまた次回。
余談
ところで、この evancz のテストコードも実は別のリポジトリのフォークです。大元は Matt-Esch/mercury-perf。 あれ、この人は、、と気がついた方はさすがですね。React とは別の VirtualDOM 実装 Matt-Esch/virtual-dom を作った方です。Uber で @raynos と共に mercury を作ってるようです。
話が逸れた。で、evancz は Matt-Esch のコードをフォークして、グラフに表示するようにしたようです。テストの実施や計測部分はほとんどいじってないように見えます。
追記
羽田野さん(@futomi)からfacebookでコメント頂きました!
> このとき興味深いのが、計測する時間はテストステップ関数の実行時間だけでなく、その直後に 以下のように setTimeout で delay 0 にして非同期の経過時間も取得していました。
私自身、コードリーディングしたわけではないので、確かなことはいませんが、たぶん、お察しの通り、レンダリングの時間を計測していると思われます。私もよく似たコードを書いたことがあります。
実は、DOM 操作が完了してから、実際にレンダリングが完了するまでには時間差があります。テレビなどの組み込み系デバイスだと、これが顕著に出ます。
しかし、レンダリング完了をスクリプトから知ることができません。では、それをどうやって計測するかというと、ブラウジングコンテキストはシングルスレッドで動作している点を利用します。
レンダリング処理を行っている最中は、シングルスレッドゆえに、タイマーですら待たされます。そこで、DOM 操作が終わった直後に、最短で実行するタイマー(ここでは、setTimeout を 0 msで指定してますが、実際にはブラウザーは 5ms として処理します)をセットします。5ms でレンダリング処理が終わらなければ、このタイマー処理は待たされます。そして、レンダリングが終わり次第、キューに貯められた、このタイマー処理が実行されます。
これでレンダリング完了までの時間を計測することができます。もしレンダリング処理が 5ms 未満で完了してしまうような高性能なマシンだと、計測は若干不利な結果になりるはずです。
ありがとうございます!なるほど。やはりそうですか。私もそういったコードをたまに書きます。特にCSSのdisplayプロパティを変更した後など。2回タイマーをネストしてるのはレンダリング完了が間に合わなかった場合のため、2回キューに積んでいるんですかねぇ。Matt-Esch本人に聞いてみたい!
最近書いたモノ: Web関連技術とかIoTとかフリーランスとか
最近書いたモノなどをBlogに書いてなかったのでまとめとく。
先月はスライド2本、コラム1本など。
オピニオン:高岡大介: フリーランスに対する5つの誤解と、気を付けている点 - Build Insider
うん。改めてみるとものすごくバラバラだw
こうゆうのなんて言うんだっけ。
興味の幅広い、多趣味、器用貧乏人、多芸は無芸?
いやいや、段々ネガティブになってるがな。
入り口として広く浅く勉強することは必要だけど、ある程度の深さも欲しいね。
来年は広く深くを目指してがんばろう。
Virtual DOMってどんだけ早いの?測ってみた
この記事は VirtualDOM Advent Calendar 2014 - Qiita の2日目です。
mizchi くんから誘われて軽い気持ちで参加したら、初日からえらくエモいエントリー(VirtualDom - なぜ仮想DOMという概念が俺達の魂を震えさせるのか - Qiita) でブルってます。
Virutal DOMとは、と言う話はしません。初日を見てください。いろいろ良いことあるみたいだけど、Virtual DOMってどんだけ早いの?知りたいですよね。
Elmの中の人が作ったTodoMVCのパフォーマンステストがあります。
いつものTodoMVCのデモで、100要素追加して、全て完了して、削除するというテストです。
「Run All」ボタンをクリックすると動きます。
http://evancz.github.io/todomvc-perf-comparison/
Virtual DOMを採用している Om, Mercury, Elm は圧倒的に早い!Angularは、、まぁこういうの苦手だよね。。 (localStorageに値を保存するので、一度クリアしたほうが正確に測れます)
ん?ちょとまて! React も Virtual DOM使ってるんじゃなかったのかよ。
Backboneに負けるのかよ!!
よく見ると、React のバージョンが 0.10.0 とちょっと古いようです。(2014/12/02現在最新は 0.12.1)
どうせなので、全て最新版にするとどうなるのかForkして作ってみました。
http://dsuket.github.io/todomvc-perf-comparison/
んー、React 微妙な感じ。。。
Mercury と Elm は Matt-Esch/virtual-dom 実装を使っているのですが、Om は内部的に React の Virtual DOM を使っています。この違いはなんなんでしょうね。。
Om, Mercury, Elm は、関数型で Immutable なデータ操作を行います。もしかしてしてこれがJITコンパイラとかに効いているのかな?私が試したのは Chrome 39 でした。他のブラウザでは違う結果になりそうですね。
初日のエントリーにもあったように、Virtual DOM は Immutable なデータ構造と非常に相性が良いです。特にデータをストリームとして扱う FRP では強力ですね。
ところで、最近 MVI(Reactive MVC and the Virtual DOM — Futurice) のような話しもありました。また新手の MV* かよ、MVVMの派生系じゃないのかと思ったのですが、FRP のパターンとしてみると使えそうな気もします。そうみると、以前私は Flux は要らないといったのですが、これも FRP に当てはめると良さそうですね。Bacon.js で Flux を作ってみた例(Flux inspired reactive data flow using React and Bacon.js - Reaktor)なんか面白そうです。
最後は憶測で雑でしたが、そういったアーキテクチャを支える技術として Virtual DOM があります。逆に言うと、Virtual DOM のおかげで新しいアーキテクチャが実現可能になりました。まだまだ発展途上ですので、今後どんなアーキテクチャやフレームワークが産まれるのか楽しみですね!
結論:
Virtual DOM 使うなら関数型(FRP)!
elm最高!!
追記
フォロー記事書きました。
VirtualDOM のパフォーマンステストについて,その後 - webとかmacとかやってみようか R
Webフロントエンドでリアクティブプログラミング
お世話になってるところで、最近気になっている「Webフロントエンドでリアクティブプログラミング」について記事を書かせて頂きました。
以前からちょこちょこ調べてはいたのですが、いい機会なのでまとめていたら結構な文量に。。
7000字弱は疲れた!!
記事中では elm 押しで書いているのですが、実際のことを考えると Bacon.js がまずは良さそうです。
elm は Haskell をマスターしてからでないと辛すぎるw
Enterprise HTML5 Conference に行ってきたよ
表題の通り、昨日開催された html5jエンプラ部のカンファレンスに行ってまいりました。
最近めっきりBlogも書いてなかったのですが、帰ってBlog書くまでがカンファレンスです!と言われたので、色々と思ったことなどを交えながらひとつレポートでも書いてみようと思う次第であります。
Html5Bizカンファレンスとしては第1回目で、平日の昼間、しかも都心から離れ場所にも関わらず、なかなかの盛況ぶりでした。オープンソースカンファレンス(OSC)と同時開催というも良かったのかもしれませんね。OSCのほうもちょっと覗いてきましたが、場所が大学ということもあるのかまさに文化祭のような雰囲気が楽しいですね。
さて、こちらのカンファレンスの内容ですが、私が参加したセッションを中心に簡単にレポートしてみたいと思います。
パネルディスカッション「HTML5とエンタープライズITの向かう先」
オープニングセッションです。これからのエンタープライズWebはどうなっていくかというテーマでのセッションでなかなか興味深いテーマでした。HTML5の華やかな機能の話よりも、進化し続けるブラウザや仕様にたいして、どのように運用・保守を続けていくのか、などまさにエンタープライズっぽい話題でした。
「ぶっちゃけMSさんとしてIE8はどうなんですか?」という白石さんの爆弾ツッコミに対して、「まずは皆さんに愛されながらもサポートが終了するIE6についてから」、とさらりと交わす春日井さんさすがです。
その他にもMEAPの話題でSencha Spaceが出たことに個人的に驚き!オープニングセッションでSencha Spaceの名前が出るなんて!ww
HTML5を活用した効果の高いリッチアプリ開発体制構築とコンサルティングの実現方法
Sencha UGとしては聞いておかざるを得ないですね。資料が上がっています。 http://www.slideshare.net/kotsutsumi/html5-31768731
UGとは違い、さすがにスポンサーセッションぽい感じでしたね。小堤さんがえらくゆっくりしゃべってるなぁ、と思ったら会場のマイクが微妙に遅れるせいでしゃべれなかった、とのこと。そういえば最初のパネルもだいぶ聞き取り辛かったですもんねぇ。
リッチアプリケーション開発を助けるOSSのJSフレームワーク「AngularJS」の魅力
図らずも、時間帯が被り Sencha vs Angular となってしまったセッション!UG1周年、デブサミ、そして今回と3回目の対決w 私は聞けませんでしたが、参加者すごかったですね。大盛況のようで。
金井さんと吉田さんの資料見て私も勉強します!
Webアプリ開発者のためのHTML5セキュリティ入門
HTML5の要素技術の基本的なところから、その脆弱性、対応方法までを丁寧にまとめられていました。非常に勉強になりました。
資料は即日はてブ500over! http://www.slideshare.net/muneakinishimura/webhtml5-31749532
「安全なウェブサイトの作り方」へ追加されるべき!!
オープンソースで始めるオフラインアプリケーション開発入門
http://www.slideshare.net/sagawafumio/ss-31750106
佐川さんのセッションです。HTML5で作る次世代Webアプリの要素技術を色々と紹介して頂きました。
Yeoman、私も使っていて、何も考えなければすごく簡単に始められ確かに便利です。しかし、実際のプロジェクトでは自前のテンプレートとかをちゃんと作る必要もあり、案外導入が面倒くさい気がします。
WebStorageやApplicationCacheを積極的に使ったオフラインSPAは、特に前述のセキュリティを気をつけないと、色々と嵌まりそうだと感じました。HTML5オフラインSPAでのセキュリティリスク、なんて講演タイトル聞きたいですね〜。誰か是非!w
OSSのブラウザ自動テストツール「Selenium」を使った、開発・テストの効率化
数年前にSeleniumは使ったのですが、当時は iPhone/iPadで使えなかったため残念だった思いがあります。最近では Appium、iOS-drive、Selendroid など、3rd Party のDriverが充実し、Webどころかアプリもテストできるそうです。これはいいことを聞いた!是非今度使ってみたい思います。
Seleniumを使ったからといって、テスト工数が0になるわけでなく、テストコードのメンテナンスは常に必要だし、IDEで自動生成できるとは言え実用的な物はやはり人手(職人)が作る必要がある。じゃあ、Seleniumを使って何がいいの?それは、これまでの「守りのテスト」から、絶えず素早くソフトウェアをリリースし続けるという「攻めのテスト」を実現させることです。 という下りはなかなか熱かったです。
JavaエンタープライズアーキテクチャにおけるHTML5
http://www.slideshare.net/yusuke/javahtml5
JJUGの鈴木さんの講演。てっきりJava8の話かと思ったらいい意味で予想を裏切り全然想定外のテーマでした。途中、タイトルから Java 抜けてるしww
大局的な話から個別詳細の話まで論理的に淀みなく終始安定しておられたのはさすが会長です。内容はもちろんのことプレゼンそのものにもいたく感動いたしました。これは資料を見ただけでは分からない。実際に聞いていてこそ伝わる物でしょうね、それだけでもこのカンファレンスへ来た甲斐がありました。
おまいらHTML5に夢見過ぎんなよ、業務はそこまで甘くねーぞ、というメッセージにがつんとやられ、とはいえそういう要望も増えているため、どういうところで適応できるのか、という非常にエンプラっぽい現実的なお話しで、はっとさせられるました。
世界のビールが飲める!懇親会
ドイツビールを中心にワールドインポートビアが沢山飲めるというすごく期待の懇親会!!実際すごく量も種類もあってビビりました。どうせ少ないんじゃないかと、なめてました。ごめんなさい^^;
まず頂いたのは ドイツのピルスナー Kaiserdom。ドイツ南部バイエルン地方のバンベルクにある、1718年から続く由緒ある醸造所のビールです。ドイツビールらしい堅実な味。癖は無く、ビールらしさを感じる、私は好きな味です。
続いては、ホフブロイ・オクトーバーフェストビア。本番ミュンヘンのオクトーバーフェストでホフブロイ・ミュンヘン醸造所が出しているビールが期間限定で発売!お台場とか日比谷とかで何月かよく分からない時期にやってるのとは違いますね。こちらは、先ほどのビールよりもスッキリとして香りもやや華やかで飲みやすい。まさにお祭りで飲むのに最適なビールですね!
3本目は、同じくホフブロイの黒ビール。ドゥンケル。「バイエルン国王のためのビール」醸造所として1589年に創設され、当時から醸造されている初めてのダークビール。伝統的なミュンヘンスタイルはバイエルンビールの原型。ほのかなキャラメル香、口当たりまろやかで味の切れが良く、ダークレッドブラウンが特徴。とのこと。ダークビールですが、苦みはそれほどなく、むしろまろやかで美味しい。これは好きな味です。3本目あたりに調度良いですね。
この後、白ビール、なんだか分からないビールなど、何本か頂きましたが、残念ながら写真と記憶はすでに失われてました。。。
何のレポートだかよく分からなくなってしまいましたが、このような素晴らしいカンファレンス(懇親会)を実現まで持っていかれたスタッフの皆様、並びにスポンサーの皆様 ありがとうございました!ごちそうさまでした!!
Sencha Touch と ExtJS
Sencha Advent Calendar 2013 7日目です。
最近、私が Sencha に詳しいと言うことで、とある仕事で Sencha を使うことになりました。しかし、Sencha でも ExtJS の方。私が得意なのは Touch なんだけど。。。最近の ExtJS は全く知らない(汗
とはいえ、ExtJS4 になって Ext.define とか MVC とか Sencha cmd とか似たような感じっぽい。というよりむしろ Touch の元になったものなので、何とかなるだろうと始めてみました。最初は使うコンポーネントが違うくらいだろうと思ったのですが、使い出していると意外と細かいところで違うものなんだなぁ、と気がつきました。
というわけで、今日のテーマは「ここが違うよ!ExtJS と Sencha Touch」
Sencha Touch と ExtJS の両方をある程度知っている人にしか分からない、細かすぎて伝わらないネタです。
configが違う
Sencha Touchでは、Ext.define で渡すコンフィグオブジェクトに、config という名前のプロパティオブジェクトを付けると、それは特殊なconfigプロパティとして認識されます。例えば、config オブジェクトに name というプロパティをセットすると、自動的に updateName や applyName というメソッドが追加されます。それらをオーバーライドすることで、各プロパティをセットしたときのフックが定義できます。
ExtJSでも config は使えるのですが、各種コンポーネントがこの作りに対応していません。Touchのコンポーネントは基本的にこのconfigを使って作られているため、プロパティのフックが簡単に行えるのですが、ExtJSではできません。そのうち対応してくれると嬉しいなぁ。
コントローラーが違う
ExtJSも4からMVCアーキテクチャが導入され、Controllerクラスが使えます。ただし、これも Touch のController と少し違います。
ExtJSのControllerの定義は次のような感じ。
Ext.define('MyApp.controller.Users', { extend: 'Ext.app.Controller', refs: [{ ref: 'list', selector: 'grid' }], init: function() { this.control({ 'button': { click: this.refreshGrid } }); }, refreshGrid: function() { this.getList().store.load(); } });
対して、Touchのコントローラーはこんな感じ。
Ext.define('MyApp.controller.Main', { extend: 'Ext.app.Controller', config: { control: { loginButton: { tap: 'doLogin' }, 'button[action=logout]': { tap: 'doLogout' } }, refs: { loginButton: 'button[action=login]' } }, doLogin: function() { //called whenever the Login button is tapped }, doLogout: function() { //called whenever any Button with action=logout is tapped } });
refs で コンポーネントをComponentQuery で指定するのはほとんど同じです。しかし、control定義が全然違う。少し驚きですね。また、ExtJS の control定義では、refs で定義した名前が使えない!!これにはやられました。Touchから入った人ははまるポイントではないでしょうか。(ってそんな人いるのか・・?)
bootloaderが違う
Sencha Cmdで、sencha generate app でアプリケーションを作るときに、sdkパスを Touch か ExtJSを切り替えるだけで、どっちのアプリケーションでも簡単に作れます。index.html も作ってくれるのですが、これが読み込む JS がまた違う。
Touch のほうは、microloader というのを読み込んで、それがアプリケーションを読み込んでくれます。この microloader を development か production を切り替えるだけで開発と本番を切り替えられます。次のような index.htmlが作られます。
<!DOCTYPE HTML> <html manifest="" lang="en-US"> <head> <meta charset="UTF-8"> <title>Sample</title> <style type="text/css">/* 略 */</style> <script id="microloader" type="text/javascript" src=".sencha/app/microloader/development.js"></script> </head> <body> <div id="appLoadingIndicator"> <div></div> <div></div> <div></div> </div> </body> </html>
ExtJSは全然違います。次は ExtJS の index.html です。
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>Sample</title> <!-- <x-compile> --> <!-- <x-bootstrap> --> <link rel="stylesheet" href="bootstrap.css"> <script src="ext/ext-dev.js"></script> <script src="bootstrap.js"></script> <!-- </x-bootstrap> --> <script src="app.js"></script> <!-- </x-compile> --> </head> <body></body> </html>
bootstrap という 似たような物があるのですが、microloaderとはまた別の物。これが結構くせ者で、ビルドする度にパスが変わったりで慣れるまで苦労しました。実は ExtJS のテンプレートの方にも .sencha/app/microloader/ があります。これが使えるのか検証してないですが、使えるようになってくれると嬉しい。
モバイルWebアプリのこれまでとこれから。
バタバタしていて遅れましたが、先日とあるセミナーでお話しさせて頂きました。
すみません、最初にお詫びします。「Sencha Touch VS jQuery Mobile」というタイトルですが、その内容は一部しか出てきません(--;
別件で調査していた内容を盛り込むでいくと、どんどんそっちが膨らみ、「モバイルWebアプリのこれまでとこれから」という壮大な内容になってしまいました。副題がメインを喰ってしまった!しかも、無駄に枚数膨らんで全部で186p!w
(主題の方を知りたい方は以前作ったスライドのほうをどうぞ)
スライドに盛り込めなかったフレームワークはまだまだ沢山あって、いつかまとめて書いてみたいです。
また、最後の BassS と Webサービスのリベンジ というところ、全く私の勝手な妄想ですが皆さんはどうお考えでしょうか??まだ全然詰め切れてないですが、もう少し考えてみるべき面白そうな議題と思っています。ぜひコメントなど頂けると嬉しいです!