例外がスローされない場合、try / catchブロックはパフォーマンスを低下させますか?

286
Kobi 2009-08-21 09:50.

マイクロソフトの従業員とのコードレビュー中に、try{}ブロック内のコードの大部分に出くわしました。彼女とIT担当者は、これがコードのパフォーマンスに影響を与える可能性があることを示唆しました。実際、彼らは、ほとんどのコードをtry / catchブロックの外側に配置し、重要なセクションのみをチェックする必要があることを示唆しました。Microsoftの従業員は、次のホワイトペーパーが誤ったtry / catchブロックに対して警告していると付け加えました。

調べてみると、最適化に影響を与える可能性がありますが、変数がスコープ間で共有されている場合にのみ適用されるようです。

私はコードの保守性について質問したり、適切な例外を処理したりすることさえしていません(問題のコードは間違いなくリファクタリングが必要です)。また、フロー制御に例外を使用することについても言及していません。これはほとんどの場合明らかに間違っています。これらは重要な問題です(いくつかはより重要です)が、ここでは焦点を当てていません。

例外がスローされない場合、try / catchブロックはパフォーマンスにどのように影響しますか?

12 answers

216
Ben M 2009-08-21 10:02.

確認してください。

static public void Main(string[] args)
{
    Stopwatch w = new Stopwatch();
    double d = 0;

    w.Start();

    for (int i = 0; i < 10000000; i++)
    {
        try
        {
            d = Math.Sin(1);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }

    w.Stop();
    Console.WriteLine(w.Elapsed);
    w.Reset();
    w.Start();

    for (int i = 0; i < 10000000; i++)
    {
        d = Math.Sin(1);
    }

    w.Stop();
    Console.WriteLine(w.Elapsed);
}

出力:

00:00:00.4269033  // with try/catch
00:00:00.4260383  // without.

ミリ秒単位:

449
416

新しいコード:

for (int j = 0; j < 10; j++)
{
    Stopwatch w = new Stopwatch();
    double d = 0;
    w.Start();

    for (int i = 0; i < 10000000; i++)
    {
        try
        {
            d = Math.Sin(d);
        }

        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }

        finally
        {
            d = Math.Sin(d);
        }
    }

    w.Stop();
    Console.Write("   try/catch/finally: ");
    Console.WriteLine(w.ElapsedMilliseconds);
    w.Reset();
    d = 0;
    w.Start();

    for (int i = 0; i < 10000000; i++)
    {
        d = Math.Sin(d);
        d = Math.Sin(d);
    }

    w.Stop();
    Console.Write("No try/catch/finally: ");
    Console.WriteLine(w.ElapsedMilliseconds);
    Console.WriteLine();
}

新しい結果:

   try/catch/finally: 382
No try/catch/finally: 332

   try/catch/finally: 375
No try/catch/finally: 332

   try/catch/finally: 376
No try/catch/finally: 333

   try/catch/finally: 375
No try/catch/finally: 330

   try/catch/finally: 373
No try/catch/finally: 329

   try/catch/finally: 373
No try/catch/finally: 330

   try/catch/finally: 373
No try/catch/finally: 352

   try/catch/finally: 374
No try/catch/finally: 331

   try/catch/finally: 380
No try/catch/finally: 329

   try/catch/finally: 374
No try/catch/finally: 334
109
TheVillageIdiot 2009-08-30 11:04.

try / catchありとtry / catchなしのすべての統計を確認した後、好奇心から、両方のケースで何が生成されるかを確認するために後ろを振り返ることを余儀なくされました。コードは次のとおりです。

C#:

private static void TestWithoutTryCatch(){
    Console.WriteLine("SIN(1) = {0} - No Try/Catch", Math.Sin(1)); 
}

MSIL:

.method private hidebysig static void  TestWithoutTryCatch() cil managed
{
  // Code size       32 (0x20)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "SIN(1) = {0} - No Try/Catch"
  IL_0006:  ldc.r8     1.
  IL_000f:  call       float64 [mscorlib]System.Math::Sin(float64)
  IL_0014:  box        [mscorlib]System.Double
  IL_0019:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                object)
  IL_001e:  nop
  IL_001f:  ret
} // end of method Program::TestWithoutTryCatch

C#:

private static void TestWithTryCatch(){
    try{
        Console.WriteLine("SIN(1) = {0}", Math.Sin(1)); 
    }
    catch (Exception ex){
        Console.WriteLine(ex);
    }
}

MSIL:

