feature image

2024年3月26日 | ブログ記事

Web Speed Hackathon 2024に初出場して優勝しました

この記事はtraP新歓ブログリレー2024、19日目の記事です

はじめに

こんにちは! 23Bの @cp20 です!

2024年の3/23(土)~3/24(日)の2日間にわたって行われたWeb Speed Hackathon 2024に初めて出場して、そしてなぜか優勝してしまいました。新歓ブログリレーっぽいただの体験記を書くつもりが優勝者の参加記を書くことになってしまってが重いんですが、頑張って書こうと思います...!

今年の順位表 (全体はリーダーボードから見れます)

体験記

もともと軽い体験記を書くつもりだったので、とりあえず技術的なところを省いて体験記を綴っていこうと思います。お前の話なんか興味ないから早く技術的な話を聞かせろという人は「改善の流れ」のところまで読み飛ばしてください。

競技前

Web Speed Hackathonに出るのは今年が初めてで、去年以前の過去問を解いたことすらなかったので、とりあえず1週間前の土日で2023年の過去問をある程度解きました。とはいってもなかなかチューニングが難しくて、そこまでスコアは伸びなかったように思います。まぁ本番もそんなにうまくはいかないだろうなと思っていました。

1日目

朝10:00にAbema Towers集合でした。渋谷駅から向かう道中の、ファミマを通過した先のファミマで昼ごはんとおやつを調達した上で会場に向かいました。

無限に撮られているであろう10FのAbemaくん

10Fのそれなりに広い (頑張れば50人ぐらいは入れそうなスペース) セミナールームでオープニングが10:00過ぎから始まりました👏 思ったよりオンサイトの参加者が少なくて悲しいなぁという感じでしたが、それはそれとして挨拶だったり説明だったり会場案内だったりをされました。

ちなみにtraPからの参加者を合わせても知り合いは @Pugma っち1人だけでした。

その後4Fのラウンジ (CA Tech Lounge) のスペースに移動して準備をしていたら、なぜかディスプレイがつかない問題が起きて、5分ぐらい格闘していました。格闘の末にディスプレイを繋げた時にはもう競技開始1分前とかで、慌てて準備をしていました。

メインのデプロイ先は去年は fly.io だったみたいですが、去年の過去問を解くときに fly.io を使おうとしたらFreeプランがFreeプランではないみたいな感じだったので今年は fly.io ではないだろうなと思っていました。そしたら今年は koyeb.com がメインのSaaSとして紹介されてました。

ただ会場参加で同じWi-Fiを共有していたからなのか、koyeb.com に怪しまれていたようで、不正利用を疑われて使えないという事態が発生しました。@Pugma っちもその被害者の一人で、traPの部内PaaSことNeoShowcaseを使ってデプロイすることでgot kotonakiしてました。

競技開始してからは競技をしつつ、デプロイ待ちとかの時間で @Pugma っちとお喋りしたり、飲み物を取りに行ったり (4Fにはフリードリンクのコーナーがあります) 、トイレに行ったり、おやつを食べたり、ご飯を食べたりしてました。

詳しくは「改善の流れ」に書きますが、1日目はそんなにまともな改善を入れられなかったので、割とゆったりとしてました。

19:00にはAbema Towersから追い出され、そのまま帰りました。帰ってご飯食べたら21:00だったのでABC出てペナルティをいっぱい出して萎えてました。あとその後ちょっとお仕事をやって、日が変わるぐらいからまた手を出し始めました。

2時間ぐらい適当に改善を入れたらスコア144.65で15位まで来れました。ちょっと眠かったのと流石に寝ないと明日の体調終わるなと思ったので今日はこれぐらいにして寝ました。

2日目

おはようございます8:30起きです。昨日と同じく朝10:00にAbema Towers集合なのでまぁそんなにゆったりしている余裕もなく、準備してさっさと家を出て会場に向かいました。ちなみにお昼ご飯はファミマとファミマを過ぎた先のローソンで買いました。

聞くところによると @Pugma っちは2時間しか眠らずにずっと改善を入れてたらしいです。というか朝起きたらめっちゃ順位下がってたので人類もっと寝ようよの気持ちになりました。ちなみにその気持ちが出たツイートがこれです。

