本の虫

著者:江添亮
ブログ: http://cpplover.blogspot.jp/
メール: boostcpp@gmail.com
Twitter: https://twitter.com/EzoeRyou
GitHub: https://github.com/EzoeRyou

アマゾンの江添のほしい物リストを著者に送るとブログ記事のネタになる

筆者にブログのネタになる品物を直接送りたい場合の宛先:
郵便番号:165-0027
住所:東京都中野区野方5-30-13 ヴィラアテネ401
宛名:江添亮

超会議3の超チューニング祭の感想

さて、超チューニング祭が終わったので、感想を書こうと思う。すでに、参加者の中で、感想を書いている人もいる。

レポート - 超チューニング祭で努力賞(最速賞)をとるためにやったこと - Qiita

ニコ動 超チューニング祭で最優秀賞もらいました

超チューニング祭に参加した - masarakki's blog

JavaScript - 超チューニング祭に参加&表彰した - Qiita

kmizu/slide_cho_tuning

また、いつの間に行ったのか、優勝者に取材したところもあるらしい。

『ニコ超3』の超チューニング祭で、“創世神”戀塚昭彦氏を上回ったカップルが見せたバランス感覚 - エンジニアtype

さて、筆者の視点からみた超チューニング祭はどうだったか。

そもそも、私がスタッフとして配置されるブースは、超時空ニコニコ研究所であるはずだった。しかし、超会議にさかのぼること三週間前、スタッフとして参加するドワンゴ社員向けの説明会の会場で、急遽、ユーザー企画のハッカソンに割り当てられたと告げられた。

なんでも、この企画はユーザーが提案したものにドワンゴが予算を出して行うまるなげ企画の一部らしい。詳細はよくわからないが、フロントエンドの最適化らしい。HTML, CSS, JavaScriptあたりだろうか。

ところで、ドワンゴから選ばれた他称精鋭達というのは、恋塚、まさらっき、水島、江添と、いずれもフロントエンドエンジニアではない。

そして、発表から超会議まで、時間が二週間ほどしかない。大丈夫なのだろうか。

かくして、その詳細が一切わからないまま、超会議3の当日となった。

当日、超チューニング祭の場所は、まるなげ広場の端に設置されていた。超歌ってみたと、何やら歌やダンスなどのパフォーマンスを行うらしき場所、さらには自作装甲車ブースに設置されたスピーカーもあって、これ以上ないくらいにやかましい場所であった。こんなところでコードが書けるのだろうか。

さて、当日、参加者に伝えられたレギュレーションは、以下のようなものであった。

サーバー側の変更は不可、HTML, CSS, JavaScriptの変更。

かつ、UI要件として、以下の条件を満たさなければならない。

  1. ログインボタン、プレミアム登録のためのボタン(もしくはリンク)
  2. 横長の広告が2つ
  3. 「利用規約」「特定商取引法」「ヘルプ」「PC表示に切り替え」「新規会員登録」へのリンク
  4. 2クリック以内で以下のコンテンツのリンク踏めるようにしてください。

また、パブリックなCDNなら使ってもいいという条件も追加された。

ファイルはsftpサーバーで取得、提出することになっていた。開始直後、なぜか誰もsftpサーバーに繋げられないという問題が発生した。用意したsftpサーバーが使用するポート番号を配布した資料に載せていなかったのが原因であった。

さて、実際にファイル群をみてみたが、現行のスマートフォン向けのニコニコ動画と同じ見た目になっている。

今回の競技では、評価点が二つある。パフォーマンスと、UIのデザインである。パフォーマンスの測定は、謎のテスト環境で測定されることになっている。UIデザインはユーザー投票で決定されるそうだ。実際の受賞には、測定と投票を元に審査で決定される。

しかし、UIデザインは、これ以上なにか改善場所があるようには思われない。このデザインが最高ではないにせよ、これは小さな画面のスマートフォン向けのデザインだということを考えると、そうそう複雑なデザインも使えない。

とすると、パフォーマンスを改善するのが、まず第一ということになる。

使われているCSSやJavaScriptも、一見すると、そんなに量が多いわけではない。しかも、大部分をGoogle AnalysticやjQueryなどの、有名なJavaScriptコードが占めていて、それほど改良点があるようには思われない。CSSやJavaScriptを一つのファイルに統合して、機械的な変換で最適化、最小化をほどこしたところで、どれだけ改善されるものか。