.method private hidebysig static void  TestWithTryCatch() cil managed
{
  // Code size       49 (0x31)
  .maxstack  2
  .locals init ([0] class [mscorlib]System.Exception ex)
  IL_0000:  nop
  .try
  {
    IL_0001:  nop
    IL_0002:  ldstr      "SIN(1) = {0}"
    IL_0007:  ldc.r8     1.
    IL_0010:  call       float64 [mscorlib]System.Math::Sin(float64)
    IL_0015:  box        [mscorlib]System.Double
    IL_001a:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                  object)
    IL_001f:  nop
    IL_0020:  nop
    IL_0021:  leave.s    IL_002f //JUMP IF NO EXCEPTION
  }  // end .try
  catch [mscorlib]System.Exception 
  {
    IL_0023:  stloc.0
    IL_0024:  nop
    IL_0025:  ldloc.0
    IL_0026:  call       void [mscorlib]System.Console::WriteLine(object)
    IL_002b:  nop
    IL_002c:  nop
    IL_002d:  leave.s    IL_002f
  }  // end handler
  IL_002f:  nop
  IL_0030:  ret
} // end of method Program::TestWithTryCatch

私はILの専門家ではありませんが、4行目にローカル例外オブジェクトが作成され、.locals init ([0] class [mscorlib]System.Exception ex)その後17行目までtry / catchを使用しないメソッドの場合とほとんど同じであることがわかりますIL_0021: leave.s IL_002f。例外が発生した場合、コントロールは行IL_0025: ldloc.0にジャンプしIL_002d: leave.s IL_002fます。それ以外の場合は、ラベルにジャンプし、関数が戻ります。

例外が発生しない場合は、例外オブジェクトとジャンプ命令 のみ を保持するローカル変数を作成するオーバーヘッドであると安全に推測でき ます。

66
John Kugelman 2009-08-21 10:00.

いいえ。try/ finallyブロックが実際にプログラムに測定可能な影響を与えることを妨げる些細な最適化である場合は、そもそも.NETを使用すべきではありません。

35
arul 2009-08-21 10:00.

.NET例外モデルの非常に包括的な説明。

リコ・マリアーニのパフォーマンスのヒント:例外コスト:投げるときと投げないとき

最初の種類のコストは、コードで例外処理を行うための静的コストです。ここでは、管理された例外は実際には比較的うまく機能します。つまり、静的コストはC ++で言うよりもはるかに低くなる可能性があります。どうしてこれなの?静的コストは、実際には2種類の場所で発生します。1つは、これらの構造のコードがあるtry / finally / catch / throwの実際のサイトです。次に、管理されていないコードでは、例外がスローされた場合に破棄する必要があるすべてのオブジェクトを追跡することに関連するステルスコストがあります。存在しなければならないクリーンアップロジックはかなりの量あり、卑劣な部分は、それ自体がスローまたはキャッチしない、あるいは例外を明白に使用しないコードでさえ、それ自体の後でクリーンアップする方法を知る負担を負うということです。

Dmitriy Zaslavskiy:

Chris Brummeのメモによると、キャッチが存在する場合、一部の最適化がJITによって実行されていないという事実に関連するコストもあります。

27
awe 2009-08-25 23:28.

例ではBenMとは構造が異なります。これは、内側のforループ内でオーバーヘッドが拡張されるため、2つのケースをうまく比較できなくなります。

以下は、チェックするコード全体(変数宣言を含む)がTry / Catchブロック内にある場合の比較のためにより正確です。

        for (int j = 0; j < 10; j++)
        {
            Stopwatch w = new Stopwatch();
            w.Start();
            try { 
                double d1 = 0; 
                for (int i = 0; i < 10000000; i++) { 
                    d1 = Math.Sin(d1);
                    d1 = Math.Sin(d1); 
                } 
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString()); 
            }
            finally { 
                //d1 = Math.Sin(d1); 
            }
            w.Stop(); 
            Console.Write("   try/catch/finally: "); 
            Console.WriteLine(w.ElapsedMilliseconds); 
            w.Reset(); 
            w.Start(); 
            double d2 = 0; 
            for (int i = 0; i < 10000000; i++) { 
                d2 = Math.Sin(d2);
                d2 = Math.Sin(d2); 
            } 
            w.Stop(); 
            Console.Write("No try/catch/finally: "); 
            Console.WriteLine(w.ElapsedMilliseconds); 
            Console.WriteLine();
        }

Ben Mから元のテストコードを実行したとき、デバッグ構成とリリース構成の両方に違いがあることに気付きました。

このバージョンでは、デバッグバージョンの違い(実際には他のバージョンよりも多い)に気づきましたが、リリースバージョンでは違いはありませんでした。

