ISUCON6 予選で得た知見やノウハウなど(デキル ISUCON スタイル)

ISUCON6 予選に出場したので知見やノウハウなどを軽く纏めます

チームメンバー

この 3 人で出場しました。 SuperMomonga, Aomoriringo, Purintai ということでチーム名は SMAP にしました。サイアク!

チームメンバーの 2 人も ISUCON6 予選振り返り記事を書いた模様です。詳しい施策内容などはそちらを参照してもらう事にして、私は主にツールや環境類について書いておきます。

予選結果

予選突破ボーダーはチーム「反社会性」の得点90,214でした。残念ながら我々は最終スコア89,000点くらいと、1000点弱の僅差での敗退でした。

ベンチマーク関連

終了 30 分前くらいになると徐々にベンチマーク実行依頼のキュー件数が増えていき、ベンチマーカーのパフォーマンスが落ちていっている様に感じました。何も変更を加えていないのに測定するたびに 11 万 -> 9.3 万 -> 8.9 万と下がっていき予選終了。最後の測定やってなかったら勝てたと考えると、若干もやもやする結果に。

再起動試験は終了 30 分前には完了するようにしておいて、最後の30分は プリパラなどを視聴する時間 に充てるのが良いと思う

出題内容について

全体の印象

今回の初期構成は 3 つのマイクロサービスで構成されるウェブアプリケーションでした。そのうち 2 つは例年通り各言語での実装が用意されていましたが、3 つ目は単にコンパイル済バイナリが置いてあるだけという感じでした。そのため、前者 2 つのアプリケーションはコード内容を含め改善しつつ、3 つめのアプリケーションについては本体を改善不可能と諦めそもそもアプリケーションを極力呼ばないようにするのが重要だったかなと思います。

主なボトルネックと改善の戦略

今回の主題となるアプリケーションは isuda と呼ばれるシンプルなオンライン辞書投稿サービスでした。単語の解説記事部分のテキストには他の単語へのキーワードリンクが挿入されているという仕様があったので、新しい単語が投稿されるたびに、その単語を含む既存の単語の解説記事部分を再生成する必要があるというものでした。

我々のチームでは Ruby 実装を改善していきましたが、Ruby では htmlify というメソッドでこの記事HTML生成部分が実装されており、主なボトルネックはここでした。ここまで把握した段階で、我々が手を付けるべき戦略としては大きく 2 つが考えられました。1 つ目は htmlify メソッド自体を高速化し記事 HTML 生成コストを削減するというもので、2 つ目は htmify メソッドの記事 HTML 生成コストについてはある程度諦め、そもそも htmlify メソッドが極力呼ばれないようにするというものです。

結論から言うと我々のチームでは後者の戦略を選びました。大まかに言えば、Sidekiq というジョブキューベースのワーカーを導入しそれに合わせてデータベースのテーブル構造を改良することで、非同期で再生成すべき記事を処理しつつ、生成結果の記事 HTML は Redis にキャッシュしていくという戦略です。また、Etag を活用し適切に 302 Not Modified HTTP ステータスコードを返却するようにすることで、そもそも Redis からのデータ読み込みやサーバー/クライアント間の通信量を削減しました。

この一連の施策だけで大体 6 〜 7 万点ほどの改善が得られました。あとは細かいチューニングやミドルウェアの設定の変更、N+1 問題の改善、データベースへのインデックス張り、TCP 通信でなく Unix Domain Socket 通信を使うなどの施策を行い最大で 11 万点までスコアを伸ばすことができました。

htmlify メソッドの改善にはほぼ手を付けていない状態で11万点のスコアを出すことができたので htmlify メソッドの改善にも手を出せていれば更に数万点は上げることができたのかなと思いますが、時間が足りませんでした。そもそも非効率なコードになっているため素朴に改善していくこともできましたが、競技プログラミング勢が居るとこの辺りの改善はアルゴリズム方面に寄せたアプローチを取れるはずなので強いだろうなという印象でした。

htmlify メソッドの素朴な改善としては、予めコンパイルしておいた正規表現オブジェクトを使いまわすという発想が頭から抜け落ちていた。あとは最長一致単語を優先してキーワードリンク化するという要件のために単語をハッシュ化して置換などの処理を行っている部分があったが、ISUCON においては単語の衝突可能性をそこまで気にする必要がないため、もっと計算コストの低い単なる乱数生成などに置き換えるのが良かったかなという気持ちもある。この 2 つだけでもやっておけばよかった。

使用したツールや環境ついて

tmux