実際のパフォーマンス計測環境の詳細は明らかになっていないが、LTE回線とスマートフォンを使って行われるそうだ。そのテスト用のURLも提供されているが、キャッシュを破棄してリロードすると、明らかに遅い。

実は、この超チューニング祭のために用意された環境には、すこし意図的な罠が仕掛けられている。まず遅い理由というのは、jQueryをホストしているサーバーが、意図的にとてつもなく遅くされているのだ。そもそも、そのURIからして、あきらかに、「超遅いサーバー」と読める。

<script src="http://cyouosoiserver.kunikiya.jp/js/jquery-1.9.1.min.js" type="text/javascript" charset="utf-8"></script>

CSSにも、よくわからないものがある。

/* 猫の日対応@2/22が終わったら消す */
.siteHeaderMenu .siteHeaderLogoNeconeco {
	width:152px;
	height:24px;
	background:url(/img/etc/shot/neconeco.png);
	background-size:100%;
	float:left;
	margin:15px 0 0 15px;
}
/* 猫の日対応@2/22が終わったら消す */

そして、CSSは明らかに同じ内容のものが重複していて非効率的にみえる。無駄にコメントアウトされたコード片もある。

このへんの整理をするのが、まずド素人ふるい落としということになるだろう。

あとは、CSSやJavaScriptのファイルをまとめたり、コードの最適化をしたり、不要な部分をそぎ落としたりするべきだろうか。少なくとも、機械的な最小化ぐらいは書けて損はないだろう。複数のファイルに分けられているとパフォーマンス上悪影響なので、CSSとJavaScriptは全部、HTMLに埋め込むべきだろうか。

細かい画像が多数ある。これはひとつの画像にまとめて、CSSで座標を指定して使う、いわゆるCSS Spriteにすべきだろうか。あるいは、HTML内にData URIとして埋め込むというのはどうか。

さて、どうするか。筆者は、HTMLやCSSやJavaScriptには詳しくないし、CSS Spriteのための作業などしたくない。それに、その他の最適化というのも、やはり泥臭い作業になる。仮にやったところで、筆者以上に優秀な人間がいるこの状況では、勝てるはずがない。

初日は、とりあえず非標準のCSSプロパティとベンダープレフィクスの削除をした。このようなCSSプロパティは使われるべきでない。特にベンダープレフィクスは邪悪である。これは、提供者が対応すべき問題ではないのだ。利用者側が、まともな規格準拠のブラウザー実装の最新の安定版を使うべきなのだ。もし、利用者が、何らかの理由で、自分の所有するコンピューターのソフトウェアを変更することができないとするならば、それは利用者側の問題である。提供者の問題ではない。特に、スマートフォンは不自由なコンピューターが蔓延しており、利用者にはroot権限すらないと聞く。なまじ、提供者が泥臭いハックで対応してしまうがために、利用者と、利用者を食い物にしている不自由コンピューターの売人がつけあがるのだ。つけあがらせてはならぬ。断じて妥協の姿勢を見せてはならない。

そして、CSSでは不自由なフォントファミリーが指定されていたので、それも削除した。

15時になった。初日は、15時にいったん、途中経過のパフォーマンスを計測するということであった。計測方法の詳細は公開されていないが、LTE回線を使ったスマートフォン実機上で計測するそうだ。

初日の途中計測では、意図的に遅いjQueryのCDNである、"cyouosoiserver.kunikiya.jp"に気がついたものが、別のCDNに切り替えるか埋め込むなどの回避方法を使って、ダウンロード時間を1秒台に削減していた。ちなみに、CSSプロパティをわずかに変更しただけの、ほぼデフォルト状態の筆者のダウンロード時間は、19秒であった。超遅すぎる。

それぞれパフォーマンスはまちまちであったが、計測結果の一位のスコアが圧倒的に素晴らしかった。なんと、ダウンロード時間は0.3秒で、処理時間が0.01秒ではないか。いったいどうやっているのだ。

sftpサーバーで該当のユーザー、user003のindex.htmlを見てみると、以下のようなコードであった。

<html>niconico(C++)</html>

なるほど、速いに決まっている。ちなみに、さんざん疑われたが、user003は筆者ではない。濡れ衣である。

もちろん、テスト結果では、user003はUI要件を満たしていないとされていた。

