ISUCON6に参加してきました

いい感じにスピードアップすることを目的とする大会「ISUCON」の第6回予選に参加しました。
結果としては、予選敗退でした。
初めての参加でしたが、苦しさと楽しさを感じることができて非常に勉強になりました。

やったことまとめ

ISUCON前

チームは同じ職場のtkyshmさんとgurisugiさんと組みました。
チーム名は「RedComp」。【赤】と【粉】です。 私はアプリを担当し、tkyshmさんがインフラ周り、gurisugiさんが司令塔といったような役割でした。

tkyshmさんと私はISUCONへの参加が初めてだったため、gurisugiさんに指導をしてもらいつつ、 pixiv private isuconと去年のISUCON過去問を解きました。

大体は業務でやっていることと変わらず、MySQLでちゃんとインデックスはって、それを使うようにし、 ループクエリにならないようにするなどをしました。
普段の業務はPerlなので、それをGoでさっと書けるようにしました。
また、redisを使った実装なども一通り触り、準備は十分かなという気になっていました。

ISUCON

ISUCON予選本番でやったことは下記のようなことです。

  • azureサーバ構築
  • Goのコード読み
  • MySQL周りの修正
  • isudaとisutarを一つに
  • Perlへの切り替え

動いていたサービス自体の概要としては、あるキーワードについて、その内容が書かれている。
記事の書かれたキーワードが他の記事に含まれているときにはそのキーワードのページへのリンクが作成される。
つまりははてな
各ユーザはスターをつけることができる。

この中で私がやったことをつらつらと書いていきます。

Goコード読み

最初にGoのファイルの有るディレクトリを開いたときに、いくつかgoファイルがあり、 main分割してるのかと思ったのですが、開いてみると、isudaとisutarという2つにmain関数が存在していました。
systemdで確認するとisupamというserviceも存在しており、これはなかなかに面白いのでは、と思っていました。

ざっとコードを読んで、isutarはスター(お気に入り?)のようなものを管理しているだけで、別れている必要がなく、 一つにするのも簡単だなと思いました。
テンプレートの中にループクエリがあるわけでもなく、メインの処理の中にも一つぐらいしか、ループクエリはありませんでした。

この時私は、「インデックスをはって、isudaとisutarを一つにすれば、とりあえず、スコアは跳ね上がるだろうなー」とか思ってました。

goのコードを変更せずにベンチを回すとタイムアウトの減点で0点でした。

isudaとisutarを一つに

isutarは処理はスターの登録とスターの取得でした。

登録の際にはisudaの方に記事があるかを確認してから登録し、取得の際にはisudaからisutarにリクエストが飛ぶなど、 相互依存の関係にありました。

とりあえず、gurisugiさんにisutarのテーブルをisudaのdbと同じところに作成してもらいました。
スターの取得処理は単純にキーワードで引いているだけだったため、isutarからisudaにコピーするだけ。
スター登録処理もisudaからポストしていた値をそのままスターのテーブルにinsertするだけ。
初期化処理もtruncateするだけ。
一瞬で統合は終わりました。

この間にtkyshmさんがキーワードの長さをdbに登録するようにしてくれていたので、それを使って処理をするように書き換えて、 統合自体はこの処理をmergeした後にしようと思っていました。
結果から言えば、統合したコードは最後までmasterにはいることはありませんでした。

キーワードの長さをdbに登録するようにしてもベンチはまだ0点。

redisからkeywordを取ってくる

キーワードをリンクにするのが重い、ということでループクエリになっていたキーワード取得の部分をredisから引くようにしました。

キーワードの長さは既にdbに格納されるようになっていましたので、初期化処理の際にキーワードを値、キーワードの長さをスコアにするようにredisに格納しました。
キーワードのinsertの際はredisにも値が格納されるようにし、ループクエリとなっていた箇所はredisからの所得に置き換わりました。

しかし、これをしても点数は200点程度。初期実装のperlですら1800点ほど出しており、もう焦りが私を満たしていました。

regexpをなくす

これ以上はどうにもということで、pprofを打ち込んだのですが、圧倒的にregexpの置換が支配的でした。
みんなのGo言語にもありますが、Goは簡単な正規表現の範囲では、他の言語の正規表現実装よりも遅いです。

MustCompileしてReplaceしている箇所を消して、strings.Replaceに変えました。
ただ、これではキーワードの分だけ記事内容を全部舐めて置換する操作は消えないので、非常に重い。

予選が終了した後にtwitterでも多く流れていましたが、Replacerで置換するのが良いです。

傍観者と化す

この時点で既に16時半ごろでしたが、gurisugiがperlでぱぱっとキーワードの取得の部分をredisにしてくれました。
この修正でいきなり4万点までいき、isudoとisutarを統合したら7万まで点数は伸びました。(同時になんかちょこちょことした修正はしましたが、おそらく大きくは効いていない)

goでやったことと全く同じことをしただけでこの差でしたので、もう私の心は壊れかけていました。
現場では、「ごおおおおおおおおおおおおおおおおおおおおおおおおおおおおおふぁあああああああああああああああああああ」と叫んでいるだけの存在となり、 ただただ申し訳なかったです。

まとめ

私はあまりにもGoを書きたいという思いが強すぎた。
このような自体をおこさないようにするため、普段の業務からGoを書くようにしたい。(願望)

goを捨てる決断をするときも、私はまだ心の中で「待ってろよ!必ず戻って来てみせる!!」とも思ってました。
非常に悔しかったですね。また開催されるときには挑戦したい。

一緒にチームを組んでくれた二人には非常に感謝しています。
運営の方々には、このような楽しいイベントを開催して頂いて、本当にありがとうございます。