Technologies for UI
ma.la
- 自己紹介
- 今日のテーマ
- livedoor Readerの紹介
- livedoor Readerの仕組み(Ajaxについて)
- デスクトップアプリとウェブアプリ
- 従来のウェブアプリとAjaxの対比
- クライアントサイドのチューニング
- サーバーサイドのチューニング
- まとめ
- 今後の方向性とか
- 終わり
次ページ
自己紹介
2006年2月1日ライブドア入社
メディア事業部 開発部所属 プログラマ
livedoor Readerなんかを作ってます
次ページ
個人的活動
最速インターフェース研究会
主任研究員
次ページ
自己紹介
終わり
次ページ
今日のテーマ
ユーザーインターフェース、 ユーザー体験のための 技術的アプローチ
次ページ
ユーザーインターフェースのための技術
技術者の視点からの発想 livedoor Readerでの工夫など
次ページ
livedoor Readerの紹介
2006年4月リリース(beta版) 登録ベースでそろそろ10万ユーザー 3日以内ログイン 7000-9000人
次ページ
リリース後の機能
サーバー分散したり livedoorクリップ連携 登録フィードの公開機能
次ページ
livedoor の採用しているテクノロジ
FreeBSD Apache2 / Apache1.3 MySQL mod_perl Sledge
次ページ
livedoor の採用しているテクノロジ
去年やったので省略
次ページ
livedoor Readerのバックエンド
いつもの感じ Apache2.2 + mod_proxy_balancer Apache1.3 + mod_perl + Sledge MySQL 4.0 クローラーにXango
次ページ
livedoor Readerのバックエンド
専門外なので省略
次ページ
今回は
ユーザーインターフェースのお話
次ページ
livedoor Readerの仕組み(Ajaxについて)
次ページ
livedoor ReaderはAjax採用
さいきん よくきくけど Ajaxってなに
次ページ
Ajaxってなに?
Asynchronous JavaScript and XML の略らしい
次ページ
livedoor Readerの仕組み
サーバーサイドはJSONに変換してデータを渡す あとの動作はクライアントサイドにゆだねる クライアントサイドはXMLHttpRequestでデータを受信
次ページ
livedoor Readerの仕組み
まるごとPerlって本に詳しく書いてあります 省略
次ページ
livedoor Reeaderの簡単な紹介
終わり
次ページ
デスクトップアプリとウェブアプリ
ウェブアプリが デスクトップアプリを 上回る可能性について
次ページ
ウェブアプリのメリットはいろいろ
インストールしなくていい 勝手にバージョンアップ ネットさえあればどこでも使える ハイパーリンクで連携できる 良いものは爆発的に広まる
次ページ
ウェブアプリの欠点
ブラウザ上で大量のデータを取り扱えない OSネイティブのGUI上のアプリケーションと比べると 描画パフォーマンスが圧倒的に悪い
次ページ
ウェブアプリの最大のメリット
必要なデータだけ受信すればいい。
次ページ
メールソフト
どれぐらいのメールを扱えるか? 10万件?100万件?
次ページ
メールソフト
Becky? ThunderBird? 10万件のメールが入ったフォルダを開いたらフリーズした=> よくある
次ページ
ウェブアプリなら
=> 先頭の10件だけを受け取ればいい。=> 検索結果だけを受け取ればいい。
次ページ
RSSリーダー
ウェブアプリのメリットが大きい=> ブラウザでそのまま記事を開ける=> 自前でクロールしなくていい=> 更新情報だけ受け取ればいい
次ページ
ウェブアプリが勝ちうるためには #1
大量のデータをサーバーサイドで高速に処理する=> 必要なデータだけを逐次、受信、描画する=> デスクトップアプリでは代替の効かない領域
次ページ
ウェブアプリが勝ちうるためには #2
ネットワーク(ソーシャル)連携で勝負する=> 集合知、統計情報を利用したフィルタリング
次ページ
ウェブアプリが勝ちうるためには #3
正しいUIで勝負する=> デスクトップアプリのUIは間違いだらけ
次ページ
正しいUIってなに?
正しさ = 速さ
次ページ
正しいUIってなに?
ユーザーがタスクを実行する際に=> 素早く=> 迷わず=> 正確に 実行できるかどうか
次ページ
正しいUIってなに?
結局のところ 速さを見れば正しさが分かる
次ページ
常識的な設計がアプリケーションを殺す
平均的な企業ユーザーが受け取るメールは1日91通 http://enterprise.watch.impress.co.jp/cda/topic/2006/06/20/8084.html 購読しているフィードの件数: 50%のユーザーが5個以下 http://japan.internet.com/research/20060929/1.html
次ページ
常識的な設計が最大のタブー
平均値をあてにしてはいけない
次ページ
平均値に基づいた設計をしない
5件しか読まない前提で設計されたRSSリーダーは ユーザーインターフェースが洗練されない
次ページ
使いづらいから使われない
RSS Bar時代=> 30 フィード
Bloglines時代=> 700フィード
次ページ
使いづらいから使われない
livedoor Reader=> 2580フィード(2006年12月現在)
次ページ
livedoor Readerの特徴
ユーザーあたりの購読数が べらぼうに多い
次ページ
統計の不一致: ブロガー調査
アクセス解析を見ると圧倒的なシェア
次ページ
統計の不一致: アンケート調査
ユーザーに調査するとそんなでもない=> http://japan.internet.co(省略されました)
次ページ
わかりやすくいうと
次ページ
ウェブアプリとデスクトップアプリ
終わり
次ページ
従来のウェブアプリとAjaxの対比
従来のウェブアプリと なにが違うか Ajaxを使うメリットとは何か
次ページ
米Yahooの調査
最近の記事 http://yuiblog.com/blog/2006/11/28/performance-research-part-1/
次ページ
レンダリング時間
HTMLの転送にかかる時間より その他のファイルの受信と レンダリングにかかる時間の方が はるかに長い
次ページ
80:20の法則
その他の処理:HTMLの受信 =「80:20」
次ページ
「UIの速さ」は「サーバー速度」ではない
ページの遷移には雑多な仕事が付きまとう! キャッシュ保存、ヒストリの保存 ローディング中の表示、タイトルバーの書き換え 表示回数の記録、セキュリティポリシーの適用 メモリの開放、JavaScriptの初期化、RSSオートディスカバリ
次ページ
Ajaxを使う
画面遷移を無くす 単純計算で8割の時間を削れる=> ユーザーの待ち時間が純粋にデータの転送速度だけで済む
次ページ
実例をdankogaiを交えて紹介します
次ページ
livedoor Blog
404 Blog Not Found
次ページ
はてなダイアリー
404 Hatena::Diary not Found(旧称:はてな弾アリー)
次ページ
HTML単体のダウンロード速度
LWP::Simpleでダウンロード=> blog.livedoor.jp/dankogai/ : 0.112308秒=> d.hatena.ne.jp/dankogai/ : 0.301575秒
livedoor Blogの方が3倍速い
次ページ
ページの描画が完了するまでの時間
Firefox2.0 + Fasterfoxで計測=> blog.livedoor.jp/dankogai/ : 11.67秒=> d.hatena.ne.jp/dankogai/ : 2.37秒
hatenaの方が5倍速い
次ページ
HTMLの受信とその他の速度の割合
livedoor Blog => 1:99 はてなダイアリー => 1:8
次ページ
HTMLの受信とその他の速度の割合
2:8ってレベルじゃない
次ページ
アニメーションで解説
次ページ
中身違うのでロード時間が違うのは当然です
もちろんこのベンチマークはフェアじゃないけれど=> ユーザーが感じるのは紛れもなく「レンダリング速度」=> 誰もApacheの応答速度なんか気にしちゃいない
次ページ
サーバーの高速化は部分最適化
サーバーの高速化は重要=> サーバーだけ速くても「その他の部分」はどうにもならない=> ユーザーの待ち時間を「減らす」ことにしかならない。
次ページ
待ち時間を「無くす」にはどうすればいいか
livedoor Readerの場合=> ユーザーの待ち時間を「無くす」=> ユーザーのアイドル時間を利用する=> 見かけ上のレスポンスを向上させる
次ページ
今までのウェブアプリケーション
ユーザーの待ち時間 = サーバーの応答速度 + 回線状況 + 全体のレンダリング速度
次ページ
livedoor Readerの場合
ユーザーの待ち時間 = サーバー応答速度 or メモリ速度 + 部分のレンダリング速度
次ページ
Ajaxとdankogaiの話
終わり
次ページ
クライアントサイドのチューニング
livedoor Readerにおける
クライアントサイドの
チューニング
次ページ
サーバーの応答速度に頼った設計をしない
サーバー速度を上げても部分最適化にしかならない サーバーが速くてもユーザーの回線が速いとは限らない
次ページ
クライアントサイドでキャッシュ
一度受信したフィードのデータはキャッシュしておく サーバーの負荷を最小限に抑えられる 手前のフィードに戻る時にキャッシュを使う
次ページ
キャッシュを使って先読みする
キャッシュクラスを作ったら 次に読む記事をあらかじめキャッシュに入れておく=> 簡単にできます
次ページ
ブラウザのキャッシュよりも自前のキャッシュ
XMLHttpRequestはGETメソッドであればキャッシュしてくれる。 どのタイミングで消えるか分からない。 XHRオブジェクト生成のコストがかかる。
次ページ
JavaScriptの処理中、ブラウザは停止する
JavaScriptはシングルスレッド タイマーを使った擬似スレッド処理が可能=> 複雑な機能を分割し、callbackを受け取るように作る
次ページ
DOM vs innerHTML
JSONからHTMLへのレンダリングをどうするか。 大雑把に言うと二通り=> 文字列でHTMLを組み立ててinnerHTMLに代入=> DOM操作でHTMLを組み立ててappendChild
次ページ
速度に関する多くの誤解
結論=> DOMプロパティの読み書きや=> DOMメソッドの呼び出しが遅い=> ケースバイケースで適切な方法を選んで使う。
次ページ
innerHTMLを使う
innerHTMLは元々IEの勝手な拡張=> 一部の人に嫌われる理由 使われまくってるので今更なくなるということはない
次ページ
良くある間違い
element.innerHTML += "hoge"=> 追記にはならない。 element.innerHTML = element.innerHTML + "hoge" と同等。=> まったく最適化されない。
次ページ
JavaScript内で処理する
innerHTMLに頻繁にアクセスしない。 複雑なHTMLを組み立てる場合は「JavaScript」の変数内で作る。 最後に一気にまとめてinnerHTMLに代入する。
次ページ
ベストプラクティス
見た目の変更: styleで。 大幅な見た目の変更:classNameで。 追加と挿入: appendChild/insertBeforeで。 大幅な書き換え:innerHTMLで。 間違ったことをしなければ極端に遅くなることはない。
次ページ
レンダリングを妨げないための工夫
先頭の一件だけ先に描画して 次以降はゆっくり描画する 記事データ自体はこの段階で全件ロード済み=> 通信の待ち時間ではない
次ページ
ブラウザの負担を下げる
読む順番で逐次レンダリング 一度に大量の記事をフォーマットするとブラクラになる=> 「できるけどやらない」
次ページ
クライアントサイドチューニング
終わり
次ページ
サーバーサイドのチューニング
livedoor Readerにおける
サーバーサイドの
チューニング
次ページ
平均値はあてにならない
レスポンスタイムを見てみる。 平均すれば0.2秒 最短で0.1秒 最長で240秒 これでは使い物にならない。
次ページ
マイフィードの読み込みが遅い
記事は先読みしているので気にならない 利用可能になるまでの時間が長いのが気になる 時間が掛かりすぎるとブラウザがタイムアウトする=> まったくつかえない状態になる
次ページ
クライアントサイドでの対応(2006年9月)
フィードリストを分割ロード=> 初回の100件はとりあえず速く返す=> 次回以降は200件ずつロード 利用開始になるまでの時間が短くて済む 効果はあった=> いずれにせよ重い
次ページ
サーバーサイドの対応(2006年11月)
丁度この時期に突発的にチューニングがしたくなった
次ページ
よくよく調べてみたら
購読リストを取得するのは速い(SQL発行1回)=> 未読件数を計算(未読フィードの件数)=> フィードの情報を取得(購読フィードの件数) 1000フィード読んでいたら最大で2001回 購読数増えるほど重くなる これはよくない
次ページ
チューニング
memcachedを使うように。
次ページ
memcached って
http://www.danga.com/memcached/ ググれ
次ページ
未読件数計算の最適化 #1
フィードごとの未読件数はキャッシュしない=> ユーザー * フィード数だけ必要になる=> 読むたび更新されるので効率が悪い
次ページ
未読件数計算の最適化 #2
フィードごとにstored_on(記事の保存時刻)を200件分保存して ユーザーがフィードを読んだ時刻と照らし合わせて未読件数を計算するようにした
次ページ
どのデータをキャッシュすべきか
一度キャッシュした値が全体で共有できるかどうか 全ユーザーで共通化できる部分を見つけてキャッシュする
次ページ
チューニング結果 #1
未読件数算出: SELECT COUNT(*) 200回=> memcached 1回 フィード情報: retrieve 200回=> memcached 1回
次ページ
チューニング結果 #1
キャッシュに入っていない分は追加で取得してキャッシュする キャッシュが全てヒットすればSQL発行は1回で済む
次ページ
チューニング結果 #2
レスポンス速度=> キャッシュがヒットすれば1/10以下。=> 平均して約3分の1に
次ページ
memcachedの高速化 #1
CPANモジュールCache::Memcachedを使用 get_multiメソッドを使うと複数のキーを一度に取得できる=> 100件の場合で一件ずつ取得するより30%ほど速くなる
次ページ
memcachedの高速化 #2
保持期限を長めにしてヒット率を上げる 代わりにクローラの更新タイミングで確実にキャッシュを消す
次ページ
memcachedのtips
deleteの際に上書きを禁止する秒数を指定できる=> 入れ違いで古い内容が書き込まれるのを防ぐ レプリケーションの遅延などで古い値が参照されることがある
次ページ
待たせないための工夫 #1
キャッシュが全く作られていない場合、今までよりも遅い キャッシュのヒット率に合わせて処理方法を動的に切り替える
次ページ
待たせないための工夫 #1
mod_perlのpnotesを使う=> 一回のリクエスト内のキャッシュのヒット率を計算=> 全体の処理時間を計測して記録
次ページ
待たせないための工夫 #2
livedoor Reader Notifier タスクトレイに常駐する更新通知アプリケーション 10分間隔で未読件数を計算
次ページ
バックエンドでキャッシュを作る
新着通知が来る段階でキャッシュが作られている=> 機械的なアクセスでキャッシュを作らせる=> 人間相手の応答は最速のレスポンスを返す
次ページ
UIのための技術
ボットは待たせてもいいけど 人間は待たせちゃダメ
次ページ
フィードリスト読み込み高速化
以上
次ページ
記事の読み込み速度の高速化
SQLの発行回数を減らしたことで全体的な高速化 今度は記事の読み込み速度が気になってきた マウスで操作していると先読みが効かない 突発的にチューニング
次ページ
やったこと
MySQLのパラメータを見直す=> key_bufferを増やす 過去記事の保存件数を減らす=> ほとんど参照されていないので
次ページ
さらに高速化するために
0.1秒と0.5秒の違いはクライアントサイドで吸収できる 遅いクエリはとことん遅い=> データサイズがでかすぎ=> key_bufferやディスクキャッシュに乗らない=> 平均速度よりも安定した速度が必要
次ページ
記事読み込みの高速化
根本的な解決=> 一台あたりのデータサイズ減らす。 とりいそぎ=> 記事もmemcachedでキャッシュするように
次ページ
memcachedの問題
でかいと保存できない(デフォルト1MB) でかいとそんなに速くない(ようだ)
次ページ
キャッシュする記事を選別
ほとんどのアクセスは先頭の記事 読者数がある程度多いフィード 先頭記事10件、購読者数50人以上=> 約37%がキャッシュから読み込まれる。=> ラッシュ時間帯は70%程度がキャッシュにヒット
次ページ
キャッシュの効果
memcachedに当たれば、ほとんど0.1秒以内 レスポンスの安定
次ページ
UIのための技術
Plaggerの巡回でキャッシュが作られて 人間の待ち時間を減らす
次ページ
記事読み込み高速化
以上
次ページ
パフォーマンスチューニングのための
ログ取りの技術
次ページ
アクセス時間の視覚化
フロントでbenchlogを取得=> Apache2の機能=> 転送にかかった時間をマイクロセカンドで記録=> ユーザーの回線速度に影響される
次ページ
バックエンド
Apache1.3なのでbenchlogが使えず=> Sledgeのフックで対応 リクエストにかかった時間をリアルタイムで監視 tail -f でログを垂れ流し
次ページ
ビジュアライズ
0.0531314秒? ログ見ててもわかりづらい Perlでワンライナーフィルタ=> 0.5秒以上かかったら「*」が付くように
次ページ
ピンポイントにログを取る
特定のフェーズにかかった時間 キャッシュのヒット率
次ページ
ログの話
以上
次ページ
さらなる高速化に向けて
記事テーブルが現在12分割 小さめのテーブルをたくさん作った方が良さそう=> 障害範囲を抑えられる=> メンテナンスにかかる時間を減らせる まだmemcachedに空きがあるので、もっと使う
次ページ
まだまだ高速化の余地あり
目標 0.5秒以上かかるレスポンスを無くす
次ページ
サーバーサイドチューニングの話
終わり
次ページ
まとめ
次ページ
サーバーの速さに頼った設計をしない
サーバーの高速化は部分最適化にしかならない ユーザーの回線状況に左右される
次ページ
JavaScriptのチューニングは重要
ちゃんと意識して作れば遅いCPUでもそこそこ動く 速いマシンではより快適に使える ベンチマーク速度よりユーザーの体感速度
次ページ
両面からのアプローチが重要
サーバーサイドで どうにもならないことが クライアントサイドの工夫で あっさり解決することもある。
次ページ
両面からのアプローチが重要
クライアントサイドでごまかすより サーバーサイドをチューニングした方が 楽なこともある
次ページ
UIエンジニアのお仕事
ユーザーを待たせない ためにはなんでもやる
次ページ
今後の方向性とか
次ページ
ノンブロッキング
クライアントサイドは非同期処理をしているが サーバーサイドはまだまだ 一カ所でも遅いとレスポンスに影響する
次ページ
いい加減で速い処理
ロードに時間がかかるぐらいなら 未読件数はいらないんじゃないか?
次ページ
非同期処理のためのアルゴリズムの再設計
UI中心のアプリケーションはレスポンスが命 必要なデータが集まったら即座に返す
次ページ
UIに適したアルゴリズムとは?
全体が高速に完了するよりも=> ゆっくりでもキャンセル可能な方が良い=> 中断、再開が可能なアルゴリズム=> 処理時間の予想が可能なアルゴリズムが必要
次ページ
例えば
10万件のデータをソート?=> ユーザーが実際に必要としているのは先頭の10件=> 最初の10件を最速で返せるアルゴリズム
次ページ
UI中心に考えるとコードの書き方が変わる
従来の速さと これからの速さ
終わり
ご清聴ありがとうございました