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

こんにちは、弥生の木村です。
この記事は弥生 Advent Calendar 2020の13日目のエントリーになります。
簡単に自己紹介です。

2019年4月:新卒入社  
2019年6月:開発本部に配属。開発研修(Java/HTML/CSS/JavaScript)  
2019年11月:自動テスト開発・運用チームのエンジニア(C#)  
2020年4月以降:会計オンラインのエンジニア(ASP.NET MVC)  

エンジニア歴2年目のまだまだ若手です。
今回は、VisualStudioのパフォーマンスプロファイラーを使ってみたお話です。

きっかけ

社内資料の中に、品質高く効率的な開発を進めるための支援ツールの例として以下が挙がっていました。

  1. 単体テスト
  2. カバレッジ分析
  3. 静的コード解析
  4. プロファイラー

「プロファイラー?何それ?」状態だった私。
エンジニアとして早めに押さえておこう!と思い、今回勉強しました。

※参考

Visual Studio でアプリのパフォーマンスを測定する

プロファイラーとは?

プログラムが実行される様子を監視・記録し、その性能の解析ができます。
プログラムの性能とは、実行時間、CPU使用率、メモリ使用量などです。
プロファイラーを使うことで、「なんか処理が遅いけどなんで?」という時にボトルネックになっている関数を見つけたり、「メモリリーク起こしてるよ...」という時にメモリを大量に消費している処理を特定できたりします。

使ってみよう

今回は普段開発で使用しているVisualStudioの「パフォーマンスプロファイラー」を使ってみます。

サンプルプログラム

10万行の文字列を読み込み、1つの文章に連結して出力する単純なプログラムです。

class Program
{
    public static void Main(string[] args)
    {
        int lineCount = 100000;
        string[] lines = Import(lineCount);
        string text = Concat(lines);
        Output(text);
    }

    // 各行を取り込む
    public static string[] Import(int lineCount)
    {
        string[] lines = new string[lineCount];
        for (int i = 0; i < lineCount; i++)
        {
            lines[i] = "sample";
        }
        return lines;
    }

    // 各行を連結する
    public static string Concat(string[] lines)
    {
        string text = "";
        for (int i = 0; i < lines.Length; i++)
        {
            text += lines[i];
        }
        return text;
    }

    // 文章を出力する
    public static void Output(string text)
    {
        Console.WriteLine(text);
    }
}

1.パフォーマンスプロファイラーの起動

パフォーマンスプロファイラーの起動の手順は以下のとおりです。

手順1. デバッグ>パフォーマンス プロファイラー
f:id:ym_AdventC:20201208185426j:plain

手順2. 分析ターゲット、ツールを選択して開始
f:id:ym_AdventC:20201208185539j:plain

そうするとデバッグ起動が開始されます。

2.分析結果の確認

処理が最後まで終わると診断結果が表示され、主に以下を確認できます。

  • 診断セッション:処理秒数
  • 上位の関数:CPU使用率が高い関数
  • ホットパス:ボトルネックとなる関数

f:id:ym_AdventC:20201208185657j:plain

Main関数がボトルネックとなっていますが、具体的にどの処理が原因なのでしょうか。
さらに見ていきます。

f:id:ym_AdventC:20201208185712j:plain

Concat関数が原因のようです。
モジュールシンボルを読み込み、ソースコード上で確認します。

f:id:ym_AdventC:20201208185723j:plain

f:id:ym_AdventC:20201208185741j:plain

「text += lines[i];」が赤くなっていますね。
どうやら、Concat関数の文字列追加処理が根本原因のようです。

3.ボトルネックを改善

では、ボトルネックの処理を修正してみます。

<変更前>

// 各行を連結する
public static string Concat(string[] lines)
{
    string text = "";
    for (int i = 0; i < lines.Length; i++)
    {
        text += lines[i];
    }
    return text;
}

<変更後>
文字列追加処理をより軽量で行うことができる「StringBuilderクラス」を用いる方法に置き換えます。

// 各行を連結する
public static string Concat(string[] lines)
{
    StringBuilder text = new StringBuilder();
    for (int i = 0; i < lines.Length; i++)
    {
        text.Append(lines[i]);
    }
    return text.ToString();
}

再度、パフォーマンスプロファイラーを起動し測定します。

f:id:ym_AdventC:20201208185803j:plain

修正前 修正後
処理時間 22.147秒 6.765秒

改善により、処理時間は約1/3に短縮しました!

まとめ

今回初めてパフォーマンスプロファイラーを使ってみました。
プログラムは単純な例でしたが、これが大規模で複雑なプログラムであるほど助かる機能なのかなと思いました。
今後、会計オンラインの業務でも使ってみようと思います。
パフォーマンスプロファイラーを使ったことのない方の参考になれば幸いです!