2日目は特にオープニングとかもないので着いたらそのまま作業開始でした。適当に改善をポコポコ入れてたらスコアが結構伸びて嬉しいなの気持ちに。traP内の順位も順調に上がっていって、最後の計測ではtraP内1位にまで躍り出ることができました。

でも途中でE2Eテスト (アプリケーションの動作が最初と変わってないことを保証するためのテスト) の存在を思い出して回してみると、真っ赤なログが吐かれて、3分の2ぐらいのテストが落ちてるよという警告が吐かれました。流石にこれではfailしてしまうと思って頑張って修正を試みたのですが、結局3分の1ぐらいは通らないままに終わってしまいました。ちなみに競技終了の段階ではスコア542.85で5位でした。

17:30に競技終了した後は18:00からの解説に参加するために10Fのセミナールームに戻りました。昨日よりも人少ないな?という気がしました。

@cut0_ さんによる解説が行われて、その後に順位発表 & 総評が行われる、、、予定だったのですが、予想以上に順位発表が遅れてしまって、結局19:30ぐらいに一旦解散することになってしまいました。手動でのアプリケーションの動作確認が大変なんやろうなぁと。ちなみにその間に現地では懇親会が行われていました。

懇親会ではおにぎりをパクパクと食べながら、オンサイトの参加者や社員さんとお話しすることができました。結構遠方からわざわざ来ている人もいて、ビックリしました。(なのに東京に住んでる皆様は来てくれなくて悲しいよ...)

おにぎり (ツナマヨ味が一番美味かった)

20:30ぐらいになってドキドキの順位発表が始まりました。先に書いたようにテストは3分の1ぐらいが落ちている状況だったので流石にfailするだろうな~と思いながら望んでいました。というかそもそも5位なので上位の人々がfailしてくれないと入賞すらできないので、まぁ流石にないかなと思っていました。

3位の発表で @a01sa01to さん (もともと13位) が表彰されたときは、お?という気持ちになりました。妙に順位発表が遅れてるときにもしかして1位~3位すら確定してないんじゃないか?と思っていたんですが、13位が3位になるということは1位~12位で10人もfailしていることになるので、その予想は合っていたのかもしれないです。

2位は @p1ass さん (もともと11位) で、上位陣がめちゃめちゃfailしているなぁという気持ちに。もしかしてワンチャン自分あるか?と思いつつもテストの件もあるし流石にないかという気持ちでドキドキしていました。

そして1位の発表で自分が呼ばれてすごいビックリしました。嬉しさよりも困惑が先に来たかもしれないです。(振り返ってみると) テンション低めの受賞者コメントを残して、喜びを噛みしめてました。ドキドキが止まらないぜ。

その後は写真撮影だけして解散という形でした。優勝者特権ということで (違うけど) 余っていたコーラを1本手にして帰路につきました。

軽率に優勝のお知らせを各所で流すと色んな人に祝われて楽しいですね。フォロワーもちょっぴり増えました。(そのあとにしょーもなツイートをしてフォロワーが減るまでがワンセット)

改善の流れ

全く技術的なことを書かないのもWeb Speed Hackathonの体験記としてどうなんだという話もあるので、ボクがやったことをつらつらと書いていこうと思います。

ただ正直この分野に関して技術的な深い話は得意ではないです。適当に勘で改善している部分が多々あるので。ボクよりしっかりと改善を入れていた人がボクの上に4人いるので、その人たちのブログとかを読んだ方が参考になるのではないでしょうか。特に途中からずっと1位~3位を競り合っていた Monicaさん (@sor4chi) 、しゅんさん (@shun_shobon)、りんたろーさん (@re_taro_) あたりが素晴らしいブログ記事を出してくれると勝手に信じています。

Web Speed Hackathon 2023 で3位になり損ねた話
これは去年のMonicaさんのブログ

ちなみにボクのスコア推移はこんな感じです。2日目、特に後半でガガガっとスコアを上げました。競馬で言うなら差しぐらいですかね、全然詳しくないけど。

