例外の処理コストとパフォーマンスチューニング

例外に掛かる処理コストについてint.Parseとint.TryParseを使って比較調査を行ってみました。


入力された文字が数字か否かを判断する方法として、数値ではない場合に例外を発生させるint.Parseがあります。

    private bool IsNumericByException(string test)
    {

      try
      {
        int.Parse(test);
      }
      catch (FormatException ex)
      {
        return false;
      }

      return true;

    }


もうひとつ別のバージョンとして、数値ではない場合にFalseを返すint.TryParseがあります。

    private bool IsNumericByTryParse(string test)
    {
      int a;
      return int.TryParse(test, out a);
    }

この2つの処理をそれぞれ10万回呼び出すと、int.Parseが6535ミリ秒、int.TryParseが9ミリ秒という結果に。



700倍くらいの速度差がある計算になります。比率で言えばそういうレベルですが10万回例外を発生させてもその程度です。例外あたり0.6ミリ秒なので、問題視することもなさそうです。


もし、この箇所をチューニングしたいと考えるなら、AppDomainにFirstChanceExceptionイベントハンドラを登録して、どういった例外が多く発生しているのか分析すると頻度の多い例外に焦点をあてることができます。問題の例外をTryParseのような例外が発生しない形式に置き換えることでパフォーマンスを向上させることができます。int.Parseだと、1例外あたり0.6ミリ秒程度ですが。(もちろん、これは環境に拠ります)


検証コード

using System;
using System.Windows.Forms;
using System.Diagnostics;

namespace WindowsFormsApplication1
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {

      AppDomain.CurrentDomain.FirstChanceException += FirstChanceExHandler;

      const int loop = 100000;

      Stopwatch sw = new Stopwatch();

      sw.Start();

      for (int i = 0; i < loop; i++)
      {
        IsNumericByException("hoge");
      }

      sw.Stop();

      label1.Text = sw.ElapsedMilliseconds.ToString();

      sw.Reset();

      sw.Start();

      for (int i = 0; i < loop; i++)
      {
        IsNumericByTryParse("hoge");
      }

      sw.Stop();

      label2.Text = sw.ElapsedMilliseconds.ToString();

    }

    private bool IsNumericByException(string test)
    {

      try
      {
        int.Parse(test);
      }
      catch (FormatException ex)
      {
        return false;
      }

      return true;

    }

    private bool IsNumericByTryParse(string test)
    {
      int a;
      return int.TryParse(test, out a);
    }

    private void FirstChanceExHandler(object sender, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e)
    {

      StackTrace st = new StackTrace(e.Exception);
      System.IO.File.AppendAllText("exception.txt", e.Exception.Message + e.Exception.GetType().FullName + st.ToString() + "\n");

    }


  }
}