何かサーバー側の状態や画面を共有したい場合には main という名前の tmux セッションで画面共有を行うというルールで運用をしていました。これにより、一々お互いの PC の画面を覗き込むといった必要がなくなりました。口頭で説明するよりも画面を見せてしまったほうが早いものが多く、そういった場合に tmux はかなり有用です。

less コマンド

less コマンドによって vi ライクなキーバインドによりログファイルの閲覧や検索などが行えるので便利です。また、less 起動中に Shift + f を打鍵することで tail -f コマンドの様なモードに入ることができ、これにより tail コマンドが不要になります。基本的には nginx や Unicorn のログファイルをこのモードで流すリアルタイム監視のためのタブを tmux で確保しておき、必要に応じて検索・閲覧・リアルタイム監視というモードをキーバインドで切り替えていました。

nginx のログ解析ツール alp

nginx のログ解析に alp を使用しました。kataribe というツールもありましたが、ドキュメントの豊富さや --aggregates オプションに惹かれ今回は alp を採用しました。HTTP エンドポイント毎のボトルネック洗い出しが一瞬で行えるのでかなり重宝しました。

Ruby アプリケーションの解析ツール rblineprof + rack-lineprof

rblineprof という、Ruby 実行時に Ruby スクリプトにおける行ごとの所要実行時間を計測してくれるツールを利用し、行単位でのボトルネック洗い出しに使用しました。実際には rblineprof を直接使うのではなく、Rack ミドルウェアとしての組み込みを行ってくれる rack-lineprof という gem を通じて利用しました。

ただし、割と SEGV してしまうのであまり活用はできませんでした。もしかしたら prefork 型のウェブサーバである Unicorn との相性が悪いのかとも思いましたが時間がなく詳細は追えていません。行単位でのボトルネック洗い出しが行えるのはかなり強力なので、次回 ISUCON7 までには調査・改善を行っておきたい所です。

自作の便利スクリプト達

ISUCON は時間との勝負なので、自動化できるオペレーションは極力自動化した方が良いです。とはいえ完全に自動化というのは実装・導入コストが大きいので、一連のコマンドやファイル編集を実行してくれるシェルスクリプトを用意しておくというのがかなり有効でした。

具体的には、以下のようなシェルスクリプトを事前に用意しておきました。

  • /etc/systemd/system/hoge.service について sed コマンドにより RACK_ENV の値を development もしくは production に切り替え、その後サービスの再起動を行うシェルスクリプト
  • github から最新のアプリケーションコードを pull し、その後アプリケーションのサービス再起動を行うシェルスクリプト

前者は ~/chenv -e development みたいな感じで叩けば環境が切り替わる感じで非常に便利。また、アプリケーションのコードを GitHub プライベートリポジトリで管理していた関係上、後者はかなり頻繁に使いました。

頻繁に行う上に複数のコマンド実行やファイル編集を行う必要がある操作の効率化をちゃんとやっておく事で、本質的でない作業に取られる時間をかなり節約することができました。こういった事前準備はかなり重要だと思います。

HackMD

HackMD という、markdown で記述するメモをリアルタイムで同時編集することができるサービスです。プライベートなメモを発行できるため、チームメンバー間のリアルタイムのドキュメント記述や情報共有などに使用しました。これを後述の Chromecast でディスプレイに常に写しておくと便利です。

Chromecast + ディスプレイ

大きめのディスプレイに Chromecast を刺しておきます。基本的には前述の HackMD のメモを映し出しておき、必要に応じて Google Chrome の任意のタブを映します。

サーバーコンソールの画面共有は tmux、ブラウザの画面共有は Chromecast という形で使い分けを行っていました。

Photoshop

コラ画像を作成するために使用します

次回に向けて

事前準備としてボトルネック洗い出し関連のツールでいくつか欲しいものが出てきたので、既存 OSS などにはプルリクエストを送り、存在しないものについては自分で作っていくなどをやっていく機運になりました。

また、特定モジュール付き nginx や redis などのミドルウェアや各種解析用ツールを簡単に導入するためにサーバープロビジョニングレシピを書いておく必要を感じました。今回は時間が無く用意できなかったのですが、これがあるとかなりの時間節約になる雰囲気があります。

感想

チームメンバー 2 人は ISUCON 経験者でしたが、私は初出場のためかなり新鮮な気持ちで楽しめることができました。問題作成や運営に於かれましては本当にご苦労様でしたという気持ちです。

本戦に出場できなかったのは残念でしたが、確かな手応えを得ることはできたので、ISUCON7 こそは本戦に出て優勝を狙っていきたいと思います。

あと、ISUCON6 予選の振り返り勉強会など開催したい。

This article was updated on 17年11月26 22:05