ISUCON13、久々の現地参加してきました(百万円ドリブン:11位)
今年もISUCONがやってきた!
こんいすー。チーム「百万円ドリブン」のnakarioです。今年もISUCONの季節になり、我々にとっては7回目の参戦を果たしてきました。ほぼISUCONについてしか書いていない本ブログの読者はISUCON関係者しかいないと思うので、ISUCONとはなんぞやみたいなのは飛ばしてさっそく感想から書いていきたいと思います。
久々の現地参加
コロナ禍の影響で長らくオンラインのみの開催となっていたISUCONですが、今年はLINEヤフーのオフィス現地参加抽選枠があり、ありがたいことに我々はその枠で現地参加してきました。オンライン開催のときにはチームメンバーで自宅に集まって参加していましたが、やはり他のチームと同じ場所で参加すると緊張感や高揚感、イベントに参加しているという実感が段違いですね!
最後の現地参加はミライナタワーのLINEオフィスでしたが、それからオフィスが移転したり、ヤフーと統合したりと前回とはガラッと変わった環境ですが、競技者視点ではモニターの貸与があったのでこちらの方が良かったですね。新宿駅ダンジョンよりも分かりやすい場所にありますし(チームメンバーの@Muratamは迷って3つぐらい別のビルに入ったりしてたみたいですが……)。
競技終了後にお酒と料理を楽しみながら感想を言い合ったり、941さんの配信を生視聴したり、二次会で他チームと合流してISUCONトークで盛り上がったりも含めて最高の一日でした!!
成績と反省点、問題に対する感想
今回は11位という成績で終えることができ、チームとしては予選敗退だった前回はもちろん、他の決勝進出回と比べてもなかなか良い成績を出せました。スポンサー企業様より「いい生活賞」も頂けたのでいい事づくしです。ですがチーム名にもある通り狙うは1位100万円なので、まだまだ修行が足りていないですね。
ISUCON本戦では各々の開発力はもちろんですが、本番ゆえの緊張からくる判断ミスや過集中の影響が無視できないというのが僕のこれまでの参戦歴からの学びでした。その点、今回は初動から優勝チームであるNaruseJunがぶっ飛ばしていたのもあり、自分でできることを着実にやろうと冷静になれていたのではないかと思います。例えば、後述しますが序盤のボトルネックの改善でしょうもない見逃しをしていたせいで大きくタイムロスしたのですが、その際も別の方法で解決することができないか考え直したり、メンバーに協力を要請したりなど、目の前のタスクにとらわれずにやるべきことをやる、ができていたと思います。後半も、実装は間に合いませんでしたが取るべき方針は間違ってなかったと考えています。ということで今回は、純粋に技術力・実装力で負けたと、認め難いですがそういうことになるという認識です。悔しい!我々のチームは毎年本番前に丸4日ほど使って過去回をおさらいしているので、定番パターンのN+1問題やDBのインデックスなどを解決する方法は理解しています。しかし、定番ゆえに手癖で解いてしまうことがあり、複数の解決方法を検討した上で最も効率の良い方法を選択する、ということを事前練習の段階で行っていませんでした。ゆえに本番でも愚直に手を動かす以外の手札がなく、最適な選択を行おうという意識があるにも関わらず実質的に取れる選択肢が一つだけ、という状況だったのかなと振り返って思います。今回は事前練習ではツールの整備などに時間を多めに割いていました。それは間違いではありませんでしたが、それだけでは足りないということが改めて思い知らされたといったところです。次回はN+1を解くにしても10個くらい手札を用意して参戦したいですね。
ISUCON13の問題の感想ですが、題材としてはどこかで見たようなライブ配信アプリだったので取っ掛かりやすさは大きかったです。独自のゲームなどが題材だとそのゲームを理解するところから始めないといけませんが、今回はすんなりアプリの概要を理解して競技に集中することができました。ベンチマーカーの待ち時間が大きかったところは気になりましたが、スコアは大きなブレもなく、改善を入れれば素直に点が上がっていったので気持ちよく取り組むことができました。こういったところを作っていくのも面白そうなので、早く我々も優勝して出題側にまわりたいです。
やったこと
- 10:30 3268点: 問題の理解や環境構築
- 11:00 各種ログやpprofの設定
- POST /initializeを叩くと同時にpprofを計測する仕組みを我々も用意していますが、ログの集計時にMySQLのrestartでアプリも再起動することに気付かず、自動集計の仕組みがうまく動かなかったので少し手間取りました
- 11:20 3,614点: DBの2台目への移動
- 11:40 9,257点: インデックスの作成、InterpolateParamsの有効化、DBコネクション数の調整
- 12:00 11,908点: 更にインデックスの作成、PDNSのTTL=120
- 12:40 20,100点: N+1の解消、DNS用のインデックスの作成
- 13:30 26,892点: icon_hashのキャッシュと304
- 前述の序盤のミスです。11時半ば頃から取り組み始めてここまでかかってしまいました。
- icon_hashのキャッシュ自体は割とすぐに実装できたのですが、ETagをレスポンスに含めているにも関わらずベンチマーカーがIf-None-Matchヘッダーを付けてリクエストを送信してくれず、ブラウザでの検証でも同様で、どこが間違っているのかわからない状態でした。
- 原因はETagの値をダブルクオートで囲まずにヘッダーに入れていたことでした。とほほ……。
- 13:45 27,168点: ライブコメントの走査対象を限定
- 15:00 34,025点: N+1、インデックス
- 15:30 43,924点: N+1
- 15:40 57,294点: slotの範囲検索をid検索に変換
- 15:50 69,737点: N+1
- 16:00 80,976点: livestream_tagsのキャッシュ
- 16:30 84,773点: tagsのキャッシュ、インデックス
- 16:40 104,645点: インデックス
- 17:30 111,401点: JSONのインデントをなくす
- 17:50 127,149点: 各種ログのオフ
16時くらいからDBの水平分割をメンバーに取り組んでもらっていましたが、あと一歩届かず3台目のサーバを丸々未使用のまま終了しました。DNSのDBを分けるなどすればもしかしたら5位くらいにはなっていたかもしれませんが、狙うは1位なので無駄なことをしないという選択をした結果です。僕がETagで手間取らなければ+1時間くらいあったはずなので、そこが一番の悔いですね……。手書きせずにライブラリを使ってもうまく行かなかったのですが、これはブラウザで検証した際の方法が良くなかったのかも。ChatGPTに質問しても見当違いの返事しかしてくれなかったので、やはり普段からサーバを書いて経験を積んでおくことが肝なのかなと思いました。あるいはCopilotに書かせるのも良いかもしれません。
最後に
今回のISUCONも大変楽しかったです。主催のLINEヤフー株式会社様、出題のさくらインターネット株式会社様、株式会社いい生活様はじめスポンサー企業の皆様、ISUCONの運営やコミュニティを支えてくださっている皆様、どうもありがとうございました!(あれば)次回もぜひ参加したいです!
ISUCON12予選突破できず無念
予選突破出来なかったけどせっかくなのでやったことを書きます。
僕はチーム「百万円ドリブン」では主にアプリケーションの修正をメインに担当していて、メンバーのaokabiとmurataはそれぞれnginxやmysqlなどのミドルウェア、なんか色々をメインに担当してもらってます。
スコアから書くと14205点でスコア再現性テストで失敗して失格でした。失格については再起動テスト含めて再現性の検証をやっている余裕がなかったので仕方ないとは思ってますが何がだめだったのかは謎のままです。
当日やったこととしては、いつもどおり一通りの計測手法を仕込んだあと、mysqlへの負荷が低すぎるということでsqliteの存在に気づいて、主にaokabiとmurataにsqliteのまま分散 or mysqlへの移行で水平スケーラビリティを確保してもらう方向で動いてもらいつつ、アプリケーションを改善していく方向でやっていました。
ただ、sqliteが通信のオーバヘッドがない分N+1が負荷になりにくいこととかが頭からすっぽ抜けていて、N+1を直したのにスコア変わらないなぁといった感じに空回りしていたなと後になって思います。また、sqlite3で動いていたwindow関数を使ったクエリをmysqlで動かしてみると「ONLY_FULL_GROUP_BYモードが有効だと非集計カラムを参照できない」といったエラーで動かないことが判明して、非効率的なサブクエリとのJOINでなんとか動かすみたいなことをしていたので、こういった情報をちゃんと予習できてなかったなと反省したり(むしろ何で今までのISUCONでこのエラーに遭遇しなかったのかが不思議)。
15:30ごろにaokabiとmurataがmysqlへの移行(/var/lib/mysqlの全置換によるinitialize時間の短縮)を完了してくれて、そこからは各々いつも以上のパフォーマンスを出せたのではないかと思いますが、さすがに時間が足りずスコアが伸び切りませんでした。mysqlサーバを3台用意しておいてinitializeのたびに向き先を変えて、今まで使っていたサーバの初期化を走らせておくっていう副案も抱えていたのでそれをやっても良かったかもですね。競技中はもっとまともな方法で解決できるはずと思っていましたが、たとえ異常な作戦でも有効ならばチャレンジしていく勇気がなければ百万円には届かないのかもしれません。
ISUCON11-priorやってみた
予選が近いので練習戦として isucon11-prior をチームでやってみました。解説記事は出ていないようだったので、答え合わせはできてませんが8割くらいは対処できたのではないでしょうか。
構成は以下のようにして、最終的に30,000点を超えるくらいのスコアで終了しました。
やったこと
- 計測環境構築
- デプロイスクリプトの用意
- N+1の解消
- MySQLのCPU負荷がネックなので、スロークエリログをもとにgetReservations、schedulesHandlerに仕込まれているN+1を解消します
- この時点でbenchサーバの移行が完了した上で4,000かな?
- nginxでstatic fileの配信
- aokabiがやってくれました
- スコアは大きくは変化しなかった(はず)
- indexの追加
- タイミングが分からないけど多分このあたり?
- overbookingの解消
- schedulesHandlerの挙動をドキュメント通りに変更
- ここらで、原因は最後までわからなかったのですがスコアが4000点付近に張り付いて動かなくなる現象が発生
- GETリクエストばかりでPOSTが来なくなっており、得点計算かユーザの挙動に原因があると考えてドキュメントを読み漁る
- 実装とドキュメントの齟齬を発見し、一般ユーザには空きのあるスケジュールのみを表示するように変更
- 実装したものの点数に変化がなかったが、いつの間にか8,000点くらいになっていたので実は効果があったのかも
- DBのmax connectionを設定
- 点数が伸びないので場当たり的にやった
- reservedカウントの管理オンメモリに移行
- indexの追加とかもあったかも
- 12,000点くらい
- interpolateParams=true
- 14,000点くらい
- 2台構成に移行
- アプリとDBで分割
- parallel=20に変更したのもここ付近?
- 24,000点くらい?
- reservedのオンメモリ化をすすめる
- 30,000点ちょい
- (失敗)reservationのbulk insert
- 一定間隔おきにためておいたreservationを一括でinsertする実装を追加しましたが、ベンチとの相性が悪かったのか点数は変わりありませんでした
- (失敗?)json Encoderの置き換え
- 公式のencoderとほぼ変わらなかった
感想
練習用に作られたものとして基本を抑えつつ、ボリュームもしっかりあってとても楽しめました!UIやパスワードまわりは大胆に手を抜きつつ、ISUCON要素をぎゅぎゅっと濃縮したような感じでした。
あたかも私がボトルネックなので変更してください!と言っているかのようにドキュメントに書いてあるID生成の部分は触ってないですし、ベンチサーバー側のメモリが溢れそうで負荷が増やせない感じだったのでまだまだ上は目指せそうです。こんなにやりがいのある問題を提供していただいて、本当に作問者の方々には感謝してもしたりないくらいです。
さて、まずは予選を突破できるよう、早寝早起きを頑張ろうと思います!
学生枠、今までありがとう #ISUCON9
ISUCON9予選突破できたのでブログ書いてます
3年連続ISUCON本戦出場!!! https://t.co/2wzmSTVMiS
— nakario (@nakario_jp) 2019年9月8日
今年から学生枠が無くなって、去年の予選の順位的に本戦出場はぶっちゃけ無理だと思ってました
盛大に砕け散って学生枠の再開を声高に主張するつもりだったのに……
本題ですが、僕はチーム(百万円ドリブン)の中では一番Goに慣れているということもあってアプリのコードの変更をメインで担当しています
やったことを時系列順で簡単に書くと
- pprofを仕込む(10:20)
- getCategoryByID のDBアクセスをなくす(12:40)
- getNewItems, getNewCategoryItems のN+1を解消する(14:30)
- なんか不要そうな FOR UPDATE を postBuy から消す(17:20)
- 上手くいくか怪しかったけど getItem からDBアクセスをなくす(17:40)
- もっと FOR UPDATE を大胆に消してみる→エラーが出たので大急ぎで戻す(18:05)
という感じです
最後ので戻せなかったら予選敗退してたのでマジ危なかった
つらつらと書きましたが、今回の最大の勝因はbcryptの計算専用に2台のサーバを使うという大胆な戦略を提案・実装してくれたチームメンバーのaokabi & murataにあると思ってます。感謝。
その他良かった点
- キャンペーン還元率に早い段階で気付けた(ISUCON8本戦でSNSシェア率による負荷コントロールの仕組みに気付けなかったので気をつけてた)
- pprof、pt-query-digest、netdata、alpなど複数のソースからボトルネックを判断した(過去問練習で見えてるボトルネックと原因が別の場所でハマった)
- 定期的に声掛けして方針を立てた(ISUCON7本戦で何時間も一人で悩み続けた反省)
- 大きいホワイトボードを用意した
- カレーを食べた
こうして見ると、学生枠で本戦に出場させてもらえた経験がとても活きているので、これからISUCONを始めようという人たちのためにも学生枠が廃止されたのは残念です
学生枠、今までありがとう
KMCとボードゲーム「超人ロック」 —— KMC Advent Calendar 2018
この記事はKMC Advent Calendar 2018 - Adventarの10日目の記事です。昨日はtetsutalow先生の担当でした。
KMCとボードゲーム
こんにちはこんにちは。KMC5回生のnakario(kmc-id: gnu)です。KMCでは昔はお絵かきなどをやっていましたが、最近はISUCONなど不定期イベントを除けばSlackとボードゲームしかしていないです。はて、コンピュータサークルである京大マイコンクラブでボードゲーム……?
実は、KMCは京大のサークルの中でもRPG研やシミュ研に次いで(?)ボードゲーム活動が盛んです。新歓コンパ、NF、春合宿などの大きなイベントで人が集まる時は必ずと言っていいほどボドゲで遊んでいます。サークルに置いてあるボドゲの数は正確には誰も把握していませんが100以上はあると思います。
超人ロック
単に「超人ロック」とだけ言えば聖悠紀先生による漫画のことを指します。今年で生誕50周年を迎えてなお続いている超長寿漫画です。人類が宇宙に進出した未来、作中屈指の超能力と永遠の命を持つ主人公ロックが色々な時代・惑星で事件に巻き込まれたり他のエスパー達と戦ったりという感じの王道スペースオペラです。
ボードゲームの超人ロックは、この漫画を原作としてエポック社が30年以上も前に発売したもので、各プレイヤーが作中のキャラクターになりきり、自分の正体を隠しつつ、各々の勝利条件を満たすためにESP能力や兵器を駆使して戦闘したり、惑星や秘密基地を探索したりというゲームです。ゲームシステムが秀逸で、ルールの修正や大胆なキャラクターの追加などの変化を経てKMCロック、RPG研ロック、名大ロックなどの独自の進化を遂げて今でも遊ばれ続けています。
このゲームの面白いところは、なんと言っても正体の騙し合いです。原作ではESP能力により外見上は完全に他人になりすますことが出来るのですが、ボードゲームのシステム上では最初から公開の「シルエットカード」と自分だけが確認して伏せておく「正体カード」として再現されています。あまり強力なESP能力やキャラ固有能力を使ってしまうと敵陣営のエースに各個撃破されてしまうので能力を隠したり、味方のふりをして団体行動しつつ肝心なところで裏切ったりといった駆け引きがコミュニケーションを生み、皆でワイワイ盛り上がれるのがサイコーに楽しいです。
また、強力なキャラクターで強力なアイテムを引いた時の俺TUEEE感、かといって多対一に持ち込まれたりダイスの出目次第で窮地に追い込まれるゲームバランス、死んだはずのキャラクターが突然生き返ったりエース級のキャラクターが地形効果で死にかけたりといったハプニングなど、何度遊んでも異なった展開になるところも他のゲームにはなかなかない良い点だと思います。
ただ、なにせ発売が30年以上前なので、当然絶版となっていたのですが、なんと来る12月21日に国際通信社よりリメイクされて発売されます!!めでたい!!!一家に一台是非買いましょう!!!!
超人ロック【CHARACTER BOARD GAME】 国際通信社
KMCでは11/24、25に開催されたゲームマーケット2018秋の先行販売で入手できたので既に何回か遊んでいます。普段はKMCロックのルールで遊んでいるのですが、これを機にオリジナルルールで遊んでみたところ、ゲームから除外されたり、無限に*1攻撃を反射可能といった強烈なカードがいくつかあったり、陣営ごとの人数比がかなり違ったりして新鮮でした。RPG研や名大のルールと比べるとオリジナルからの変更がだいぶ少ないとは言え、こういったあまりに苛烈な部分はしっかり修正さているのだと実感しました。なお、リメイク版ではこういった独自に発展してきたマイナールールを尊重するため、あえてルールに修正を加えていないとのことです。ルールの曖昧性で困ったり、このカード壊れ性能すぎる!と思った場合はぜひKMCロックのページをご覧ください。
プレイログ
実際のプレイの様子を軽く紹介します。今回のメンバーはgnu、drafear、yuma、jacker、siotouto、base64です。
すべてのキャラクターはロックと敵対し悪事をなそうとするEvil、ロックと共にEvilの秘密基地を破壊せんとするGood、それぞれ別々の勝利条件のもとに動くSpecialの3陣営に分類されます。KMCロックのルールではロック:その他Good:Evil:Specialの陣営ごとに1:0:2:2とランダム*2に1人正体カードを選びます。僕が引いたのは追加キャラクターであるリュカーンでした。リュカーンは高い能力値に加えて強力なアイテムを最初から所持しているEvil陣営のエースとも言えるキャラクターです。
まずゲームの前編はEvilたちが悪巧みをしている秘密基地を探す惑星探索編です。僕はGoodキャラクターのシルエットカードを選択したのでGoodのフリをして惑星を探索します。惑星を探索するとエスパーを取り締まる警官と戦闘になったりトラップを踏んだりするのですが、たまにゲームを有利に進められるアイテムを拾えるので、歩いているだけで死ぬような虚弱な能力値キャラクターでない場合はGoodかSpecialのフリをすることが多いです。
探索していると「自分の正体露見」カードを引いてしまいました。これで自分がリュカーンであることは皆にバレてしまいました。せっかく高い能力地を持っているのでいろんなキャラのフリができたのですが、こうなっては仕方ありません。Evil陣営のエースとして仲間を集める役を果たします。
今度は「他人の正体判明」カードを引きました。これを使うと他人の正体を一人だけこっそり覗き見ることができます。これでdrafearの正体を見てみるとなんとロックのうちの一人である「テオ」でした。こんなに早く敵の親玉を発見できるとはついていますね。捨てる神あれば拾う神あり。
さらにさらに超強力なアイテムである「ニケ」を入手しました。これは能力値が高いほど有効活用できるカードなので、リュカーンとの相性は抜群です。もう怖いものなしです。
jackerさんが秘密基地の場所が書かれた「情報入手」カードを規定枚数入手したため後半の秘密基地編に突入します。秘密基地ではEvilシルエットを選択した人が重要拠点を裏向きに伏せて設置します。今回のEvilシルエットはsiotoutoさん一人のみなので、彼の好きなように配置できます。もしも彼の正体がGoodだった場合は破壊しやすいように隣接して設置することができるので、まだ正体を確認していない僕は少々不安です(まぁニケがあるので大抵なんとかなると思っていますが……)。
秘密基地では敵の親玉がdrafearだと僕は知っているので、ドシドシ戦闘を仕掛けていきますが、そこはさすが主人公らしく高い能力値を活かしてのらりくらりと逃げられ続け、4つある重要拠点のうち3つを破壊されてしまいました。最後の重要拠点を破壊されると負けてしまうため、重要拠点の上に陣取ってdrafearが来るのを待ちます。
いよいよ最終戦闘。僕とdrafearの他には僕の味方っぽい動きをしているyumaが戦闘に参加します。更にここで秘密基地編に突入してすぐ罠にハマって死んでいたbase64が「実は生きていた」のダイスロールに成功して復活してきました。何もせずにあっさり死んでいったので敵か味方かわかりませんが放置します。
yumaと協力しdrafearを殴ったところものの数ターンで倒せてしまいました。やはりもともとの能力値が高い上にニケを持っていたら怖いものなしですね。その間base64は静観を決め込んでいましたが、yumaが攻撃したところ固有能力「分解消去」を発動したのでbase64は「ジェシカ」というキャラクターに確定しました。彼女は能力値が全キャラ中最低で、勝利条件が「ゲーム終了時に独身男性キャラとパーティを組んでいる」というなかなか厳しい条件のため、かわいそうな子扱いされている不憫なキャラクターです。そんなわけで彼女を引いてしまった場合はゲーム終了直前まで息を潜めて、最後に独身男性に媚を売って勝たせてもらうのがセオリーなのですが、今回のbase64のムーブは実に素晴らしいと言わざるを得ません。
リュカーンは独身男性なのであとはジェシカとパーティを組めばみんなハッピーエンドだったのですが、僕の手札には相手の正体を見ることが出来る「接触テレパス」のカードが……。そしてKMCロックでは相手の正体を見ている場合勝利宣言を拒否されません。魔が差した僕はbase64に対し接触テレパスを発動、見事(?)ダイスロールに成功して僕とyumaは正体を見てしまいました。これにはbase64もブチ切れ。殆ど無いESP能力を駆使してyumaに殴りかかりますがあっさりいなされてゲーム終了。僕とyuma、あとは死んでいたsiotoutoさんがEvil陣営だったので勝利しました。可愛そうなジェシカ……。
と、超人ロックはこんな感じのゲームです。文章力と時間とスペースの問題でここでは語りきれないので、ぜひ一度プレイしてみてください。
KMCM
明日のアドベントカレンダーは同期のnonyleneさんの担当です。お楽しみに!
KMCは年齢・所属・所在地など問わず誰でも入部できます。ボードゲームを遊びたい人、ついでにプログラミングしたりお絵かきしたり作曲したりゲーム開発したりしたい人はぜひ例会にお越しいただくかメールでご連絡ください!