2021年8月11日水曜日

Mac を カフェイン漬けにしてフレーム落ちを防ぐ

人も計算機も怠ける。

Lv3マルスは手強く、学習も進展が見えづらい。そんな状況で兼ねてから懸念していた事に取り組んだ。取得しているビデオフレームが具体的にどうなっているかの検証である。

キャプチャーカードのフレームレートは基本的に60Hzなのだが、開発初期の検証によって、ビデオフレームが均一のタイミングで来ているわけではない事がわかっていた。具体的にはだいたい12msの整数倍の間隔で、最終的に60Hzになるようなタイミングで来ている。例としては (12, 12, 24, 12, 24, 12, 12, 24) といった間隔である。周期性もわかりづらく、12msが1回だけ挟まる回もあれば2回挟まる回もある。24msが連続で来ることは(たぶん)ない。20Hzでの動作を目的としているので、3フレームごとに画像を取得したいのだが、その間隔は48ms から 60 ms (実測した時もだいたいこの間隔になる。23msのやつが2回くるか1回来るかで変わってしまう)と、安定しないものになる

今まではあまり気にせずに1ステップごとにタイマーを設定して、「一周したあとで46ms経っていない場合は46msになるまで待つ」というアルゴリズムでやっていて、大体平均して20Hzのデータを作り出していた。しかし、この方法だと実際に何フレームスキップしたのかもよくわからないし、遅延でフレームドロップがあった際に認識することもできない。

もし全てのフレームが問題なく読めるのであれば、3フレーム取得して2フレーム分は捨てる、という選択もできるかもしれないが、今までの実装ではコントローラへの出力も同時に行うと60Hzで画面を読み込むことができない、という問題があった。

そこで、コントローラの方をコードを見てみると、コマンドを出力したあと、スイッチ側からの入力を確認するようにできている。これがだいたい一周期あたり16msで、ビデオ出力の60Hz (16.666...Hz)と微妙に異なる値になっていた。この周期に一致しないとコマンド出力が完了しないことになっていたのでビデオフレーム取得と同時にやると双方が干渉して60Hzの取得は不可能、という形になっていた。

少し検証してみた結果スイッチ側からの入力を無視してもコントローラ操作は可能らしく、フレームレートが十分に遅ければほぼコマンド入力を落とすことなく操作できることがわかった。(本来はスレッドを立ててやるべきなのかもしれないが…)これでスイッチ側からの入力を待つ時間を削除できるので、コマンド入力の時間が最大16msかかっていたものを1ms以下に抑えることができた。

コントローラ操作の時間を削減できたので、他の障害がなければビデオフレームをだいたい60Hzで入力できるようになった。他の障害がなければ、と書いた。障害になるものがいくつかあったのである。

まずは学習を行うスレッドである。CPUを100%近く使う計算を常時行っているので、これが稼動しているときは60Hzが厳しくなる時もある。これについては60Hzで取得するのを諦めて、タイミング測定とビデオフレーム取得を繰り返すことで解決した。具体的にはフレームを取得できた時点で前回取得時の時間と比べて、40ms以下だった場合はまだ3フレームスキップされていないと判断してもう一度取得し、そうでなければ受け入れる、というものである。そのときに時間を記録しておき、もし65ms経過していた場合はフレーム落ちの可能性があるとして報告するようにした。この処理をすることで、前よりも強く3フレームごとに取ることの保証が可能になり、問題がある際も認識できるようになった。

もう1つ問題になったのが、Macの謎の挙動である。スクリーンセイバーや、ディスプレイをオフにする設定に関係なく、1、2分ユーザからの入力がないと勝手にフレーム落ちを初めるのである(怠けモードと呼ぶことにする)。この現象があることは以前から気がついていたのだが、落としたフレームの数を数え始めたことによってより顕著に問題視されるようになった。怠けモードに入る前はフレーム落ちは0.1%以下程度(一試合3000ステップで最大2フレーム程度)なのに対し、怠けモードに入ると10%程度も落とすようになる(一試合100-500フレーム!)。これはまずいと思い対策を考えることにした。

まずはソフトウェアでの対処を考え、無理なようならUSBの疑似HIDを作って一定間隔で何か入力をしてもらうようにしようと思い、まずはソフトを探してみた。どうやら caffeinate というコマンドが元々 Mac に入っているらしく、これが一時的にスクリーンセイバー、ディスプレイの消灯、ディスクの回転停止などの電源関係の機能を抑制してくれるらしいことがわかった。

試した結果、caffeinate では怠けモードは解消されないことがわかった。他にも色々調べた結果、怠けモードは他の省エネ機能 (App Napやら何やら)とも関係ないこともわかった。次に試そうと思ったのがマウスやキーボード入力を勝手に定期的にしてくれるソフトで、 Caffeine がそれに該当するらしいことがわかった (少なくとも昔は、「本来存在しない F15 というキー」を押し続けるソフトだったらしい)。導入し、Karabiner Element のインスペクタで確認しても、F15なるキーの入力がなかったので、不安に思いながらもしばらく放置した結果、無事に怠けモードに入るのを回避できたのである!

これらの試行錯誤の結果、フレーム落ちは合計で0.1%以下にまで抑えることが可能になり、今までよりもずっと綺麗なデータを取得することが可能になった。データのクオリティは学習に直接影響を与えるので、これは大きな変化かもしれない。今まで配信ソフトが起動しているときどんだけフレーム落としてたんだ…? 検証の必要がある…。

色々直した結果、現在の学習状況はこんな感じである。

上のグラフが報酬(青、左Y軸)とロス(橙、右Y軸)で、下がQ値(青、左)と試合の長さ(橙、右Y軸)である。X軸は共通して試合数である。報酬もロスも一定のパターンが見えづらい変化をしているが、Q値はある時期(400試合目くらいか)から着実に伸びてきている。このパターンはLv2の時も見られたので、勝ちパターンだと思いたい。グラフを用意したのが途中からで、これはLv3の最初からではないのだが、ここまででもだいたいLv2のときの2倍くらいの時間がかかっている気がする。ちなみに本稿の修正を施したのはグラフの1000試合目くらいである。

これで直すべきところは直した。あとはAIガオガエンを信じて練習を続けてもらうだけだ。

0 件のコメント:

コメントを投稿