結論
これらのテストに基づいて、Try / Catchパフォーマンスにわずかな影響を与えると言えると思います。

編集:
ループ値を10000000から1000000000に増やしようとしましたが、リリースで再度実行してリリースにいくつかの違いを取得しました。結果は次のとおりです。

   try/catch/finally: 509
No try/catch/finally: 486

   try/catch/finally: 479
No try/catch/finally: 511

   try/catch/finally: 475
No try/catch/finally: 477

   try/catch/finally: 477
No try/catch/finally: 475

   try/catch/finally: 475
No try/catch/finally: 476

   try/catch/finally: 477
No try/catch/finally: 474

   try/catch/finally: 475
No try/catch/finally: 475

   try/catch/finally: 476
No try/catch/finally: 476

   try/catch/finally: 475
No try/catch/finally: 476

   try/catch/finally: 475
No try/catch/finally: 474

結果が結果的でないことがわかります。場合によっては、Try / Catchを使用したバージョンの方が実際に高速です。

15
Guffa 2009-08-21 10:14.

try..catchタイトループでの実際の影響をテストしましたが、それ自体では小さすぎて、通常の状況でのパフォーマンスの問題にはなりません。

ループがほとんど機能しない場合(私のテストではx++)、例外処理の影響を測定できます。例外処理を伴うループの実行には、約10倍の時間がかかりました。

ループが実際の作業を行う場合(私のテストではInt32.Parseメソッドを呼び出しました)、例外処理の影響は小さすぎて測定できません。ループの順序を入れ替えることで、はるかに大きな違いが得られました...

12
RHicke 2009-08-21 10:29.

キャッチブロックがパフォーマンスに与える影響はごくわずかですが、例外スローはかなり大きくなる可能性があります。これはおそらく同僚が混乱した場所です。

9
Ted Oddman 2019-11-28 21:17.

予防は取り扱いよりも優れている」とはいえ、パフォーマンスと効率の観点から、事前検証よりもトライキャッチを選択することができました。以下のコードを検討してください。

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 1; i < int.MaxValue; i++)
{
    if (i != 0)
    {
        int k = 10 / i;
    }
}
stopwatch.Stop();
Console.WriteLine($"With Checking: {stopwatch.ElapsedMilliseconds}"); stopwatch.Reset(); stopwatch.Start(); for (int i = 1; i < int.MaxValue; i++) { try { int k = 10 / i; } catch (Exception) { } } stopwatch.Stop(); Console.WriteLine($"With Exception: {stopwatch.ElapsedMilliseconds}");

結果は次のとおりです。

With Checking:  20367
With Exception: 13998
8
Isaac 2009-08-30 09:51.

try / catchはパフォーマンスに影響を与えます。

しかし、それは大きな影響ではありません。try / catchの複雑さは、ループに配置される場合を除いて、単純な割り当てと同じように、一般にO(1)です。したがって、それらを賢く使用する必要があります。

これは、try / catchのパフォーマンスに関するリファレンスです(ただし、その複雑さについては説明していませんが、暗黙的に示されています)。Throw FewerExceptionsセクションをご覧ください

6
supercat 2014-08-29 06:11.

理論的には、try / catchブロックは、例外が実際に発生しない限り、コードの動作に影響を与えません。ただし、try / catchブロックの存在が大きな影響を与える可能性があるまれな状況や、影響が目立つ可能性がある、まれではあるがほとんどわかりにくい状況がいくつかあります。この理由は、次のような特定のコードです。

Action q;
double thing1()
  { double total; for (int i=0; i<1000000; i++) total+=1.0/i; return total;}
double thing2()
  { q=null; return 1.0;}
...
x=thing1();     // statement1
x=thing2(x);    // statement2
doSomething(x); // statement3

コンパイラは、statement2がstatement3の前に実行されることが保証されているという事実に基づいて、statement1を最適化できる場合があります。コンパイラがthing1に副作用がなく、thing2が実際にxを使用していないことを認識できる場合は、thing1を完全に省略しても問題ありません。[この場合のように] thing1が高価な場合、それは主要な最適化になる可能性がありますが、thing1が高価な場合も、コンパイラーが最適化する可能性が最も低い場合です。コードが変更されたとします。

x=thing1();      // statement1
try
{ x=thing2(x); } // statement2
catch { q(); }
doSomething(x);  // statement3

