kaa_a_zu’s blog

月ごとのレポートや技術的な内容を発信していきます

Recruit主催のスピードハッカソンに参加してきた

本日、3/7 RECRUIT主催の スピードハッカソン に参加しました。色々な理由があって、絶対に優勝する心積もりで参加をしました。結果、優勝できなかったです。凄く悔しい思いをしています。

本日やったことと今の想いは文字に起こさないとダメな気がしたので拙い文章ではありますが、書きます。 尚、簡潔に書くので詳細を知りたい部分があったらカーーズへのリプやコメントをください。

スピードハッカソンとは

Recruit社の実サービスであるホットペッパービューティのパフォーマンス改善を行うハッカソンで、順位決定のスコアの測定にはLightHouseを使用します。 

ハッカソンのルール

①1つの詳細ページのみのパフォーマンスを上げる

Chrome Canaryでの動作確認を行う

③見た目を変更することはNG

④挙動を変更することはNG 

行ったパフォーマンスチューニング

ボトルネックの探し方

サイトの解析ツールとして、LightHouseよりもわかりやすく、詳細に修正した方が良い箇所を指摘してくれる、yellowlabを利用した。 

改善前のサイトの状態

  • HTTP1.1 での通信
  • webサーバーはNginx
  • ビルドツール, タスクランナー等は使われていない
  • ノーマルなCSSを使っている
  • jQueryが動いている

f:id:kaa_a_zu:20200307231625p:plain

チューニングした項目

チームの皆で大きく分けて10個の改善をしました。

尚、1ページのみのチューニングで時間も限られていたためビルドツール,タスクランナーを使うことは本質的ではないと思い使わない事を選択しました。

1. Scriptのdefer/async読み込み 

Scriptは パーサー ブロックをしてしまうため、それを阻止するため非同期で読み込み

(jQueryなど他から依存されているスクリプトの読み込みは同期的に読み込まないといけないので対象外)

2. 各サブリソースの読み込み順序の変更

CSSはできるだけ最初に読み込んだ方が良いため<Head>の序盤に配置、ScriptについてもDOM操作がないものは序盤に配置

3. 画像のwebp化, 他拡張子の圧縮

今回はchromeが対象のブラウザだったため、<Picture>タグでは囲まずにwebpのみを採用。webpの画像は用意されていなかったので、squooshを使ってwebpに変換。また、一部の画像はwebpよりもpngを使った方がサイズが小さくなったので、それらには ImageAlpha での減色 と ImageOptim での圧縮を実行(盲目的にwebp最強だと思ってたので意外でした。てかwebpの実体をあまり理解せずに使っているの良くないな....

4. 画像の遅延読み込み

最初はchrome のネイティブlazy-load を利用をしていた

<img src="hoge.png" loading="lazy"

時間があったので、表示領域だけを無駄なく遅延読み込みしてくれる lazysizes を使ったところ、こちらの方が良いスコアが出ました。

5. <img>にwidth, heightを設定

webpなどベースライン形式の画像は縦幅横幅を指定しないで表示してしまうと、ロードが完了するまで縦幅横幅がわからないため、ロードに応じて<img>要素のサイズ更新が行われてしまうと聞いたことがあったので追記した

6. Scriptのminify化

今回は、UglifyJS を使った。

webpack v4の圧縮機構でデフォルト採用されている Terser との比較をしてみたかったです。

7. 使われていないcssの削除

今回は、 PurgeCSS を使った。

リクルートの方が作った[css-optimization](https://github.com/toshi1127/css-optimization)も試してみたかったです。

8. Cssのminify化

今回は、clean-css を使った 。

 

〜ここまででLightHouseでのスコアは65くらいでした〜

9. HTTP/2に変更

リクエスト数の上限の壁を超えました。(笑)

10. NginxでのgZip

配信の時に小さい方が良いよね。

 

これら10個のことをやった結果、

パフォーマンス96

改善後のスコア

5時間で 11 から97!!!!!!!!!!!!!!!!!!!! 個人的にはすごいと思っちゃいました(笑)

 

.....ここまでやって負けました!!!!!!!!

ほぼ、同じような内容をやっていた1位との差はホスティングの部分に注目したか』でした。

僕たちのチームは終了30分前くらいまではある程度の差をつけて1位でいました。正直、慢心をしてしていました。そして、その間に2チームに抜かれました。嘘だろって思いました。

愚直なパフォーマンス改善を続けた @しゃがみーさんたち、そして express のホスティング(静的ファイルの提供)の遅さに気がつき、大差を付けて優勝した @どらくん, @たじまちゃん,@ましくん, @もっちくん たちは凄かったです。

 

コンテストが終わって更にすればよかったなーと思う点がいくつかあるので書きます。

  • バンドルしてラウンドトリップを1回にしたらどうなったのかな。これは今回の場合http2との無制限並列リクエストの比較になるのかな?
  • gZipじゃなくてSSL化してBrotliで圧縮したらどうなったのかな?
  • JSのデッドコードを削除 minify化した後に出来るのかな?
  • CSSセレクタをもっと簡単に参照できるようなチューニングしたらどうなるのかな?
  • HTTP3って適用できたのかな?
  • Performanceパネルをもっとしっかりと見えば良かった
  • 表示サイズに合わせた画像の最適化は、今回においてあんまり効果なさそうだったけど、推測するな計測しろらしいので時間が余ったらやった方が良かった?

感想

パフォーマンスについては、興味があっても、なかなか触れる機会がなく1人だと詰まってしまうことが特に多い部分だと思います。それを既存の大きなサービスを使い、複数人で行えたことは、本当に良かったです。何より楽しかったです(笑) 主催をしてくださったRecruit社の皆さん、ありがとうございました。

結果に対してですが、随所でも書いた通りとても悔しいです。(どのくらいか悔しかったかというと、懇親会で出た寿司を1つも食えないくらい) ですが、過去の自分と比べると成長を感じれました。また、自分が苦手な部分(今回で言うとPerformanceパネルを見ることを毛嫌いしてるのが良くないw)についても、改善をすることの重要性を知れたので良かったです。

最後に、これだけは言わせて欲しいです!!!! 優勝はできなかったけど、僕たちのチームの団結力は1位だった気がしています。初めて会う人たちで、それぞれの分野も知識量も違いどうなるか最初は不安でした。その中でそれぞれの得意分野を活かして進めることができたのは、このチームの皆だったからな気がしています。最終的にそれぞれが出来ることをやり切れたことは良かったです。@さぃとくん @大石くん @やだくん ありがとうございました。

ただ、良い思い出で終わらせるわけにはいきません。
すげえ悔しいから(笑)
絶対に来年は優勝してみせます。1位になったチームの皆さん、来年参加する皆さん、よろしくお願いします。

改めて、最高のイベントでした!

 

p.s. リクルート社員の皆さん。パーカーを貰い忘れました。