ただし、二位は0.6秒で、デフォルトとほぼ変わらぬ見た目と機能を維持していた。

しかし、この結果は興味深い。index.htmlが最小であれば、ダウンロード時間も処理時間も短縮される。当然である。JavaScriptとかCSSとか画像とかのリソースを別ファイルにしなければ、別々にダウンロードする必要もないので、パフォーマンス上有利である。そもそも使わなければ、なおいい。

では、もし、軽量なHTMLで、レギュレーションが規定するUI要件を満たしてやれば、パフォーマンス最速は狙えるのではないか。無論、そのようなページはすこぶる利便性を欠くため、ユーザー投票で負けることは確かだが、少なくとも、パフォーマンスで一位にはなれるわけだ。真面目な方法では、知識不足で勝つことは望めないとすれば、悪い戦略ではない。どんなに汚い方法であれ、レギュレーションさえ満たして、パフォーマンス計測で最速になればいいのだ。

さて、もう一度、レギュレーションの規定するUI要件を確認してみよう。

  1. ログインボタン、プレミアム登録のためのボタン(もしくはリンク)
  2. 横長の広告が2つ
  3. 「利用規約」「特定商取引法」「ヘルプ」「PC表示に切り替え」「新規会員登録」へのリンク
  4. 2クリック以内で以下のコンテンツのリンク踏めるようにしてください。

1. 2. 3.は問題がない。単にa要素やimg要素でリンクしておけばいいだけだ。問題は4.である。「以下のコンテンツ」というのは、結構たくさんあるし、ランキング一覧などもあって、単なるリンクひとつふたつだけで済ますことは出来ない。これをいったいどうしたものか。

ここで考える。この文面の条件さえ満たせばいいのだ。マキャベリになれ。方法は問わぬ。2クリックだ。2クリックでリンクが踏めればいいのだ。

さて、最初から叩き台として用意されているindex.htmlは、現行のスマートフォン向けページと、同等機能を有している。したがって、レギュレーションの規定するUI要件も満たしている。軽量index.htmlから、元のindex.htmlに飛ばしてやればいいのだ。

window.location.href="original_index.html" ;

しかし、デフォルトのindex.htmlで、もし「以下のコンテンツ」へのリンクを踏むために2クリック要するものがあれば、問題である。軽量index.htmlからオリジナルindex.htmlに飛ぶ際に、1クリックを要してしまうと、合計3クリック必要となり、UI要件を満たせない。すべて1クリックで行けるかどうか検証するのは面倒だ。

考えろ。こんなとき、マキャベリなら、何をするか。

結局、クリックが問題なのだ。クリックさせなければいいのだ。軽量index.htmlから、0クリックでオリジナルindex.htmlにとべればいい。そう、例えば、特定の要素の上にマウスが乗ったら、別ページに飛ぶような仕組みがあればいい。

elem.addEventListener("mouseover",
    function( evt )
    { // クリックしてないよ
        window.location.href = "original_index.html" ;
    }
) ;

これだ。0クリックでオリジナルのindex.htmlに飛べれば、UI要件は満たせる。UI要件さえ満たせれば、パフォーマンスは最速だ。もはやHTMLの機械的な最小化も必要ない。他社に比べて圧倒的余裕があるだろうから、リンクをCSSでボタン風のUIにしても、楽勝で一位になれるだろう。HTMLの機械的な最小化変換も必要あるまい。むはははははは。さらばだ、マキャベリになれぬ超真面目な諸君。レギュレーションさえ満たしていればいいという柔軟な発想ができれば、パフォーマンスで一位になれるのだよ。見よ、ダウンロード時間0.5秒、レンダリング時間0.01秒の、この圧倒的パフォーマンス。しかもUI要件は満たしている。これで勝てないはずがない。

結果は・・・甘かった。

結局、レギュレーションを満たすための大量のリンクのせいで、index.htmlのサイズは膨らむ。サイズが膨らむとダウンロードに時間がかかる。他の真面目なガチ勢よりはダウンロードが速いとはいえ、やはり時間はかかる。

そもそも、軽量なindex.htmlで、レギュレーションの規定するUI要件を満たすためのリンクをはる必要はないのだ。軽量index.htmlには、オリジナルのindex.htmlに自動的に飛ぶためだけのコードを入れればいいのだ。そうすれば、index.htmlのサイズは最小となる。ただし、計測を通すために、色々と飛ばす工夫が必要になり、レンダリング時間が伸びるが、そこさえ回避してしまえば、極めてサイズの小さいindex.htmlが作成できる。すなわち、ダウンロード時間の削減につながる。