これで、statement2を実行せずにstatement3を実行できる一連のイベントが存在します。のコードにthing2例外をスローできるものがない場合でも、別のスレッドがを使用して、クリアされたことをInterlocked.CompareExchange通知してqに設定しThread.ResetAbortThread.Abort()beforeステートメント2がその値をに書き込む前に実行する可能性がありますx。次に、catchThread.ResetAbort()[デリゲートを介してq]実行され、statement3で実行を続行できるようになります。もちろん、このような一連のイベントは非常に起こりそうにありませんが、このような起こりそうもないイベントが発生した場合でも、仕様に従って機能するコードを生成するにはコンパイラーが必要です。

一般に、コンパイラーは、複雑なコードよりも単純なコードを除外する機会に気付く可能性がはるかに高いため、例外がスローされない場合、try / catchがパフォーマンスに大きな影響を与えることはめったにありません。それでも、try / catchブロックの存在により最適化が妨げられる場合がありますが、try / catchの場合は、コードをより高速に実行できます。

4
Ira Baxter 2009-08-31 22:20.

try / catchブロックがどのように機能するか、および例外が発生しない場合にオーバーヘッドが高い実装とオーバーヘッドがゼロの実装については、try / catchの実装に関する説明を参照してください。特に、Windows 32ビットの実装はオーバーヘッドが高いと思いますが、64ビットの実装はそうではありません。

2
l33t 2020-10-07 05:42.

はい、try/catchパフォーマンスを「損なう」でしょう(すべてが相対的です)。無駄なCPUサイクルに関してはそれほど多くはありませんが、考慮すべき他の重要な側面があります。

  • コードサイズ
  • メソッドのインライン化

基準

まず、いくつかの洗練されたツール(BenchmarkDotNetなど)を使用して速度を確認しましょう。としてコンパイルされRelease (AnyCPU)x64マシン上で実行されます。テストでは実際にそれNoTryCatch()が非常に速く、少し速いことがわかりますが、違いはないと思います。

|            Method |   N |     Mean |     Error |    StdDev |
|------------------ |---- |---------:|----------:|----------:|
|        NoTryCatch | 0.5 | 3.770 ns | 0.0492 ns | 0.0411 ns |
|      WithTryCatch | 0.5 | 4.060 ns | 0.0410 ns | 0.0384 ns |
| WithTryCatchThrow | 0.5 | 3.924 ns | 0.0994 ns | 0.0881 ns |

分析

いくつかの追加のメモ。

|            Method | Code size | Inlineable |
|------------------ |---------- |-----------:|
|        NoTryCatch |        12 |        yes |
|      WithTryCatch |        18 |          ? |
| WithTryCatchThrow |        18 |         no |

コードサイズNoTryCatch()はコードで12バイトになりますが、try / catchはさらに6バイトを追加します。また、を書くときはいつでも、try/catchおそらく1つ以上のthrow new Exception("Message", ex)ステートメントがあり、コードをさらに「肥大化」させます。

ただし、ここで最も重要なことは、コードのインライン化です。.NET単なる存在throwキーワードは、メソッドは、コンパイラによってインライン化されることはありませんことを意味し(遅くコードを暗示するだけでなく、より少ないフットプリント)。私は最近この事実を徹底的にテストしたので、それはまだで有効であるようです.NET Coretry/catch同じルールに従うかどうかわからない。TODO: Verify!

完全なテストコード

