まーぼうの書きたいことをたらたらと

研究したりゲームしたり作ったりの日記

Visual Studioのパフォーマンスプロファイラーを使ってみた

こんにちは、まーぼうです。久々のブログ投稿です。

今日は珍しく技術系っぽい記事を書こうと思います。

パフォーマンスプロファイラーとは?

Visual Studioを用いて作ったプログラムの処理効率を分析するためのツールっぽいですね。今回初めて使いました。

docs.microsoft.com

なぜ使うのか

最近、今作っているゲームでスコアシステムを作っていました。

スコアアタックとそれを用いたランキングシステムを作っていたのですが、ランキングデータをロードするのが結構重かったんですね。何となく重い理由は分かるんですけど、ちゃんと・ラクに測定してみたいと思ったので、分析ツールを使うことにしてみました。

使用したVisual StudioのバージョンはVisual Studio 2017 communityで、今回使ってみるソースコードは、このcommitのソースコードになります。

github.com

まず使ってみる

[分析]→[パフォーマンス プロファイラー] で、パフォーマンスプロファイラーを開いてみました。

f:id:mabo168general:20190521190211p:plain

チェックしたい項目を選んで、ゲームを起動。左にプロファイラーを寄せています。

f:id:mabo168general:20190521190254p:plain

ゲームを終了すると、レポートを作ってくれます。起動時間に比例して作成に時間が結構かかるので、長い間測定するのは厳しいですね。

f:id:mabo168general:20190521190450p:plain

レポートを作成し終わると、こんな画面に。そうそう、こういうのが見たかったんや!

f:id:mabo168general:20190521190542p:plain

上画面を見てみると、途中でFPSがガクッと落ちている部分があります。ここでどの処理が重かったかを調べたいので、GUIを操作して調べたい時間帯を絞ってあげます。

下画面にどの処理が重かったかが並ぶので「プログラムの階層として深い部分にある割に処理時間を喰っている場所」を探します。普通に調べるとmain関数がトップに来るので……

f:id:mabo168general:20190521191253p:plain

怪しそうところはScoreRankingData::ScoreRankingData()っぽそうですが、確信がないのでこの関数を呼んでいる1つ上の部分から調べてみます。StageSelectScene::StageSelectScene()ですね。

f:id:mabo168general:20190521191526p:plain f:id:mabo168general:20190521191537p:plain

他のデータのロードに比べてScoreRankingData::ScoreRankingData()が重いのが明確ですね……ということで、ScoreRankingData::ScoreRankingData()をしっかり調べてみます。

f:id:mabo168general:20190521191700p:plain

sp.split()という、データを解釈する関数が滅茶苦茶重いのが分かりました。最初はデータの格納方法(m_stageDataMap.insert(~~~)の部分です)を直そうと思っていましたが、これは解釈部分を直したほうが良いのが分かりますね。危うく効果が薄い作業をするところだった。

ちなみに、FpsMesuringというクラスで処理速度を測定しようと試みているのが分かりますね……

もっと詳しく見てみます。

f:id:mabo168general:20190521192021p:plain

一見するとpushFunc()が重いんですが、これは再帰関数なのであまりアテにならないなぁと思いました。ここらへんどう解釈すればいいのか難しいですね……

他の重い部分の候補はfor文の部分っぽいです。ループ回数が多すぎて重いのかなと思います。減らす方法については心当たり(「文字列を先頭から読み込んで区切って。区切った文字列に対してまた先頭から読み込み」という処理をしているのでO(n2)O(nm)からO(n)くらいに効率化できそう。mは探索木の深さ。)があるので直す価値がありそうです。

あとは、splitStr.push_back()も怪しいみたいです。string型の変数を大量に用意してpush_back()処理をしないといけなくなっているのも重い原因なのかなと思います。解決策としては

  • 予めreserve()する(対処療法的)
  • stringは1つにまとめて、部分文字列の情報はindexで管理する

といった方法が思いつきます。

分析した結論

  • StringBuilder::Split()を軽くする。
  • 方針は2つ
    • for文を回す回数を減らす
    • 部分文字列の管理をindexで管理する

使ってみて

どこが重いか、というのをデータをもとに見つけるのが楽ですね。プログラムの修正の戦略を立てるのにすごく役に立ちました。あと凄い使いやすい。今後も使っていきたいですね。

ただ、分析のレポートデータ自体は重いので、そうバンバン使うものじゃないなぁとは思いました。30秒くらいの起動で30MBくらいの容量になったので。まずはPCの容量を増やすべきですが。

  • ある程度開発したらこれを使って検査
  • 実装した機能が重いなと思った時の原因分析

といった使い方をすると良いと個人的に思いました。趣味ではなく仕事のソフトウェア開発だとどうなのかが気になります。

今回の記事はここまでです。読んでくださりありがとうございました。 f:id:mabo168general:20190521194243p:plain