2009-05-15 4 views
20

例外はパフォーマンス上のペナルティがあることを知っています。例外を回避しようとするのは一般的に大規模なtry/catchを落とすよりも効率的です。 ? try/catchを宣言しただけでは、たとえそれが例外をスローしないとしても、何のコストがかかりますか?Cでの 'try'のパフォーマンスコスト#

+8

あなたはこの質問に答えることができる唯一の人です。あなたは、使用しているハードウェアやソフトウェアの種類を知っている唯一の人です。あなたの顧客に関連するパフォーマンスメトリックを知っているのはあなただけです。現実的なベンチマークを書き、現実的なハードウェアで試してみてください。その答えを知ることができます。あなたのために質問に答えようとする人は誰でも、あなたのものではなく、彼らの顧客のために自分のマシン上でのパフォーマンスの特性を推測することです。いずれにせよ、それはあなたが必要とするデータではありません。 –

+0

@エリック - うーん。 –

答えて

26

tryの実行コストは非常に小さいです。例外処理の主なコストは、スタックトレースやその他のメタデータを取得することです。これは、実際に例外をスローする必要があるまで支払われないコストです。

しかし、これは言語と実装によって異なります。 C#でシンプルなループを書いて、自分で時間をかけてみませんか?

+1

tryは、実際には例外が実際にスローされたときにのみ実際に検査されるメタデータによって基本的に処理されるため、無料です。 –

5

共通のことわざは、それらがキャッチされたときに例外が高価であるということです - スローされません。これは、例外のメタデータ収集(スタックトレースの取得など)ののほとんどがであるため、実際にはtry-catch側(throw側ではなく)でのみ発生します。

実際には、スタックの巻き戻しはかなり速いです - CLRは呼び出しスタックを上り、見つかったfinallyブロックのみに注意します。純粋なtry-finallyブロック内のどの時点でも、ランタイムは例外(メタデータなど)を '完了'しようとしません。

私が覚えているところでは、フィルタを使って試してみると(「catch(FooException){}」など)、例外的に何もしない場合でも同じように高価です。

私は次のブロックと方法(CatchesAndRethrowsそれを呼び出す)と言ってベンチャーう:

try 
{ 
    ThrowsAnException(); 
} 
catch 
{ 
    throw; 
} 

が方法で速いスタックウォークにつながる可能性があります - など:

try 
{ 
    CatchesAndRethrows(); 
} 
catch (Exception ex) // The runtime has already done most of the work. 
{ 
    // Some fancy logic 
} 

いくつかの数字:

With: 0.13905ms 
Without: 0.096ms 
Percent difference: 144% 

ここに私が走ったベンチマークがありますUG):

static void Main(string[] args) 
    { 
     Stopwatch withCatch = new Stopwatch(); 
     Stopwatch withoutCatch = new Stopwatch(); 

     int iterations = 20000; 

     for (int i = 0; i < iterations; i++) 
     { 
      if (i % 100 == 0) 
      { 
       Console.Write("{0}%", 100 * i/iterations); 
       Console.CursorLeft = 0; 
       Console.CursorTop = 0; 
      } 

      CatchIt(withCatch, withoutCatch); 
     } 

     Console.WriteLine("With: {0}ms", ((float)(withCatch.ElapsedMilliseconds))/iterations); 
     Console.WriteLine("Without: {0}ms", ((float)(withoutCatch.ElapsedMilliseconds))/iterations); 
     Console.WriteLine("Percent difference: {0}%", 100 * withCatch.ElapsedMilliseconds/withoutCatch.ElapsedMilliseconds); 
     Console.ReadKey(true); 
    } 

    static void CatchIt(Stopwatch withCatch, Stopwatch withoutCatch) 
    { 
     withCatch.Start(); 

     try 
     { 
      FinallyIt(withoutCatch); 
     } 
     catch 
     { 
     } 

     withCatch.Stop(); 
    } 

    static void FinallyIt(Stopwatch withoutCatch) 
    { 
     try 
     { 
      withoutCatch.Start(); 
      ThrowIt(withoutCatch); 
     } 
     finally 
     { 
      withoutCatch.Stop(); 
     } 
    } 

    private static void ThrowIt(Stopwatch withoutCatch) 
    { 
     throw new NotImplementedException(); 
    } 
