feature image

2024年12月12日 | ブログ記事

ISUCON14 fail記 KatoMegumi

最終ベンチ 30164点 参考順位 20位 failでした。構成はサーバー1台です(分割に失敗)

追試でのスコアが最終スコアより低かったのがfail理由のようでした。が、競技後の感想戦で何も手を付けずにベンチを回したところ75%以上スコアが出ていて、fail理由は迷宮入りしています。(困った……)

各FAIL理由の詳細については以下のとおりです。
...
2.負荷走行NG : 追試における3回の負荷走行が全てFAILであるか最終スコアの75%以下でした

30,164 KatoMegumi 2.負荷走行NG
https://isucon.net/archives/58837992.html

分担

NaruseJunの分担を参考に、各メンバーの初動を何となく決めていました。

やったこと

Ansible 流し込み

書いておいた Ansible でせっせと各サーバーに設定を流し込みます。なにをしたかと言うと

んな感じです。これ以外のリポジトリに内容を書き込むといった実装に依存するところは温かみのある手作業でやってあげます。

あとパスワード書くとこだるいな~って思ってたら今回はパスワードなしで入れてビビりました。

pprotein設定

pprotein用にnginxやmysqlのログを設定

pprotein用のコードをアプリに仕込む必要があったのでその作業も実施。echoやmuxが使用されていた場合、1行echoInt.Integrate(e)と書いて終わりだったはずが、chiという知らないルーターが使われていたため以下のように対応(standalone.Integrateでうまく動かせなかった)。

{
  mux := chi.NewRouter()
  mux.Handle("/debug/log/httplog", NewTailHandler("/var/log/nginx/access.log"))
  mux.Handle("/debug/log/slowlog", NewTailHandler("/var/log/mysql/mysql-slow.log"))

  mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
  mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
  mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
  mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
  mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
  mux.Handle("/debug/fgprof", fgprof.Handler())
  go http.ListenAndServe(":3000", mux)
}

これによりpprof, http log(alp), slow query log(slp)の計測結果をweb uiで簡単に見ることができるようになりました。本来はgitRepositoryMiddlewareによりその計測時のビルド元コミットへのリンクも表示されるのですが、chiルーターでこれを使うことができず断念。

後でわかったのですがどうやら以下のようにすればよかったようです。

pproteinHandler := integration.NewDebugHandler()
go http.ListenAndServe(":3000", pproteinHandler)

appGetRides → 1110

o1くんがN+1を消してくれました。

⚡️ appGetRides by trasta298 · Pull Request #4 · KatoMegumi24/isucon14

calculateDiscountedFareがN+1になってたのも解消したのだが、改善してる数字が見えず保留にしていたら最後まで忘れていた。

⚡️ appGetRides N+1消した by trasta298 · Pull Request #6 · KatoMegumi24/isucon14

ライド情報周りのindex → 3700

この後も継続的に気付いたタイミングで slow query log を見て使用できるメモリ量とか write 優勢になってないかを確認しながら index を仕込むことを繰り返してました。

https://github.com/KatoMegumi24/isucon14/pull/5
https://github.com/KatoMegumi24/isucon14/pull/8

appGetNearbyChairsのN+1解消(?) → 3390

o1使役係であるが担当。この辺からo1を信頼しすぎて全然レビューしてなかったので、for文1個分残ってるのに気づかず。あとで回収。

⚡️ appGetNearbyChairs N+1 by trasta298 · Pull Request #9 · KatoMegumi24/isucon14

appPostRides の N+1解消 → 3440

o1くんがやってくれました。

⚡️ appPostRides の N+1 解消 by trasta298 · Pull Request #10 · KatoMegumi24/isucon14

notificationのポーリングを2000ms毎に → 4600

クライアントアプリの動作確認時に大量のGETリクエストが見えていたので要改善メモをしてました。そのままが確認。
マニュアルを読むと

状態が変更されてから3秒以内に通知されていることが期待されます。

とあったので一旦2秒に変更しました(後に変更される)。

ownerGetChairs → 4200

slowlog見ると一番上に

SELECT `id`,`owner_id`,`name`,`access_token`,`model`,`is_active`,`created_at`,`updated_at`,IFNULL(`total_distance`, N) AS `total_distance`,`total_distance_updated_at` FROM `chairs` LEFT JOIN (SELECT `chair_id`,SUM(IFNULL(`distance`, N)) AS `total_distance`,MAX(`created_at`) AS `total_distance_updated_at` FROM (SELECT `chair_id`,`created_at`,ABS(`latitude` - LAG(`latitude`) OVER (PARTITION BY `chair_id` ORDER BY `created_at`)) + ABS(`longitude` - LAG(`longitude`) OVER (PARTITION BY `chair_id` ORDER BY `created_at`)) AS `distance` FROM `chair_locations`) AS `tmp` GROUP BY `chair_id`) AS `distance_table` ON `distance_table`.`chair_id` = `chairs`.`id` WHERE `owner_id` = 'S'

