ICFPC2019感想&反省

ICFPC2019が終わってもう一週間も経ってしまった。忘れないうちに感想と反省を書いておく。

リポジトリは生きてるサーバのパスワード等が入っているのでまだ公開しません。後処理をする前に中国旅行に来てしまったため……。

あ、チームは1kg cheeseです。Register直前までチームメンバーが好き勝手なチーム名を提案していてまとまらなかったが、yuustiがドミノ・ピザの1キロチーズピザを頼もうとか言い出したため、なし崩し的にこのチーム名になった。

コンテストの感想

問題文中でLambda LifterとPanterに言及しており、しかも3回アップデートが来るというそれらの回を踏襲したような形式から、また微妙な回かと思って戦々恐々としていたが、フタを開けてみればかなり良い問題だった。問題自体は適度な大きさの探索でとっつきやすく、プログラムを提出する必要がないのは安心感があるし、タイブレークとしてリアルタイム性のあるマイニング問題が出たのも新鮮味があった。

一方で、前半に要素を詰め込みすぎな感じはした。特に24時間ノンストップでLightningを終わらせて疲れ切った直後に、4時間でマイニング対応してねというのはなかなか辛い。体力を消耗して余裕がなくなる前に対処できるよう、最初からマイニングのようなリアルタイム性を要するシステムが来ると分かっていると良かった。

役割分担

今年はy3eadgbeが一身上の都合で抜けて、新しくkenkooooが入った。役割は以前とほぼ同じで、AI班がkawatea, yuusti, mkut、インフラがosa_k, amylase, kenkooooという分担になった。

  • ○役割分担、人数の割当て共に適切だった。
  • ✘インフラ班は人数が多かったものの、互いに互いのコードをちゃんと把握しておらず、ownerが寝ているときにトラブルが発生すると結構どうしようもなかった。特に今回はリアルタイム性が重要だったため、即時トラブル対処ができないと点数に直結してしまうので厳しかった。
    • 全員バラバラの言語かつモジュールでシステムを作っていたのが遠因となりシステムが把握しづらくなっていたので、全部入りの1モジュール構成にすれば見通しが良かったかもしれない。
    • MySQLにアクセスする部分はPythonを使っていたが、誰もPythonMySQLドライバの挙動をよく分かっておらず、異常に大量のコネクションを張ろうとして、サーバ側のコネクション制限に引っかかりまくって辛かった。
  • ✘インフラがいつ壊れるか戦々恐々としていると結構脳に負荷がかかるので、暇な時にAIを実装してみるといった遊びができなくて悲しい。
    • 自分は左手法で隙間を舐めていく動きがいいんじゃないかとずっと主張していたので実装したかったけど、余裕がなくて結局諦めてしまった。
    • 壊れにくく、かつ他人にメンテを投げやすい設計ができると負荷が軽減できるのでは?

解答ファイルの管理

前回まではソルバの出力をwrapperがポータルサイトREST APIを叩いてアップロードする形式だったが、今回は所定のディレクトリに置かれたファイルをIndexerが定期的に確認する方式にした。

  • ○前回までと同様、Flask製のポータルサイトMySQL Connection Error等で落ちまくっていたため、HTTP経由で生成した解答を登録する方式だとほぼ間違いなく死んでいた。
  • ○インフラが整わないうちに生成された解答をとりあえずコピーしておき、後で採点するというムーブができた。
  • ✘ファイルはコピーされたがIndexerにまだ検出されていない状態の解答が見えないため、AI班に待ちが発生してしまった
  • ✘解答のVerifyもIndexerにやらせてしまったため、バッチ実行の後などで解答が大量に生成された後はIndexerが遅くなった。
    • 新しいファイルを拾ってキューに入れるプロセスと、実際に採点と登録をするプロセスを分ければマルチプロセスできそう。
    • ソルバを実行するためのwrapperに採点まで組み込む構成も考えたが、verifierがおかしいことが後でわかった時のbackfillや、複数のマシンをセットアップした時にいちいちHeadless Chromeをセットアップしないといけなくて面倒くさいなどの、非本質的な部分でrejectしてしまった。ただ、AI作成者が自分のマシンでwrapperを走らせていたことも考えると、一概にダメとは言えなそう。

ソルバの管理

前回と同様、Gitリポジトリのmasterで指定されたパスにあるコードを make && ./run.sh する素朴な実装にした。

  • ○ソルバの更新がすぐに反映できた。
  • ○サーバ上で実行する限りにおいて、必ず最新の補助スクリプトを使うことを強制できた(2017年はブランチで分けたが、古い補助スクリプトが使われて問題を起こすケースがあった)。
  • ✘ソルバにバグが混入すると、その後もずっとバグり続けるのが厳しい。オフライン実行型のタスクなら普通は大きな問題にはならないが、今回はLambda Coinのマイニングというオンラインに近いタスクがあったため、バグったソルバのせいで解答に失敗することがあった。最終的に10万LMC程度損していると思われる。
    • ソルバをパッケージ化してfreezeし、可能な範囲で正しさが保証されているソルバを動かせるようにするべきだった(ディレクトリを切ることで擬似的にfreezeしてもらっていたが、間違ってバグっているコードで上書きする事故があった)。

マイニング

Lightning終了時点まで完全にオフラインの問題だと思っていたところに突然ぶっこまれたので、あまり複雑なシステムを実装する気力がなく、最終的にはその時点で有力だったソルバと、kawateaが作ったマップ生成プログラムを直列に実行してsubmitするスクリプトを書いた。

  • ○初回のマイニングが開始した時点ではとりあえず動いた。
  • ✘ずっと動かしていたら、問題サイズがある程度大きくなったところでソルバの実行に長時間かかるようになり、数回解答に失敗した。
    • 最終的に、マイニングのソルバも通常のシステムとマージして、強いCPUを使いつつ複数のソルバを並列で回せるようにした。もっと早くやっておくべきだった。

リポジトリ構成

単一のGitリポジトリにみんなでコミットする構成。特にブランチ分けやレビューなどしなかったので、無限にマージコンフリクトが起きて面倒だった。git pushのときに勝手にfetchしてrebaseするオプションとかありそうだけど……。