ISUCON参戦記

うまです。

ISUCON参戦したので、参戦記を書きます。

チーム名はkstmで、学生枠での参加です。インフラ周りのふぉのと、司令塔のあーちゃん、お荷物枠の僕の三人で参加しました。全員ものづくりサークルkstmのメンバーです。チームの団結力は高いほうかと思いますが、共通言語がないのが問題点です。

 

まずは予選から。

githubに一応ログを残しています。

github.com

様々な事情から、予選の練習を開始したのが前日の夕方頃からで、練習不足感甚だしいしかも寝不足の状態での予選でした。少し触ってみて決めたことは、ISUCONで使える言語の内、ふぉのはPerlRubyがある程度読めますが、インフラ周りを見ることに徹するのでその2つはボツ、あーちゃんはJavascript大好きなのでNode.JSが使え、PHPもかけます。僕はPHPGolangがスコシヨメルですが、たいして書けません。しかしPHPはバリバリにフレームワークが使ってあり、その知識もないと見るのは厳しそうだと判断し、優先度を下げました。よって担当割当はnginxなどをふぉの、サーバープログラムをNode.JSであーちゃん、SQLを僕が担当することを事前に決めました。

少し仮眠を取り、サークル部屋に集合。本番開始直前に開始の一時間の遅れとNode.JSの実装がないこと、さらにPHPがエラーでうまくいかない可能性があること、付け加えてGolangも点数が出ない事が判明、大慌てで言語の変更を迫られました。とりあえず過去問を見たところ、GolangならSQLクエリも読みやすく使えそうだとの判断で、僕はSQLを読むのをGolang実装で読むことにし、サーバープログラムはとりあえずGolangでやってみて無理そうならその時考える方針で開始しました。

とりあえずインスタンス起動して初enqueue、たいして性能でずGolangに変更してenqueue、0点を記録してからの滑り出しでした。僕とあーちゃんはちょっと長めのコードだったのでひたすらコメントを付けて回りました。その間にふぉのさんが改善してくれて600点ちょいを出せるようになりました。このあとはひたすらこの時に書いたコメントに従ってコードの改善をしていくことになります。なにしてるかよくわからなければコメント書いとけばなんとかなる・・・こともある・・・。僕は頭があまり回っておらず、構造把握は殆どできてない状態でしたが、あーちゃんに言われるがままやっていました。まともに改善したのはN+1問題のクエリ一つだけと、SQL担当とはとても思えない結果でした(お荷物枠の力発動!)。それからGolangを一年ほど前に少し書いただけというあーちゃんにGolangの仕様を聞かれたら答え、わからなければggって答えと、そんなことをずっとしていました。apt-get upgradeの罠をたまたま回避できたり(誰かしてるだろと思って僕はしなかった)、謎のタマキさんには出会わなかったり、最後どうしても時間内に改善できなかったLIMIT 1000を100に変えたりキャッシュつけたりいろいろした(主に僕以外のメンバーの頑張りの)結果5000点以上上がったり、再起動後のジャッジの点数ではなく最終ジャッジの点数が取られたことなど(再起動試験は行っていたが、不安要素満載だった。failしてても文句言えない・・・)と、大変運にも恵まれていました。最終スコアは7898と学生枠3位での通過となりました。この本戦通過の通知は、サークルバーベキュー大会が終わって直後のサプライズになったことを記しときます。

 

そして、昨日の本戦。もちろん練習時間は験を担いで(?)ほぼ一日、これまた練習不足での本戦でした(反省しろよ!!)。朝の長野駅集合で新幹線で東京は渋谷ヒカリエまで。北陸新幹線かがやきはとても速くて快適ですね!堪能して山手線で渋谷まで。開場40分前に集合場所についてリラックスしていました。開場し、かっこいい参加者を示すカードが貰えてLINEオフィスまで。カードにはtwitterアイコンをそのまま提出したのですが、もっとまともなアイコンを使えばよかったと少し反省。会場に到着して、最初は景色のいい窓際のソファ席をチョイスしたのですが、机が低く狭く、あまりやりやすい環境ではなさそうだったので机席に移動。長机も1つ借りてとても良い環境でコードを読むことができました。周りに強い人がたくさんいる中での緊張感を味わえてよかったです。

さて、本戦ですが、結果から言うとfailの0点でした。いくら時間がなくとも慌てていようとも天地がひっくり返ろうとも、再起動試験だけは絶対にするべきことだということをまず書いておきます。ただ、半数以上のチームがfailで落ちていました。もう一度書きます。再起動試験は絶対にやるべきです。

内容はAPIをいかに効率的に叩くか(まとめすぎ?詳細はggってください)というやつでした。予選と同じ体制で、Golangを選びました。とりあえずubuntu14.04の3コアCPU4GBメモリ*3という構成だったので、ubuntu14.04のapt-getで標準で入るGolang1.3(←今は違うかも?)でapp.goがコンパイルされている可能性があったので、Golang1.5に上げてくださいと提案。インフラ周りの改善とGolang1.5によるコンパイルで10000点を少し超える点数を出し上位陣の仲間入り。司令塔のあーちゃんから「dataが明らかに遅いです」ともらったので、そのあたりを読みました。何が遅いかすぐには気づけなかったのですが、よくよく読んでみると何回もAPIを叩いていることが判明。ここでの改善案として、1,Golangお得意の並列化をする、2,redisにクエリ結果をキャッシュするの二点を考えました。並列化を実装してみたのですが、なぜかエラーで落ちる。クエリ結果をキャッシュする方も、キャッシュしていいやつとしてはダメな奴があり、途中で気付いて分けていたりしたのですがなぜかfail連発でダメ。ブラウザから見ると正常に動いているように見えるのですけどね。そこそこのコードを書いたり消したりしましたが、結局コードとしては余り貢献できませんでした。並列化もしていいやつとダメな奴、クエリ結果のキャッシュもやっていいやつとダメな奴があったっぽいです。もっとちゃんとAPIの仕様を調べておけば回避できたかもしれませんが、考察できたかどうかは未知数です。並列化してはいけない奴、キャッシュしてはいけない奴に気づけなかったので、どこかコードが間違っているんだろうといろいろ書きなおしたりしてたのですが、点数はfailになるか下がるかのどちらかで、大した改善はできませんでした。もっと自分の書いたコードに自信を持つことはもちろんのこと、APIは並列で一斉に叩けばよいわけではないことを学びました。

本戦終わっての結果発表、今年は学生賞は該当なしでした。学生枠全チーム再起動試験で落ちたっぽいです。過去の自分に伝えることができるなら、「本戦始まったら一切PCに触れるな。そしたら学生賞は取れるぞ」と教えてあげたい・・・。一位のfuziwara組には脱帽です。どうやったらあんなにすごい改善が実装できるのか。すごすぎです。

 

ISUCON参戦記は以上です。お荷物枠になってしまいましたが、来年はちゃんと活躍して本戦に行けるように、一年間精進します。

@Goryudyuma