が見えてヤバかったので対応。最初はが対応(WIP: 総移動距離をchair_locationsに入れるように by eyemono-moe · Pull Request #7 · KatoMegumi24/isucon14)。
chair_locationsテーブルに最新緯度経度とそれまでの総移動距離を入れて常に各椅子につき1つのchair_locationレコードが存在するようにした。
が、initial dataのlocationsのマイグレーションがめんどくさかったため一旦放置。trastaに引継ぎ(⚡️ ownerGetChairs by trasta298 · Pull Request #12 · KatoMegumi24/isucon14)。

引き継がれたもの、initで既存のデータも加工しなきゃいけないことに気づかず時間溶かす (後悔ポイント)
chairテーブルに total_distance_updated_at last_longitude を持ってpost時に計算するようにした。

chairPostCoordinateのクエリ削減 → 7090

pprof見ると POST /api/chair/chairs のリクエストが多く、ネックになっていたので、クエリを減らして高速化を図った。o1だけだとうまい感じにならなかったので、方針は採用しつつ、cursorでclaudeとかを使っていい感じに書いた気がする。
chairテーブルにlast_longitude last_latitude を持っておくことで、chair_locationsテーブルへのクエリを減らした。

クエリを減らす by trasta298 · Pull Request #16 · KatoMegumi24/isucon14

ride_status のキャッシュ → 7400

getLatestStatus がさまざまなところで呼ばれていてキャッシュした方が早そうというノリで実装。そんなに増えなくてサーバー分割したときバグるのが怖いので入れずに放置。

ただこの後サーバー分割に失敗。別サーバーでも isuride-matcher が起動していてそれと競合したのが原因。終了後に試したらスコア 36000 だったそう。くやしい。

マッチングアルゴリズムのやつ → 20000

スコア計算などが書かれているマニュアル等もコンテキストとして含めて、o1に投げるとコードを書いてくれた。最初はうまい感じにいかなかったが、僕の考える配送戦略等をコンテキストとして含めるといい感じになった。internalGetMatchingは左をride、右をchairとした割当問題(二部グラフの最小重み最大マッチング)なのでハンガリアン法を使うとよい。
My Algorithm : kopricky アルゴリズムライブラリ

出してくれたコードを反映させるとベンチマークでエラーが出るので、プログラムのミスを疑っていろいろ調べたが、単純に通知のポーリング間隔が長すぎるのが原因なことに気づくまでに時間を1時間以上溶かして大後悔ポイント。o1くんごめんなさい。

⚡️ 楽しい椅子マッチング by trasta298 · Pull Request #18 · KatoMegumi24/isucon14

appGetNearbyChairs の N+1 → 24000

終了30分くらい前に N+1が残っていることに d_etteiu8383 が気づいたので trasta が対応。o1で一発。

scでchairsのキャッシュ実装 → 29000

pprofを見るとchairAuthMiddlewareでのsqlx.GetContextが重そうなことが分かったため実装を確認。chairAuthMiddleware内で毎回SELECT * FROM chairs WHERE access_token = ?してたのでaccess_tokenをkeyにしたchairのキャッシュを持つことに。

はじめmapで雑に実装したら排他制御できてなくて壊れたので素直にmotoki317/scを使わせて頂きました。@tokiありがとう。めちゃくちゃ使いやすかったです。

ChatGPT o1 活用方法

単純なN+1解消や処理の一元化など、変更する範囲が狭いときに主に力を発揮する気がする (調べたわけではない) ので主にそういうタスクを任せた。
本番直前にChatGPT o1 pro modeが公開され、o1 proがGUIでしか使えなかったので、今回のために、プロンプトの作成用の repo to textツールを自作した。 (o1に適当に作ってもらっただけなので公開予定なし)
これでコンテキストに含めたいファイルを選んでやってもらいたいタスクや相談したいことを書くだけでいつでも使えるようにした。

ぶっつけ本番で試したのでどうなるかな~と思っていたが、蓋を開けてみれば神だった。途中から出力されたコード見ずにベンチ回して一発で通ったからヨシってやってました。
日進月歩で進化しているので来年はどこまで活用できるか楽しみです。

使用例: https://chatgpt.com/share/6755a15a-67e4-8013-9f4e-c36121d75678

感想

trasta

普通に悔しいです。事前練習やらなかったつけが初動の遅さにでた気がします。あと変なポイントで躓きすぎた。リベンジします。あとLLMあれば僕いらない。

d_etteiu8383

今回が初参加でした。とにかく僕の実装速度が遅すぎました。練習が足りなかった。あまり大きな改善ができない&インフラ周りを2人に丸投げしちゃってたので申し訳ない気持ちでいっぱいです。

anko

練習すれば時短になったこと数えられないほどあってそれは反省なんですが、割と雰囲気よくできてメンバーも楽しんでいてよかった~って思ってます。次回は終了後にメンバーとグータッチできるように頑張りたいです。

trasta icon
この記事を書いた人
trasta

いろいろやってます。

anko icon
この記事を書いた人
anko

21B/理学院物理学系 CTFがすきなオタク

d_etteiu8383 icon
この記事を書いた人
d_etteiu8383

グラフィック班とゲーム班とSysAd班所属 いろいろ活動しています

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2024年12月18日
ISUCON14にツールの力で勝ちたかった
mazrean icon mazrean
2023年11月26日
ISUCON13にツールの力で勝ちたかった(mazrean)
mazrean icon mazrean
2021年9月21日
ISUCON11 traP CM制作についての小話
dan_dan icon dan_dan
2024年12月18日
ISUCON14感想戦で40万点超えました
mazrean icon mazrean
2023年11月26日
【 #ISUCON 】 最近の若者は ssh しないらしいですよ
ikura-hamu icon ikura-hamu
2022年7月24日
ISUCON12に向けてダッシュボードを自作してログを可視化しました
Ras icon Ras
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記