Microsoft 직원과의 코드 검토 과정에서 우리는 try{}
블록 내부에서 큰 코드 섹션을 발견했습니다 . 그녀와 IT 담당자는 이것이 코드 성능에 영향을 줄 수 있다고 제안했습니다. 실제로 그들은 대부분의 코드가 try / catch 블록 외부에 있어야하며 중요한 섹션 만 확인해야한다고 제안했습니다. Microsoft 직원은 다가오는 백서에서 잘못된 try / catch 블록에 대해 경고한다고 덧붙였습니다.
주변을 둘러보고 최적화 에 영향을 줄 수는 있지만 변수가 범위간에 공유되는 경우에만 적용되는 것으로 보입니다.
코드의 유지 보수에 대해 묻거나 올바른 예외를 처리하지 않습니다 (문제의 코드는 리팩토링이 필요합니다). 또한 흐름 제어에 예외를 사용하는 것에 대해서는 언급하지 않으며 대부분의 경우 분명히 잘못되었습니다. 이것들은 중요한 문제이지만 (일부는 더 중요합니다) 여기서 초점은 아닙니다.
예외가 발생 하지 않을 때 try / catch 블록은 어떻게 성능에 영향을 줍 니까?
답변
확인해 봐.
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
답변
시도 / 캐치과 시도 / 캐치하지 않고 모든 통계를 본 후, 호기심보고 저를 강제 뒤의 두 경우에 생성되는 것을 볼 수 있습니다. 코드는 다음과 같습니다.
씨#:
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
씨#:
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의 전문가는 아니지만 로컬 예외 객체가 네 번째 줄에 생성 된 것을 볼 수 있습니다. 그 .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
함수가 반환됩니다.
예외가 발생하지 않으면 예외 객체 만 보유하고 점프 명령 을 보유하기 위해 로컬 변수를 작성하는 것이 오버 헤드라고 안전하게 가정 할 수 있습니다 .
답변
아니요. try / finally 블록이 사소한 최적화로 인해 실제로 측정에 영향을 줄 수있는 경우, 우선 .NET을 사용하지 않아야합니다.
답변
Rico Mariani의 성능 팁 : 예외 비용 : 던질 때와 던지지 않을 때
첫 번째 종류의 비용은 코드에서 예외 처리를하는 정적 비용입니다. 관리되는 예외는 실제로 여기에서 비교적 잘 수행되므로 정적 비용이 C ++에서보다 훨씬 저렴할 수 있습니다. 왜 이런거야? 정적 비용은 실제로 두 종류의 장소에서 발생합니다. 첫째, try / finally / catch / throw의 실제 사이트는 해당 구문에 대한 코드가 있습니다. 둘째, 관리되지 않는 코드에는 예외가 발생할 경우 파괴해야하는 모든 객체를 추적하는 것과 관련된 은폐 비용이 있습니다. 상당한 정리 논리가 있어야하며 몰래하는 부분은
드미트리 자 슬라브스키 :
Chris Brumme의 메모에 따르면 : 캐치가있을 때 JIT가 일부 최적화를 수행하지 않는다는 사실과 관련된 비용이 있습니다.
답변
이 예에서는 Ben M 과 구조가 다릅니다 . 내부 for
루프 내부의 오버 헤드가 확장 되어 두 경우를 잘 비교하지 못하게됩니다.
다음은 검사 할 전체 코드 (변수 선언 포함)가 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으로 늘리려 고했는데 Release에서 다시 실행하여 릴리스에서 약간의 차이점을 얻었습니다. 결과는 다음과 같습니다.
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를 사용하는 버전이 실제로 더 빠릅니다!
답변
나는 try..catch
단단한 루프에서 실제 영향을 테스트했으며 , 정상적인 상황에서 성능 문제가 되기에는 너무 작습니다.
루프가 거의 작동하지 않으면 (내 테스트에서 x++
), 예외 처리의 영향을 측정 할 수 있습니다. 예외 처리 루프는 실행하는 데 약 10 배가 더 걸렸습니다.
루프가 실제 작업을 수행하는 경우 (내 테스트에서 Int32.Parse 메서드라고 함) 예외 처리에 영향을 거의 미치지 않아 측정 할 수 없습니다. 루프 순서를 바꾸어 훨씬 더 큰 차이를 얻었습니다 …
답변
캐치 블록은 성능에 미미한 영향을 미치지 만 예외는 상당히 큰 조정이 될 수 있습니다. 동료가 혼란 스러웠을 수 있습니다.