ISUCON10 予選敗退した

ISUCON10。 今年はあっという間に募集が終わって不参加かな、とも思ったけど 声かけていただき id:Soudai さんと id:kamipo さんと、昨年と同じチームで出場した。

とはいえ今年は僕は子も産まれ 京都に移住したのもあって、昨年のように東京で3人集まっての参加は難しく、一人だけ京都の自宅からリモートで繋いで参加、という形になった。

素振り

一応去年組んだメンバーであるとはいえ 敗退しているわけだし、今回は前述のように一人はリモートで作業することになるので練習はしておくべきだろう、ということで 2週間ほど前に1日つかって素振り(練習)をした。 昨年の予選問題はすっかり忘れているのでそれを使って、一日中zoomで繋いで会話しつつ、必要に応じてエディタの画面を共有して一緒に見ながら作業する練習だったり。 あと今年は New Relic が特別ライセンスで使用できるってことでせっかくだからそれを導入してみてどうなるか、の練習も。

当日

朝5時台に子が目覚めるので一緒に起きて抱っこで30〜40分ほど散歩し、帰宅してから朝食たべて 子の朝寝と一緒に再度寝て、9時頃目覚めてさぁ準備、と思ったら2時間の延期…。 18時にはお風呂に入れて20時までには寝かしつけ、というサイクルだったのでどうしよう…という困惑はありつつも、どうしようもないので今日は妻に頑張っていただく、ということで ともかく開始まで待機。

序盤

まずは環境作り。サーバにログインしてアプリの動作を確認、git repositoryに最低限のコードをpushして ローカル環境で動作できるかどうか試す。 web app がAPIのみで static files が分離されていたのでちょっと苦労したけど、それらもローカルに持ってきて手元でnginxも立てたりして とりあえず動かせるようにはした。少し手間はかかったけど これによってコード変更時の動作確認とデバッグがやりやすくなっていたのでやっておいて正解だったと思う。

Rubyでやっていく方針だったので初期実装のGoからRubyに切り替え。NewRelicを仕込んでベンチを回してボトルネックを見てみる。とにかく検索が重い。

インフラ周りやDBのチューニングはSoudaiさんに任せてアプリやクエリの改善をしていこう、と。 まずはじゃあ明らかにヤバそうな features の LIKEクエリを別tableに正規化して軽くしよう、と動き始め。

中盤

estate_features, chair_features のtableを作って 初期データから生成されるデータを作って insert時にfeaturesもinsertしていく処理を書いて 検索時にそれらを使ったクエリに書き換えて…

OR検索じゃなくてAND検索だったから GROUP BY にしなきゃ、id IN (?) だと対象idsがemptyになった場合はクエリ壊れてしまう、とかでちょいちょいハマったり。 結局これがちゃんと動いたのは16:30過ぎ。時間かかりすぎたな…。しかも実際には features を使ったクエリは少なかったようであまりスコアには影響せず (ここは反省点)。

じゃあまだ遅いのは何だろうね、ということで 幾つかindexを適当に貼ったりしながら次の改善点を探す。 widthheight などのrangeで絞っているのを 選択肢idのequalで絞れないか、と提案。やってみることに。 kamipoさんにschema変更してもらって そこの初期値を設定するコードを書いてもらい、それを使ってクエリを単純化するコードを書いて、と分担し。

しかしこのあたりで POST /api/estatePOST /api/chairタイムアウトしてしまうなどでベンチがまったく通らず。 諸々改善してようやく通るようになった頃にはもう20時を過ぎていた。これでスコアはようやく 1,500 程度。。

GET /api/recommended_estate/:id はSoudaiさんが UNION クエリに書き換えたが 何故かベンチがこけるのでrevert。 やっていることをよく見て考えたら 「椅子の3辺のうち2つが ドアの2辺におさまるかどうか」だけを調べられれば良いのではないか、と気付いたので少しだけ簡略化することは出来た。このへんは普段leetcodeとかで問題読んで目的のコードに落とし込む訓練をしていた成果はあったかな、と思う。

終盤

やり残したことはたくさんあって nazotte とかはほぼ手を付けられず (kamipoさんが最低限の無駄なクエリを省くようにはしてくれていたが)、あとはもう時間も無いので logを切ったり NewRelicを切ったり 再起動テストをしたり で終了。最終スコアは 1,656 と 予選通過ボーダーにも全然届かない結果となった。

反省点

自分なりにはベストを尽くして最大限のパフォーマンスは出せたと思っているので悔いは無いけど、それでこの結果なので単純に力不足が露呈することになっていてつらい気持ち。

チームの反省として、kamipoさんの力を引き出せなかった、というのがある。予選翌日に感想戦で改めて見直したところ MySQLのindexまわりで改善できるところがたくさんあった。

このあたりに自分ももう少し気付ければ良かったのは勿論だけど、チームとして最高のパフォーマンスを出すためには もっとkamipoさんに集中してここらへんを見てもらえるように作業分担を調整する必要があったのだろうな…。

features の正規化 (時間かかったわりにはそこまで大きなスコア改善に寄与しなかったわけだが)や rangeの選択肢のequal化などは優先度を調整しつつ僕が一人で全部やって その間にkamipoさんがindexを見れるように、といった分担が出来ていなければいけなかったんだろうな、と思う。

昨年もそうだったけど全体的に手を動かすスピードが遅かったりハマったときに無駄にハマり続けて時間を浪費してしまっていた、というのはある。 ここは日々の鍛錬でどうにかしていくしかない。

感想

一人自宅からリモート参加というのは初挑戦だったけど、そこはそれほど苦にはならない程度に出来たかな、と思うので それは良かったと思う。 とはいえ理想としてはチーム全員集まってワイワイやれた方が楽しいとは思うけども。 来年はどうなるかな〜。

運営の皆様は今回も準備や当日の進行が大変そうではありましたが 取り組み甲斐のある面白い問題を出していただき感謝です。 おつかれさまでした。本選も素晴らしい競技になることを期待しております。

Repository