+1

私は、例外が投げられなかったときにOPがパフォーマンスの影響について質問したと思います。 – Shimmy

7

実際には、数ヶ月前、私は、ASP.NETのWebアプリケーションを作成していた、と私は誤って非常に長いループでtry/catchブロックを包みました。ループで例外が発生していなかったとしても、処理に時間がかかりすぎていました。私が戻ってループにラップされたtry/catchを見たとき、私はそれを逆のやり方でやりました。ループをtry/catchブロックにラップしました。パフォーマンスが向上しました。あなた自身でこれを試すことができます:

int total; 

DateTime startTime = DateTime.Now; 

for(int i = 0; i < 20000; i++) 
{ 
try 
{ 
total += i; 
} 
catch 
{ 
// nothing to catch; 
} 
} 

Console.Write((DateTime.Now - startTime).ToString()); 

try/catchブロックを取り出してください。大きな違いが見えます!

+11

Hmmm。私はちょうど.Net 2.0でこれを試しました( 'Stopwatch'を使って)。 50000試行の20000回のループ反復は 'try-catch'なしで4184ms、' try-catch'で4363msです。それは信じられないほど小さな違いです。このような違いは、各反復が単純な加算操作を超えて実際に何かを実行している場合、さらに目立たなくなります。私はデバッグの有無にかかわらず同様の結果にヒットしました。 – Brian

4

実際に何が発生するのかを確認するには、以下のコードを実行します。単純な2次元配列をとり、範囲外のランダム座標を生成します。例外が一度だけ発生した場合は、もちろん気付かないでしょう。私の例は、これを数千回実行するときに何が意味するのかを強調するために行われ、例外をキャッチして単純なテストを実装すると、あなたを救うことができます。

 const int size = 1000; 
     const int maxSteps = 100000; 

     var randomSeed = (int)(DateTime.UtcNow - new DateTime(1970,1,1,0,0,0).ToLocalTime()).TotalMilliseconds; 
     var random = new Random(randomSeed); 
     var numOutOfRange = 0; 
     var grid = new int[size,size]; 
     var stopwatch = new Stopwatch(); 
     Console.WriteLine("---Start test with exception---"); 
     stopwatch.Reset(); 
     stopwatch.Start(); 
     for (int i = 0; i < maxSteps; i++) 
     { 
      int coord = random.Next(0, size * 2); 
      try 
      { 
       grid[coord, coord] = 1; 
      } 
      catch (IndexOutOfRangeException) 
      { 
       numOutOfRange++; 
      } 
     } 
     stopwatch.Stop(); 
     Console.WriteLine("Time used: " + stopwatch.ElapsedMilliseconds + "ms, Number out of range: " + numOutOfRange); 
     Console.WriteLine("---End test with exception---"); 

     random = new Random(randomSeed); 

     stopwatch.Reset(); 
     Console.WriteLine("---Start test without exception---"); 
     numOutOfRange = 0; 
     stopwatch.Start(); 
     for (int i = 0; i < maxSteps; i++) 
     { 
      int coord = random.Next(0, size * 2); 
      if (coord >= grid.GetLength(0) || coord >= grid.GetLength(1)) 
      { 
       numOutOfRange++; 
       continue; 
      } 
      grid[coord, coord] = 1; 
     } 
     stopwatch.Stop(); 
     Console.WriteLine("Time used: " + stopwatch.ElapsedMilliseconds + "ms, Number out of range: " + numOutOfRange); 
     Console.WriteLine("---End test without exception---"); 
     Console.ReadLine(); 

このコードの出力例:

---Start test with exception--- 
Time used: 3228ms, Number out of range: 49795 
---End test with exception--- 
---Start test without exception--- 
Time used: 3ms, Number out of range: 49795 
---End test without exception--- 
関連する問題