51

try catch ブロックは高価なので、できれば避けるべきだというアドバイスを聞いたことがあります。

私の質問は、具体的には .NET プラットフォームに関するものです: try ブロックはなぜ高価なのですか?

回答の要約:

この問題には明らかに 2 つの陣営があります。try ブロックは高価であると言う陣営と、「たぶんほんの少し」という陣営です。

try ブロックが高価であると言う人は、通常、コール スタックを巻き戻すことの「コストが高い」と述べています。個人的には、特に例外ハンドラがここに保存される方法について読んだ後では、その議論には納得できません。

Jon Skeet は、「たぶんほんの少し」の陣営に属しており、例外とパフォーマンスに関する 2 つの記事を書いています

私が非常に興味深いと思った記事が 1 つあります。その記事では、try ブロックの「その他の」パフォーマンスへの影響 (必ずしもメモリや CPU の消費とは限りません) について説明していました。Peter Ritchie は、try ブロック内のコードが最適化されていないことを発見したと述べています。彼の調査結果については、こちらで読むことができます。

最後に、CLR に例外を実装した人物によるこの問題に関するブログ エントリがあります。ここで Chris Brumme の記事をご覧ください。

4

11 に答える 11

20

高価なのはブロック自体ではなく、例外をキャッチすることすらありません。それ自体が高価です。例外を処理できるスタックフレームが見つかるまで、呼び出しスタックをアンワインドするランタイムです。例外のスローはかなり軽量ですが、ランタイムが適切な例外ハンドラーを見つけるために6つのスタックフレームをウォークアップする必要がある場合(つまり、6つのメソッド呼び出しの深さ)、場合によっては最終的にブロックを実行すると、かなりの時間が経過することがあります。

于 2008-10-02T21:12:47.797 に答える
14

通常、発生する可能性のある例外を適切に処理していないことを意味するため、try/catchブロックを回避しないでください。構造化例外処理(SEH)は、ランタイムがキャッチハンドラーを探してコールスタックをウォークし、そのハンドラーを実行し(複数存在する場合もあります)、finallyブロックを実行してから戻る必要があるため、実際に例外が発生した場合にのみコストがかかります。正しい場所にあるコードに戻って制御します。

例外は、プログラムロジックを制御するために使用されるのではなく、エラー状態を示すために使用されることを目的としています。

例外に関する最大の誤解の1つは、それらが「例外条件」に対するものであるということです。現実には、それらはエラー状態を伝達するためのものです。フレームワーク設計の観点からは、「例外的な条件」などはありません。条件が例外的であるかどうかは、使用状況によって異なりますが、再利用可能なライブラリは、それらがどのように使用されるかをほとんど知りません。たとえば、OutOfMemoryExceptionは、単純なデータ入力アプリケーションでは例外的な場合があります。独自のメモリ管理を行うアプリケーション(SQLサーバーなど)にとってはそれほど例外ではありません。言い換えれば、ある男性の例外的な状態は、別の男性の慢性的な状態です。 [http://blogs.msdn.com/kcwalina/archive/2008/07/17/ExceptionalError.aspx]

于 2008-10-02T21:18:09.763 に答える
6

try ブロックはまったく高価ではありません。例外がスローされない限り、コストはほとんどまたはまったく発生しません。また、例外がスローされた場合、それは例外的な状況であり、パフォーマンスはもう気にしません。プログラムがフォールオーバーするのに 0.001 秒かかるか、1.0 秒かかるかは問題ですか? いいえ、違います。重要なのは、報告された情報がどれだけ優れているかです。そうすれば、それを修正して再発を防ぐことができます。

于 2009-02-03T15:36:55.680 に答える
4

人々は、例外をスローすることによるパフォーマンス コストを過大評価していると思います。はい、パフォーマンス ヒットがありますが、比較的小さいものです。

次のテストを実行し、100 万の例外をスローしてキャッチしました。Intel Core 2 Duo、2.8 GHz で約 20 秒かかりました。これは、1 秒あたり約 50K の例外です。そのほんの一部でもスローしている場合は、アーキテクチャに問題があります。

これが私のコードです:

using System;
using System.Diagnostics;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < 1000000; i++)
            {
                try
                {
                    throw new Exception();
                }
                catch {}
            }
            Console.WriteLine(sw.ElapsedMilliseconds);
            Console.Read();
        }
    }
}
于 2008-10-03T01:38:49.310 に答える
3

try/catch ブロック内にコードをラップすると、コンパイラはより多くの IL を出力します。次のプログラムを見てください。

using System;
public class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("abc");
    }
}

コンパイラは次の IL を発行します。

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       13 (0xd)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "abc"
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000b:  nop
  IL_000c:  ret
} // end of method Program::Main