したがって、筆者と同じ戦略を取ったもう一人の人間の発想のほうが、より柔軟であったために、敗北を喫してしまった。

パフォーマンス測定では、筆者は三位となった。そう、こんなに不真面目な戦略をとっても、まだ上がいるのだ。

さて、姑息なマキャベリ達はこのへんにして、超真面目勢はどうか。

まず、運営の仕掛けた初歩的な罠を、一瞬で把握したことはもちろんである。ドワンゴの他称スターエンジニア達は、しきりと、CDNが遅い、jQueryでタイムアウトまで待っている処理がある、この挙動は変えてもいいものだろうかと迷っていたが、彼らは冷静に、レギュレーションを満たしているかどうかだけを判断し、さっくりと挙動を変えた。Google Analyticsも使われていたが、そもそもレギュレーションにGoogle Analyticsを使えとは書かれていないため、あっさり削除された。

その他、CSSやJavaScriptの最適化、機械的な変換のようなことは、普通に行われていたようだ。

超真面目勢は、短時間でCSS Spriteに書き換えていた。あのように画像をひとまとめにして座標で指定するのは、完全に自動化できぬだろうから面倒で時間のかかる作業だと思うのだが、あっさりと書き換わっていた。特に再優勝賞を取ったチームは、画像の特性をみて、見た目をほとんど劣化させず圧縮しやすい加工までしていた。この手の画像処理は、デザイナーの得意分野なのだろう。デザイナーの価値を認識した。

そして印象的だったのが、画像のキャッシュだ。古くはcookie、Flash内のcookie、最近ではlocal storageのような、ユーザー側に保存されるストレージ領域に、画像を入れてしまうのだ。これにより、次回以降はJavaScriptでキャッシュの有無を調べて、ダウンロードを回避できる。

どうやら、テスト環境は、ばらつきを押さえるために、複数回のテストをして、その平均をとっていたようだ。問題は、テスト後にcookieやlocal storageのようなストレージ領域の破棄をしていないために、このようなキャッシュはとても効率的だったようだ。もっとも、多くの環境では、local storageが使えるだろうから、テスト環境がそのようになっていたとしても、悪いわけではない。

それにしても、HTMLを最小化していないし、コメントもあれば、CSSで無駄に装飾を入れて遊んでいたとはいえ、筆者の超不真面目な戦略が、デフォルトとほぼ変わらぬ見た目と機能を維持したガチ勢に負けてしまったことは、ただただ呆然とするしかない。

思うに、敗因は、HTML最小化やCSSの有無ではなく、同じURIの広告画像を二箇所に普通にimgで表示していたので負けたのだろう。筆者はData URIとか、local storageによる画像のキャッシュのような技法を使っていなかったのだ。複数のファイルに細かく分けるということは、極めてパフォーマンス上よろしくないことを認識した。

そういえば、誰だったかは忘れたが、今回の競技では、数十個のドメインを使って、ものすごく泥臭い画像の並列ダウンロードを実現していた人もいた。これは、多くのブラウザー実装で、ひとつのドメインから同時にダウンロードするファイルの数は、紳士協定により低く抑えられているため、ドメイン名を分けることで、大規模な並列ダウンロードをさせるというものだ。もちろん、local storageへの画像キャッシュに敗北していたが。

こんな突発的なチューニング競技に、いったいどれだけ真面目な人間が集まるのか。そして、この短時間で、一体どれだけ作業ができるのかと、筆者は疑問であったが、やはりプロは格が違った。それにしても、様々な技法があるものだ。

ドワンゴ広告

この記事はドワンゴ勤務外に書かれた。

4月26日、27日は、ドワンゴ社員のほぼ全員が超会議にスタッフとして参加するため、28日、30日は振替休日となっている。また、5月1日、2日は有給休暇の取得推奨日となっているので、まだ有給休暇を使い切っていないほとんどのドワンゴ社員は有給休暇を取得している。そのため、今年のゴールデンウィークは、9連休だ。

筆者はまだドワンゴに入社して日が浅いので、本来有給休暇はないのだが、5月1日、2日は、入社して日が浅い社員でも、特別に有給休暇の先取りを認めているので、休みとなっている。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0