2日間かけてのスコア推移
GitHub - cp-20/web-speed-hackathon-2024
Contribute to cp-20/web-speed-hackathon-2024 development by creating an account on GitHub.
競技中使っていたリポジトリ
[スコア] @cp-20 · Issue #36 · CyberAgentHack/web-speed-hackathon-2024-scoring-tool
注意事項・レギュレーション {{regulation}} 注意事項・レギュレーションを確認して、同意しました 計測対象の URL {{url}} https://webspeedhackathon2024-cp20.koyeb.app/ 登録区分 {{kind}} 学生
スコアの推移 (詳細)

アプリを見てみる

最初にとりあえずパッケージマネージャを確認したらpnpmでした。去年もpnpmだったので、今年はBunかなとか思っていたんですが、そこまで攻めませんでしたね。

ビルドしてみるとまぁ恒例のバカデカバンドルサイズのファイルが生成されて、バカ重サイトが誕生しました。最初フッターだけが読み込まれて、ヒーロー画像が遅れて読み込まれて、さらにそれぞれのセクションが読み込まれて、みたいなCLSありまくり、ロード遅すぎなサイトですね。

デプロイする

まずデプロイしてみたらBuildpackでとりあえずやってみると10分ぐらい経って失敗して、Dockerfileに切り替えてまた15分ぐらい経ってようやくデプロイできました。

何はともあれということで初期スコアを計測してみると、スコア40.00で暫定2位になりました。まぁこの時点での順位なんてただのデプロイ速度対決なので、全く意味はないですが、、、この計測が終わった時点で11:14なので、デプロイまでで約45分かかっていることがわかります。ゆっくりだね。

バンドラーを置き換える

バンドラーはtsupを使っていて、んんん?となりました。なので初手Viteに書き換えようかと思ったんですが、wasm周りで苦しんで結局諦めてしまいました...

wasmはMagikaに依存してるっぽくて、最終的にMagikaは消したのでこの時点で消せば良かったな、、と思っています。

jQueryを削る (削りたかった)

特に最初はLighthouseとかを見るまでもなくヤバいものがあったりするので、とりあえずコードを見て行きました。エントリポイントでなぜかReactをjQueryを使ってマウントしているので削るしかないなと思って削りました。(7200c77)

しかしこれをすると、なぜか画面全体が描画されなくなってしまいました。最初は原因がこのjQueryを消したことであることに気付かずにずっとなんでだろうなぁと思っていて、3時間ぐらい溶かしました。アホすぎる、、、 (修正コミット 08674cd)

ちなみに後からバンドルサイズをガリガリ削るタイミングで気づいたんですが、await registerServiceWorker()している間に$(document).ready(...)が内部でフックしているDOMContentLoadedが発火され終わってしまうので、単純にdocument.addEventListener("DOMContentLoaded", ...)に置換するだけではコールバックが実行されなくなってしまいます。なのでregisterServiceWorker()のPromiseとDOMContentLoadedイベントが発火されるのが両方終わった後に実行するように上手く書き換えなければいけません。ただボクは最終的にServiceWorkerの中身を全て消したので、単純にregisterServiceWorker()awaitしないことで解決しました。

scrollイベントにpassiveをつける

評価項目に「漫画をスクロールして読む」というのがあったので、過去の先輩の記事で見た改善方法を試してみました。 (fa3ffdf)