using System;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace TryCatchPerformance
{
    public class TryCatch
    {
        [Params(0.5)]
        public double N { get; set; }

        [Benchmark]
        public void NoTryCatch() => Math.Sin(N);

        [Benchmark]
        public void WithTryCatch()
        {
            try
            {
                Math.Sin(N);
            }
            catch
            {
            }
        }

        [Benchmark]
        public void WithTryCatchThrow()
        {
            try
            {
                Math.Sin(N);
            }
            catch (Exception ex)
            {
                throw;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<TryCatch>();
        }
    }
}

Related questions

MORE COOL STUFF

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは、夫に会ったとき、典型的な交際のアドバイスに逆らいました。

マイケルシーンが非営利の俳優である理由

マイケルシーンが非営利の俳優である理由

マイケルシーンは非営利の俳優ですが、それは正確にはどういう意味ですか?

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

特徴的なスターのコリン・エッグレスフィールドは、RomaDrama Liveでのスリル満点のファンとの出会いについて料理しました!加えて、大会での彼のINSPIREプログラム。

「たどりつけば」をオンラインでストリーミングできない理由

「たどりつけば」をオンラインでストリーミングできない理由

ノーザンエクスポージャーが90年代の最も人気のある番組の1つになった理由を確認するには、Blu-rayまたはDVDプレーヤーをほこりで払う必要があります。

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖は、世界で2番目に大きいボイリング湖です。そこにたどり着くまでのトレッキングは大変で長いですが、努力する価値は十分にあります。

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

サロンからのヘアトリミングや個人的な寄付は、油流出を吸収して環境を保護するのに役立つマットとして再利用できます。

ホワイトハウスの最も記憶に残る結婚式を見てください

ホワイトハウスの最も記憶に残る結婚式を見てください

過去200年以上の間にホワイトハウスで結婚したのはほんの数人です。彼らは誰でしたか、そしてそこで結婚式を獲得するために何が必要ですか?

驚くほど素晴らしいDropMixミュージックミキシングカードゲームは30ドルで驚くべき取引です

驚くほど素晴らしいDropMixミュージックミキシングカードゲームは30ドルで驚くべき取引です

DropMixはNFC対応のカードゲームで、基本的にはリミックスアーティストになります。現在、Amazonでは$ 30まで下がっており、これまでで最高の価格に匹敵します。ロックバンドで有名なHarmonixによって開発されたDropMixは、おそらく少し野心的すぎるように思われます。結局のところ、ほとんどの人は素晴らしいリズムを持っていませんが、ゲームは驚くほどうまく実行されます。

メアリーJ.ブライジがついにハリウッドウォークオブフェイムスターを獲得

メアリーJ.ブライジがついにハリウッドウォークオブフェイムスターを獲得

写真:APメアリーJ.ブライジは、間もなくハリウッドウォークオブフェイムのスターを獲得します。これは、メアリーJよりもハリウッドウォークオブフェイムのほうが正直なところ恩恵です。

MeltdownとSpectreの脆弱性についてこれまでに知っていることはすべて、簡単な方法で説明されています

MeltdownとSpectreの脆弱性についてこれまでに知っていることはすべて、簡単な方法で説明されています

画像:グラズ工科大学/ NataschaEiblがデザインしたロゴ。MeltdownとSpectreは、攻撃者がシステムメモリに保存されているあらゆる種類の情報にアクセスできるようにする2つの脆弱性に付けられた名前です。

彼のニューヨークの家から追い出されようとしている97歳の第二次世界大戦の獣医。メリーエフィングクリスマス

彼のニューヨークの家から追い出されようとしている97歳の第二次世界大戦の獣医。メリーエフィングクリスマス

日本人に襲われたときに真珠湾にいた97歳の第二次世界大戦のベテランが、ニューヨークのブルックリンから追い出されています。

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya shared a sweet photo in honor of boyfriend Tom Holland's 26th birthday Wednesday

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

シーレン「Ms.JuicyBaby」ピアソンは、先月脳卒中で入院した後、「もう一度たくさんのことをする方法を学ばなければならない」ため、言語療法を受けていることを明らかにしました。

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

オスカー受賞者の世紀半ばの家には、3つのベッドルーム、2つのバス、オーシャンフロントの景色があります。

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、生後4か月の娘、モナコに母乳育児をしていると語った。

投資ノート:Bioscout AU$300万シード

投資ノート:Bioscout AU$300万シード

Bioscoutは、農家を運転席に置くという使命を負っています。Artesian(GrainInnovate)やUniseedと並んで、最新のシードラウンドでチームを支援できることをうれしく思います。問題真菌症による重大な作物の損失は、農民にとって試練であることが証明されています。

リトルマーケットリサーチ1| 2022年のクイックグリンプス遠隔医療市場

リトルマーケットリサーチ1| 2022年のクイックグリンプス遠隔医療市場

遠隔医療は、パンデミック後の時代では新しいものではなく、時代遅れの分野でもありません。しかし、業界を詳しく見ると、需要と供給の強力な持続可能性と、米国で絶え間ない革命となる強力な潜在的成長曲線を示しています。

スタートアップ資金調達環境:タイのスタートアップエコシステムの次は何ですか?

スタートアップ資金調達環境:タイのスタートアップエコシステムの次は何ですか?

2021年は、世界的なベンチャーキャピタル(VC)の資金調達にとって記録的な年でした。DealStreetAsiaによると、東南アジアも例外ではなく、この地域では年間で記録的な25の新しいユニコーンが採掘されました。

ムーアの法則を超えて

ムーアの法則を超えて

計算に対する私たちの欲求とムーアの法則が提供できるものとの間には、指数関数的に増大するギャップがあります。私たちの文明は計算に基づいています—建築と想像力の現在の限界を超える技術を見つけなければなりません。

Language