コード探偵 ファイル04 「CPUは正直者」
前回と前々回で紹介した方法で処理落ちの犯人を割り出すことができました。犯人を割り出したら、次はどのようにして犯行現場を特定する必要があります。
今日、取調室に呼び出されたのはCPU。CPUは多彩な知能犯で、数百から数千もある複数の箇所を駆け巡り、ありとあらゆる方法で、時にはこちらが想像もしない方法で犯行を行います。CPUの犯行現場は数百から数千もある複数の候補箇所から割り出す必要があります。これら全ての候補ををしらみつぶしに探していくには途方もない労力と時間が必要になってしまいます。
では、どうすればCPUの犯行現場を押さえることができるのでしょうか?
実はこのCPU、根は正直者で「はい」「いいえ」で答えられる質問には常に正しい返事をしてくれます。この性質を利用することによって、数百、数千、たとえ犯行現場が1万箇所あったとしても14回以下の質問で犯行現場を見つけ出すことができます。
その方法とは、犯行現場になりそうな箇所に番号をつけてから「パフォーマンスを殺した現場は○番より下か?」という質問を繰り返すだけです。もし、犯行現場になりそうな場所が8ヵ所あった場合、最初に「犯行現場は4番以下か?」と質問し、答えが「はい」の場合は次に「2番以下か?」と質問します。最初の質問の答えが「いいえ」なら「6番以下か?」と質問になります。
言い換えれば、最初に犯行現場になりそうな箇所を半分にわけて、どっちのグループの場所で犯行を行ったのかを質問、その答えによって更に半分に分けて…という風に繰り返すわけです。
ここまでで気づいた人もいると思いますが、これは検索アルゴリズムである二分探索の方法そのものです。
タイムルーラーで処理時間を視覚的に測定する
実際にはタイムルーラーを使って常に二箇所の処理時間を測定していきます。まず最初にGame.UpdateとGame.Drawに掛かった時間を測定します。Game.Updateの処理時間がGame.Drawの処理時間より長く掛かっている場合は次にGame.Updateの中で処理している内容を二分して時間の測定をします。以下のコードは一例です。
// 上半分の処理時間測定
timerRuler.BeginMark("Upper", Color.Red);
UpdatePlayers(); // プレイヤーキャラクターの更新
UpdateEnemyAI(); // 敵キャラクターAIの更新
timerRuler.EndMark("Upper");
// 下半分の処理時間測定
timerRuler.BeginMark("Lower", Color.Purple);
UpdateEnemies(); // 敵キャラクターの更新
UpdateParticles(); // パーティクルシステムの更新
timerRuler.EndMark("Lower");
この結果、上半分(Upper)の処理時間が下半分(Lower)よりも時間が掛かっているとすれば、処理落ちの原因はプレイヤーキャラクターの更新部分か、敵キャラクターの更新部分のいずれかになります。次にそれぞれの結果を測定し、更にそれぞれのメソッド内でGame.Updateメソッド内で測定位置を変えていったようにしていきます。
タイムルーラーの特徴として、処理時間を視覚的に見ることができることです。下図のようにタイムルーラーの状態を見るだけで、処理落ちしているのか、どれくらいの時間を処理に費やしているのかというのが視覚的にすぐにわかります。
この例では処理落ちはしていませんが、仮にこの状態から更に最適化をしようとした場合、更新部分の処理時間が描画より掛かっていることが判ります(一段目の黄色い部分にはデバッグ情報表示に掛かった時間も含まれているので、二段目のバーにゲーム自体の処理時間を測定している)。
このコードのゲーム更新部分ではアニメーションの更新とキャラクターの移動しかしていないので、それぞれに掛かった時間を測定した結果が以下のようになっています。
アニメーション処理に時間が掛かっていることが判ったので、次にアニメーション処理内で時間を掛かっている場所測定するという風に繰り返します。
このコード内で犯行現場になりそうな箇所、つまり処理落ちしそうな原因となる箇所は大体20ヶ所程ありますが、「更新部分の処理時間の方が多いか?」と「アニメーション処理時間の方が多いか?」と、たった二つの質問しかしていないこの段階で残り4ヵ所程度まで絞り込むことができています。
このようにCPUパウンドの場合、処理速度に掛かった時間を測定をする箇所変えながら二分探索を使うことで、すばやく犯行現場を割り出すことができます。
つづく……