わずかに変更されたバージョンの場合:

using System;
public class Program
{
    static void Main(string[] args)
    {
        try { Console.WriteLine("abc"); }
        catch { }
    }
}

より多くを放出します:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       23 (0x17)
  .maxstack  1
  IL_0000:  nop
  .try
  {
    IL_0001:  nop
    IL_0002:  ldstr      "abc"
    IL_0007:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000c:  nop
    IL_000d:  nop
    IL_000e:  leave.s    IL_0015
  }  // end .try
  catch [mscorlib]System.Object 
  {
    IL_0010:  pop
    IL_0011:  nop
    IL_0012:  nop
    IL_0013:  leave.s    IL_0015
  }  // end handler
  IL_0015:  nop
  IL_0016:  ret
} // end of method Program::Main

これらすべての NOP およびその他の費用がかかります。

于 2008-10-04T11:29:42.180 に答える
3

IMO この議論全体は、「カウンターをインクリメントする必要があるため、うわー lops は高価です... もう使用しない」、または「うーん、オブジェクトの作成には時間がかかるので、作成するつもりはありません」と言っているようなものです。もう大量のオブジェクトです。」

要点は、おそらく何らかの理由でコードを追加することです。1 CPU サイクルであっても、コード行によってオーバーヘッドが発生しない場合、なぜそれが存在するのでしょうか? 無料のものはありません。

アプリケーションに追加するコード行と同様に、何かを行うために必要な場合にのみコードを配置することをお勧めします。例外をキャッチする必要がある場合は、それを行います...何かを格納するために文字列が必要な場合と同様に、新しい文字列を作成します。同様に、一度も使用されていない変数を宣言すると、それを作成するためにメモリと CPU サイクルが無駄になるため、削除する必要があります。try/catch と同じです。

つまり、何かを実行するためのコードがある場合、何かを実行すると何らかの方法で CPU やメモリが消費されると想定します。

于 2009-02-20T21:11:44.523 に答える
2

キャッチブロックほど心配する必要のあるブロックは試してはいけません。そして、ブロックの記述を避けたいということではありません。実際にはブロックを使用しないコードを可能な限り記述したいということです。

于 2008-10-02T21:23:43.510 に答える
1

それらが特に高価であるかどうかは疑わしい。多くの場合、それらは必要/必須です。

ただし、コールが戻るたびに例外を再スローするのではなく、必要な場合にのみ、適切な場所/レベルのネストで使用することを強くお勧めします。

アドバイスの主な理由は、if---elseがより良いアプローチである場合にtry-catchesを使用するべきではないと言うことだったと思います。

于 2008-10-02T21:12:50.323 に答える
1

これは私が心配することではありません。私はむしろ、トライの明快さと安全性に気を配っています...最終的に、それがどれほど「高価」であるかについて自分自身をブロックします。

私は個人的に 286 を使用していませんし、.NETや Java を使用している人もいません。進む。99.999999% のユーザーが正常に動作している基礎となるフレームワークではなく、ユーザーや他の開発者に影響を与える優れたコードを作成することを心配してください。

これはおそらくあまり役​​に立ちません。私は痛烈に言っているのではなく、視点を強調しているだけです。

于 2008-10-03T00:02:21.257 に答える
0

すべてのtryは、多くの情報 (スタック ポインター、CPU レジスタの値など) を記録する必要があります。これにより、例外がスローされた場合にスタックを巻き戻し、 tryブロックを渡したときの状態に戻すことができます。すべての試行で多くの情報を記録する必要があるだけでなく、例外がスローされたときに、多くの値を復元する必要があります。したがって、トライは非常に高くつき、スロー/キャッチも非常に高くつきます。

これは、例外を使用してはならないという意味ではありませんが、パフォーマンスが重要なコードでは、あまり多くの試行を使用したり、頻繁に例外をスローしたりしないでください。

于 2008-10-02T21:39:28.653 に答える
0

少しO/Tですが…

例外処理を必要としないというかなり優れた設計コンセプトがあります。これは単に、例外がスローされる前に、例外をスローする可能性のある条件についてオブジェクトを照会できる必要があることを意味します。

「write()」の前に「writable()」と言える、みたいな。

これはまともなアイデアであり、これを使用すると、Java でのチェック済み例外がばかげているように見えます。つまり、条件をチェックし、その直後に、同じ条件に対してまだ try/catch を書くことを余儀なくされているということです。

これは非常に優れたパターンですが、チェック例外はコンパイラによって強制できますが、これらのチェックは強制できません。また、すべてのライブラリがこのデザイン パターンを使用して作成されているわけではありません。これは、例外について考えるときに念頭に置いておくべきことです。

于 2008-10-02T21:26:46.217 に答える