Web Speed Hackathon 2021 miniでほぼ満点を出しました
あけましておめでとうございます!19の翠(sappi_red)です。いつもはSysAd班 [https://trap.jp/sysad/]で部内サービスの開発・保守をしています。 Web Speed Hackathon 2021 miniに参加した(している)ので、今回はそれでほぼ満点を出した話を書きます。このコンテストはWeb パフォーマンス改善を競うものです。イベントの詳細は開催告知記事 [https://developers.cyberagent.co.jp/blog/archives/32747/]をご覧ください。今回のminiは今年の2月に行われたWeb Speed Hackathon 2021をベースにしているようです。 そちらの方にも参加したのですが、そのときに参加記を書いていなかったので、振り返りつつ記録するのにちょうどいい機会なのといくつかできそうなことがあったので、参加してみることにしました。年末には記事を出そうと思っていたのですが、文量が増えて結局終了1日前になってしまいました…。 前回のリポジトリ [https://github.com/sa

ちなみにこれだけだとほとんど改善しませんでした。

HeroImageの表示を改善する

tsupが吐き出すmeta-file.jsonbundle-buddy.com のESBUILDのところに突っ込むとバンドルを良い感じに分析してくれます。それで見るとimageSrc.tsが20MB近くあって流石にやばいだろうと思ったので改善することにしました。

なぜかbase64形式でエンコードされたバカデカHeroImage (横幅4096px) がthree.jsを使ってシェーダーを用いて読み込まれていて、どう考えてもヤバいだろとなったので、何とかなるだと思ってthree.js依存を消してただ画像を表示するだけにして、base64でエンコードされていたものもちゃんと画像に出力して形式をavifに変えました。 (ffba46b)

ただ後に発覚するんですんですが、ImageMagickを使ったavifへの変換がうまく行ってなかったっぽくて、全然圧縮はされていませんでした。

svgの謎の部分を消す

2023の過去問にもあったんですが、ただのsvgに謎のbase64エンコードされたものが含まれているので、何も考えずに消しました (0b3fb51)

見た感じ何も変わってないのでたぶんセーフです。

ビルドコンフィグをいじる

今更ですがビルドコンフィグをいじりました。minifyとかsplittingとかtreeshakeとかを入れました。(531dcbe)

ただ解説で衝撃の事実を知らされたんですが、tsupはformat'esm'になってないとCode Splittingをしてくれないらしいです。formatはいったん'cjs'にしてバグらせたので'iife'のまま結局最後まで進みました。

正直慣れてないのでホントにViteに移行したかったです。

この時点で計測してスコア53.75で暫定46位 (14:37) になりました。

フッターモーダルのテキストをlazy loadする

これもbundle analyzer見ててヤバそうだな~と思ったのでバンドルから消しました (a556492)

アイコン系をtree-shaking

アイコンを

import * as Icons from '@mui/icons-material';

のように読み込んでいたので、必要な分だけを


import { ArrowBack, ... } from '@mui/icons-material';
const Icons = { ArrowBack, ... };

のように読み込むように変更しました。(3d9c08c)

momentを消す

バンドルサイズがデカいと話題のmomentを削りました。そこまで依存が大きくなかったので自前実装で置き換えました。(0cc6458)

ガッツリ依存してるときはdayjsとかへの置き換えを検討するとよいと思います。

HeroImageの高さを指定する

画像の高さが指定されないとロードされる前と後でCLSが起きてしまうので高さを指定しました。(645dae5)

ただこれでモバイルでの表示をぶっ壊しました。(この時点では気づいてない)

AppとAdminを分割

メインのアプリの方 (Adminではない方) の表示速度が重要になってくるので、Adminとバンドル自体を分けることでバンドルサイズ削減を図りました。(e8de96c)

もう少しうまいやり方があったのかもしれませんが、とりあえずindex.htmlをコピーしてadmin.htmlを作るというやり方で対処しました。サーバーで配信するところもそれに合わせて切り替えるのですが、かなりやりやすかったので作問者もこれを意図していたのかなという気がしました。

この時点で計測してスコア62.20で暫定20位 (17:14) でした。

画像の読み込みをlazyに

多くの箇所で使われている<Image>コンポーネントのloadingプロパティがデフォルトでloading='eager'になっていたので、デフォルトではloading='lazy'にするようにしました。(56b8a9d)

Separatorをただのdivに

謎にcanvasに描画するという形式で作られていた<Separator>コンポーネントをただの<div>に書き換えました。(179c83f)

作品詳細ページの無駄なリクエストを削る

useEpisodeList()のリクエストで既にエピソードの名前やらの必要な情報は全て手に入れているので、個別にuseEpisode()のリクエストを飛ばしていた部分を削りました。(b6da86c)

lodashを消す

バンドルサイズが無駄にデカいと話題のlodashを自前実装で置き換えました。(d0037aa) 今回使われていたのは_.clamp()_.map()ぐらいだったので楽でした。

この時点で計測してスコア88.75で暫定10位 (15:52) でした。

トップページの無駄なリクエストを削る

作品詳細ページと同じようなことがトップページで沢山起きてたのでなくしました。(c0342ae)

この時点で計測してスコア93.75で暫定11位 (17:01) でした。

フォントのsubset化

これも過去の先輩の記事で見たことがあったのでやりました。(42cfe0b)

な~~~~~~んもわからん!!!!!!!【Web Speed Hackathon 2023 参加記】
こんにちは、@d_etteiu8383 [https://trap.jp/author/d_etteiu8383/]です。今更ぼっち・ざ・ろっく!にハマっています。3/5-3/6の二日間にかけてWeb Speed Hackathon 2023[https://cyberagent.connpass.com/event/270424/]に参加しました。本記事はその参加記です。 ...高スコアが出せたわけでもなく、詳しい解説ができるわけでもないので、本記事は参加記という体の反省会です。 Web Speed Hackathon 2023 とは> Web Speed Hackathonとは、予め準備してあるWebアプリケーションのパフォーマンスを改善することで競い合うハッカソンです。主にWeb技術(フロントエンドおよびNode.js)に関するチューニングを出題いたします。 表示に非常に時間がかかるサービスをどこまで高速化できるかを競います。題材は「架空のショッピングサイト」を予定しています。https://cyberagent.

実際にやったことは google-webfonts-helper から Noto Sans JP のjapaneseとlatinのみのsubsetをnormal(400)とbold(700)だけ作成して、読み込ませるようにしました。

ただこれは競技終了後に気付いたんですが、今回はNoto Sans JPがどこでも使われてないので単純に消すだけでよかったみたいです、、、

画像周りを改善

画像をuseImage()関数を使って/imagesにアクセスすると変換処理が走ったりして嫌になるので/raw-imagesという直接imagesフォルダから画像を返すエンドポイントを生やして色々いい感じにしました。(0cf8ce7)

画像を事前にリサイズした上でavifに変換するなどして高速化を図りました。ただこれもavifに見せかけた何かなのであんまり改善されていないです。

エピソード詳細ページの無駄なリクエストを削る

useBook()useEpisode()が両方呼ばれていたんですが、useEpisode()だけで十分そうだったのでuseBook()は削りました。(eb12be4)

漫画ビューアーの改善

さっきの変更で壊れた部分を修正したり、無駄なリレンダリングを削ったりしました。(2178989)

実はこれはコンテナクエリを使えばCSSだけでかけるらしいんですが、ボクのCSS力ではサッと書くことはできませんでした。厳しい。

この時点で計測してスコア49.00で暫定55位 (18:34) でした。なんでスコアが下がったのかよくわかってません。(ブレてるだけかも)

画像周りを改善再び

/raw-imagesへの置き換えを別の箇所でもやっただけです。(de83bcd)

SSRされたデータを使うように

実はサーバー側でSSRをしていて、事前にリクエストを飛ばして返却するHTMLに入れてくれています。ただそれを使わずに再度リクエストを飛ばしていて無駄だな~と思ったのでfallbackに指定するようにしました。(eb8a842)

ただ実際のところ上手く行っているのかは分かりません。調べてもあんまり出てこなかったので詳しい人教えてください。

この時点で計測してスコア54.25で暫定46位 (19:12) でした。ただこの後に特に意味もなく2回再計測したらそれぞれスコア81.50スコア55.25が出たので25点ぐらいは余裕でブレるんだな~という気付きを得ました。ちなみに懇親会で聞いた話では100点ブレたという話もありました。ブレすぎだろ。

JXLの変換先をBMPからJPEGに

Service WorkerでJXLを受け取った時にBMPに変換していたんですが、BMPは圧縮されない良くない形式なのでJPEGにしました。(f49058f)

Service Workerの並列リクエスト制限をやめる

並列リクエスト数が5に制限されていたのでそれをなくしました。(76e1774)

JXLをやめる

そもそもChromeがJXLに対応していないのに無理に使う必要もないので事前にJXLからavifに変換して配信するようにしました。(dfc1690)

この時点で計測してスコア144.65で暫定15位 (25:31) でした。たぶんJXLやめたのが結構効きました。

検索アルゴリズムの改善

検索のときに値をマッチさせるのにunicode-collation-algorithm2というライブラリを使っていたのですが、バンドルサイズが1MBぐらいあるやばライブラリだったので雑に自前実装して消しました。(ff28808)

HeroImageのスタイルを修正

モバイルでぶっ壊れていることに気付いたので直しました。(ec1d951)

画像を圧縮する

ImageMagickによるavifへの変換がうまく行っていないことに気付いたので、sharpというnpmパッケージを使って画像の圧縮を行いました。(99f8b2a)

どうでもいいですが、こういうちょっとしたプログラムを書くのはBunが優秀です。最近はBun Shellも出てきてshell scriptとTypeScriptのいいとこどりな書き方ができます。お試しあれ。

とかサークル内で宣伝したらdaxとかzxとかを出されました。Denoが好きな人とかNode.jsが好きな人はそういうのを使うといい感じに書けるらしいです。

この時点で計測してスコア132.70で暫定33位 (10:56) でした。思ったよりスコアが上がってないですね。

HeroImageの最適化

結局ヒーロー画像はavifもどきにしただけで改善していなかったのでsharpで圧縮をかけるのと同時にデバイス (ディスプレイ幅) ごとに最適な画像を出し分けるようにしました。(15f9c4b)

srcSetプロパティ を使うことで割と簡単に実現できます。

画像最適化

これも/imagesのやつを事前リサイズして/raw-imagesにしただけです。(f567594)

検索アルゴリズムの修正

unicode-collation-algorithm2を吹き飛ばした後の自前実装がミスっていたのを直しました。(735ea7b)

今回はfailしている人がたくさんいたみたいなので、こういうミスには気を付けた良いことがわかりますね。

検索を高速化

検索してから (useBook()で) 本の情報を取得するのではなく、事前に (useBookList()で) 本の情報の一覧を取得しておいて、検索結果を表示する際にはそれを使うように変更しました。(962d281) (上手く動いてなかったので修正したコミットa3aaf61)

この時点で計測してスコア215.15で暫定19位 (11:43) でした。ちょっとずつ改善されてきました。何が効いてるのかよくわかってないですが。

SSRする

全てのページで必要な情報を全て事前に取得しておくことで上手くSSRを走らせることに成功しました。(5493221)

もともとSSRは走っていたんですが、必要な情報がなくてエラーが含まれたHTMLが返却される状態ででした。それをちゃんと動くように直しました。

これによってCLSがほぼほぼなくなりました。

この時点で計測してスコア248.45で暫定14位 (12:11) でした。

検索アルゴリズムの修正

さっきの計測で検索が「計測できません」になっていたのでまだバグってるのかと思って修正しました。(859bac6)

めちゃめちゃ初歩的なミスでした...(しっかり動作確認をしよう!)

検索結果表示を遅らせるように

検索欄に入力があったタイミングで毎回検索して結果表示をしていると、かなり重くて入力しているときの体験も悪いので、500ms入力がなかったタイミングで初めて検索をかけるように改善しました。(eaf4061)

管理画面のログインフォームを高速化

全く手を付けていなかった管理画面を見たらログインフォームが信じられないぐらい重かったのでコードを見ていたら怪しい正規表現がありました。ReDoSと呼ばれるバカ重正規表現っぽかったので、コメントを頼りに直して、動作確認までちゃんとしました。(a92ef03)

ここで上位3人がfailしていたので、しっかりと動作確認までするのは大事ですねと言う気持ちに。

ボクはちょっと前にReDoSの存在を学んだので、タイムリーだなぁという気持ちになりました。学んだことが生かせるのは気持ちいいですね。

この時点で計測してスコア338.00で暫定12位 (13:12) でした。検索とログインのスコアが爆上がりして、特にログインは満点近く取れたのでホクホクです。

E2Eテストをパスするように修正

なぜかE2Eテストで検査するURLがシードデータに存在しないURLだったので、それでもエラーが起きないように変更しました。(84879ed & fdac9bf)

さらにフッターのテストでモーダルを遅延読み込みしていることでfailしているのがあったので、それも修正しました (dc36763)

さらにE2Eテストを走らせてVRTの画像も変更しました。(6213cb8)

この時点で計測してスコア376.30で暫定11位 (15:07) でした。

バンドルサイズを削る

いらないライブラリを削ってバンドルサイズを減らしました。(aae7be6)

謎に機械学習を使う激重ライブラリMagika、lodashみたいなやつのunderscore、アイコンのためだけに入っている@mui系を消しました。

この時点で計測してスコア380.55で暫定13位 (16:16) でした。

ビルドをproductionに

そのままです。ビルド時のmodeproductionを指定するようにしました。(cb209ef)

逆に今までなんでdevelopmentだったのか意味不明ですね。

SpacerのCLSをなくす

<Spacer>コンポーネントが謎に読み込まれてから幅を確保するというCLSを生み出すためだけに存在しているような設計をしていたので、CSSで幅を確保するように変更しました。(d2f07e0)

jQueryを消す

最初に消すのに失敗したjQueryをやっと消すことに成功しました。(594573d & 4ed2435)

ただ別に30KBぐらいバンドルサイズが減るぐらいでそこまでパフォーマンスに影響はもたらさないんですけどね。(普段なら30KBは相当デカいが、最後まで削っても300KBぐらいあったので...)

この時点で計測してスコア449.20で暫定9位 (16:35) でした。CLSを削ったのが大きかったのかも。

漫画ビューアーのCLSを減らす

漫画ビューアーが動的に幅が確保されていてCLSが大きかったので、なるべく減らすようにしました。(e2c8e8d)

ただCSSを使えば0にできたので、CSS力が足りないなと思う次第。

漫画のスクロールを改善

漫画のスクロールがやっぱり重いので原因を調べて改善しました。(d9e04b2)

ただこれが最適解というわけではなく、CSSで全てを置き換えられるのでやはりCSS力が、、、でもこれでも漫画のスクロールは満点を出せているので十分なのかもしれません。

ちなみにここで「世界は我々の想像する以上に変化するため、2 ** 12 回繰り返し観測する」という怪しい文言が出てきて思わず笑ってしまいました。もちろん消しましたが。

利用規約などのlazy loadを最適化

かなりeagerに利用規約とかのテキストファイルを読み込んでいたんですが、実際にモーダルが開かれるまでは読み込みを遅延するようにしました。(e7321e7)

この時点で計測してスコア542.85で暫定5位 (17:23) でした。これが最終計測です。

感想

スコアについて

最終的な計測は下のような感じになりました。ボトルネックになっている順に、作品の情報を編集 & エピソードの追加といった管理画面周り、利用規約の表示、エピソード詳細ページ、作品の検索あたりがありますね。

テスト項目 スコア
[App] ホームを開く 89.70 / 100.00
[App] 作者詳細を開く 95.80 / 100.00
[App] 作品詳細を開く 98.30 / 100.00
[App] エピソード詳細を開く 67.30 / 100.00
[App] 作品を検索する 42.75 / 50.00
[App] 漫画をスクロールして読む 50.00 / 50.00
[App] 利用規約を開く 25.00 / 50.00
[Admin] ログインする 49.50 / 50.00
[Admin] 作品の情報を編集する 0.50 / 50.00
[Admin] 作品に新しいエピソードを追加する 24.00 / 50.00

正直手が回らなかったというのが大きいので、もっと手を早く動かせるようになりたいですね。変なところで時間を溶かさないのも大事かも。

技術的な話

個人的な惜しかった / もっと良くできた / 来年は頑張りたいポイントを挙げていきます。

Adminページ周り

途中までずっと放置してた上に、最終的に改善したのはログインのReDoSぐらいだったので、もう少し頑張れたんじゃないのかなぁという気がしています。

結局ほとんどコードを見てすらいないので実は今も何を改善すれば良かったのかすらわかっていないです。

初手のVite移行

これに成功してればもっと楽だったかもしれません。wasmは出てきたらだいたい削られる運命にあるので、頑張って無視して移行を成功させたかったですね。

バンドラーは色々変えてきてるので (解説で言ってたところによると今年は最初Rspackを使う予定だったらしい) 何が来ても初手Viteに置き換える覚悟と実力を持っておきたいですね。

正直普段はNext.jsを使っていてViteのビルド設定もそんなに知らないのでしっかり勉強しておきます...

画像をもっとうまく最適化する

最適化用にスクリプト書いてわざわざエンドポイントを増やして配信するとかいう変なことをしてたんですが、/imagesエンドポイントを良い感じに変えて、初期に1回だけ変換を噛ませて2回目以降はキャッシュするようにしたらスマートに全部最適化できたのかなと思うなどしました。

もっと攻めた話ですが、サーバーの前にプロキシを挟んでそこで返すという案もあるなと思いました。Cache-Controlヘッダとかでいい感じにやれると面白そう。

CSS力を高める

昨今のCSSの進化は目覚ましいもので、JSでしか書けないように思われるものも意外にCSSだけで書けたりしてしまうんですね。Web Speed HackathonではChrome最新版での動作だけしか求められていないので最新の機能を (Canary実装とかは流石に無理だけど) 使えます。特に最近普及してきたコンテナクエリとかはかなり表現力が高いみたいで、使いこなせるようになりたいものです。

CSSだけで表現することで、JSを使うよりパフォーマンスが高くなるだけでなく、SSR時のCLSを防ぐことができるようになります。

画像の難読化を書き換える

漫画の画像のencrypt / decryptの処理、難しそうだったので触れませんでした。触れなくてもそれなりにスコアは出たので良かったんですが、もう少し軽くできないものかとは思いました。

バンドルサイズもっと削る

まだ削り切れてないライブラリ群がたくさんあって、もっと削れたなと言う思いがあります。ただあんま使われていないライブラリは割と削って、残ってるのはかなり大きいライブラリ群だったと思うので、難しいよなぁとは思いました。

代表的なものとしてFormikとかがあるんですが、どうするのが良いんでしょうかね、、、

さいごに

初出場としてはかなり上出来なのではないでしょうか。かなり良いところまで改善できたと思いますし、上位陣がたまたまfailしたので優勝してしまいました。

逆にまだまだだなと感じさせられることも多かったです。他の人の参加記的なブログもちらほらと出てきていますが、ボクが思いつかなかったような改善を入れている人がいて流石だなぁという気持ちです。また解き直してみたいなぁという気持ちがあります。(でも実際にやるのは1年後の直前とかだったりしそう)

あと今回traPでみんなに出よう!!!!!!!という圧をかけたら興味を持ってくれる人が何人か出てくれたは良いんですが、みんなReactの前に撃沈していった感じがあるので、部内Web Speed Hackathon & 講習会を開催したいなぁの気持ちになりました。ボクのやる気があれば夏か秋か冬ぐらいに開催されます、たぶん。(そのためにCyberAgentさんはscoring-toolsを公開して頂けると...)

今年はおこぼれの優勝みたいな感じなので、来年こそは堂々と1位を取れるようになりたいものですね。


明日は @s9 さんの記事です。たのしみ~

それと6日後にまた記事を書く予定なのでそれも楽しみにしててくださいね!

cp20 icon
この記事を書いた人
cp20

23B ただのぷろぐらまーです icon: https://twitter.com/sora_douhu

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2023年12月11日
DIGI-CON HACKATHON 2023『Mikage』
toshi00 icon toshi00
2021年8月12日
CPCTFを支えたWebshell
mazrean icon mazrean
2021年5月19日
CPCTF2021を実現させたスコアサーバー
xxpoxx icon xxpoxx
2024年3月22日
traPグラフィック班の活動紹介2024
haru10 icon haru10
2022年9月26日
競プロしかシラン人間が web アプリ QK Judge を作った話
tqk icon tqk
2024年3月17日
⑨でもわかる8bitアレンジ講習会
vPhos icon vPhos
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記