[c#] C #에서 try / catch의 실제 오버 헤드는 무엇입니까?

그래서 저는 try / catch가 약간의 오버 헤드를 추가하므로 프로세스 흐름을 제어하는 ​​좋은 방법이 아니라는 것을 알고 있습니다.하지만이 오버 헤드는 어디에서 왔으며 실제 영향은 무엇입니까?



답변

나는 언어 구현의 전문가는 아니지만 (그러니 이것을 염두에 두십시오), 가장 큰 비용 중 하나는 스택을 풀고 스택 추적을 위해 저장하는 것이라고 생각합니다. 나는 이것이 예외가 던져 질 때만 발생한다고 생각합니다 (하지만 나는 모릅니다), 그렇다면 예외가 던져 질 때마다 이것은 적절한 크기의 숨겨진 비용이 될 것입니다. 다른 코드에서는 많은 일이 진행되고 있습니다.

예외적 인 동작에 대한 예외를 사용하는 한 문제가되지 않는다고 생각합니다 (프로그램을 통한 일반적인 예상 경로가 아님).


답변

여기서 확인해야 할 세 가지 사항 :

  • 첫째, 실제로 코드에서 try-catch 블록을 사용하는 경우 성능 저하가 거의 또는 전혀 없습니다. 응용 프로그램에 포함하지 않으려 고 할 때 고려해서는 안됩니다. 성능 저하는 예외가 발생할 때만 작용합니다.

  • 다른 사람들이 언급 한 스택 해제 작업 등에 추가로 예외가 발생하면 스택 추적과 같은 예외 클래스의 구성원을 채우기 위해 런타임 / 반사 관련 작업이 많이 발생한다는 사실을 알아야합니다. 객체 및 다양한 유형 멤버 등

  • 나는 이것이 throw;예외를 다시 던질 경우 일반적인 조언 이 예외를 다시 던지거나 새로운 것을 생성하는 것보다 단순한 경우에 모든 스택 정보가 다시 수집되는 이유 중 하나라고 생각합니다. 던지는 것은 모두 보존됩니다.


답변

예외가 발생하지 않을 때 try / catch / finally를 사용하는 오버 헤드 또는 프로세스 흐름을 제어하기 위해 예외를 사용하는 오버 헤드에 대해 질문하고 있습니까? 후자는 다이너마이트 막대를 사용하여 유아의 생일 촛불을 밝히는 것과 다소 유사하며 관련 오버 헤드는 다음 영역에 속합니다.

  • 일반적으로 캐시에 있지 않은 상주 데이터에 액세스하는 예외로 인해 추가 캐시 누락을 예상 할 수 있습니다.
  • 일반적으로 애플리케이션의 작업 세트에없는 비상주 코드 및 데이터에 액세스하는 예외로 인해 추가 페이지 오류가 발생할 수 있습니다.

    • 예를 들어 예외를 throw하려면 CLR이 현재 IP 및 모든 프레임의 반환 IP를 기반으로 finally 및 catch 블록의 위치를 ​​찾아야하며 예외가 처리 될 때까지 필터 블록을 추가해야합니다.
    • 메타 데이터 읽기 등 진단 목적으로 프레임을 생성하기위한 추가 건설 비용 및 이름 확인
    • 위의 두 항목 모두 일반적으로 “콜드”코드와 데이터에 액세스하므로 메모리 부족이 발생하면 하드 페이지 오류가 발생할 수 있습니다.

      • CLR은 지역성을 개선하기 위해 자주 사용되는 데이터와는 거리가 멀어 자주 사용되는 코드와 데이터를 넣으려고하므로 추위를 더워지게하므로 불리하게 작용합니다.
      • 하드 페이지 폴트의 비용은 다른 모든 것을 무시할 것입니다.
  • 일반적인 캐치 상황은 종종 깊기 때문에 위의 효과가 확대되는 경향이 있습니다 (페이지 폴트 가능성 증가).

비용의 실제 영향은 당시 코드에서 진행중인 다른 작업에 따라 크게 달라질 수 있습니다. Jon Skeet는 유용한 링크와 함께 여기에 좋은 요약을 제공 합니다. 나는 예외로 인해 성능이 크게 저하되는 지점에 도달하면 성능뿐만 아니라 예외를 사용하는 데 문제가 있다는 그의 진술에 동의하는 경향이 있습니다.


답변

내 경험상 가장 큰 오버 헤드는 실제로 예외를 던지고 처리하는 것입니다. 나는 누군가가 어떤 객체를 편집 할 권한이 있는지 확인하기 위해 다음과 유사한 코드를 사용하는 프로젝트에서 일한 적이 있습니다. 이 HasRight () 메서드는 프레젠테이션 레이어의 모든 곳에서 사용되었으며 종종 수백 개의 개체에 대해 호출되었습니다.

bool HasRight(string rightName, DomainObject obj) {
  try {
    CheckRight(rightName, obj);
    return true;
  }
  catch (Exception ex) {
    return false;
  }
}

void CheckRight(string rightName, DomainObject obj) {
  if (!_user.Rights.Contains(rightName))
    throw new Exception();
}

테스트 데이터베이스가 테스트 데이터로 가득 차면 새 양식을 여는 동안 매우 눈에 띄는 속도 저하가 발생합니다.

그래서 나는 그것을 다음과 같이 리팩토링했는데, 나중에 빠른 측정에 따르면 약 2 배 더 빠릅니다.

bool HasRight(string rightName, DomainObject obj) {
  return _user.Rights.Contains(rightName);
}

void CheckRight(string rightName, DomainObject obj) {
  if (!HasRight(rightName, obj))
    throw new Exception();
}

즉, 정상적인 프로세스 흐름에서 예외를 사용하는 것은 예외없이 유사한 프로세스 흐름을 사용하는 것보다 약 2 배 더 느립니다.


답변

일반적으로 받아 들여지는 이론과는 달리 try/ catch는 성능에 상당한 영향을 미칠 수 있으며 예외가 발생하는지 여부입니다!

  1. 일부 자동 최적화를 비활성화하고 (설계 상) 일부 경우 디버깅 지원 에서 예상 할 수있는 디버깅 코드를 삽입 합니다. 이 점에서 항상 저와 동의하지 않는 사람들이있을 것이지만, 언어는 그것을 필요로하고 분해는 그것을 보여 주므로 사전 정의에 따라 망상 적 입니다.
  2. 유지 관리에 부정적인 영향을 미칠 수 있습니다. 이것은 실제로 여기에서 가장 중요한 문제이지만 (거의 전적으로 그것에 초점을 맞춘) 마지막 답변이 삭제되었으므로 더 중요한 문제가 아닌 덜 중요한 문제 (마이크로 최적화)에 초점을 맞추려고 노력할 것입니다 ( 거시적 최적화).

전자는 지난 몇 년 동안 마이크로 소프트의 MVP로 블로그 게시물의 몇 가지에 덮여, 그리고 당신이 쉽게 찾을 수있는 신뢰 아직 StackOverflow의이 걱정되어 훨씬 에 대한 내용 나는대로 그들 중 일부에 대한 링크를 제공 할 수 있습니다 있도록 필러 증거 :

  • try/ catch/finally ( 및 파트 2 ) 의 성능 영향 , Peter Ritchie가try/catch/finally비활성화하는 최적화를 탐색합니다 (표준의 인용문을 사용하여 더 자세히 설명하겠습니다).
  • 성능 프로파일 링 ParseTryParseConvertTo 이안 허프으로는 “예외 처리가 매우 느립니다”과 내공으로이 점을 보여줍니다 있음을 노골적으로 주장Int.Parse하고Int.TryParse주장하는 사람이 서로 …에 대하여 그TryParse용도가try/catch무대 뒤에서,이 되거한다고!

/ 사용 여부와 상관없이 분해 된 코드의 차이점을 보여주는 이 답변 도 있습니다 .trycatch

코드 생성에서 노골적으로 관찰 할 수있는 오버 헤드 가 있다는 것이 너무 분명해 보이며 , 그 오버 헤드는 마이크로 소프트가 중요하게 여기는 사람들이 인정하는 것 같습니다! 그러나 나는 인터넷을 반복하고 있습니다

예, 한 줄의 간단한 코드에 대해 수십 개의 추가 MSIL 명령이 있으며 비활성화 된 최적화도 다루지 않으므로 기술적으로는 마이크로 최적화입니다.


프로그래머의 생산성 (매크로 최적화)에 초점을 맞춰 삭제 된 답변을 몇 년 전에 게시했습니다.

여기저기서 CPU 시간의 몇 나노초를 절약하지 않아도 인간이 수동으로 최적화하는 데 많은 시간이 누적 된 것을 보충 할 수 있기 때문에 이는 불행한 일입니다. 상사가 더 많은 비용을 지불하는 것은 무엇입니까? 한 시간 중 한 시간입니까, 아니면 컴퓨터가 실행중인 한 시간입니까? 어떤 시점에서 우리는 플러그를 뽑고 더 빠른 컴퓨터를 구입할 때임을 인정 합니까?

분명히 우리는 코드뿐만 아니라 우선 순위를 최적화 해야합니다 ! 마지막 답변에서 두 코드 스 니펫의 차이점을 그렸습니다.

try/ 사용 catch:

int x;
try {
    x = int.Parse("1234");
}
catch {
    return;
}
// some more code here...

try/ 사용하지 않음 catch:

int x;
if (int.TryParse("1234", out x) == false) {
    return;
}
// some more code here

프로파일 링 / 최적화 (위에서 다루었 음)가 아니라면 시간을 낭비 할 가능성이 더 큰 유지 보수 개발자의 관점에서 고려하십시오. try/ catch문제 가 아니었다면 필요하지 않을 수도 있습니다. 소스 코드 … 그중 하나는 4 줄의 상용구 쓰레기를 추가로 가지고 있습니다!

점점 더 많은 필드가 클래스에 도입됨에 따라이 모든 보일러 플레이트 가비지 (소스 및 디스 어셈블 된 코드 모두)가 합리적인 수준을 훨씬 넘어 누적됩니다. 필드 당 4 개의 추가 줄, 항상 같은 줄입니다 … 우리는 반복하지 않도록 배웠습니까? 나는 우리가 숨길 수있는 가정 try/ catch우리뿐만 아니라 단지 예외 (즉, 사용하지 있습니다 … 일부 홈 양조 추상화 뒤,하지만 Int.TryParse).

이것은 복잡한 예가 아닙니다. try/ 에서 새 클래스를 인스턴스화하려는 시도를 보았습니다 catch. 생성자 내부의 모든 코드가 컴파일러에 의해 자동으로 적용되는 특정 최적화에서 실격 될 수 있음을 고려하십시오. 컴파일러가 지시 된대로 정확히 수행하는 것과 달리 컴파일러가 느리다는 이론을 일으키는 더 좋은 방법은 무엇입니까 ?

생성자에 의해 예외가 발생하고 그 결과로 일부 버그가 트리거된다고 가정하면 열악한 유지 관리 개발자는이를 추적해야합니다. 즉,의 스파게티 코드와는 달리으로, 같은 쉬운 일이되지 않을 수도 있습니다 고토 악몽, try/ catch에 혼잡이 발생할 수 있습니다 세 가지 차원 이 다른 클래스와 메소드를 같은 방법의 단지 다른없는 부분으로 스택을 이동하지만, 수 있기 때문에, , 모두는 유지 보수 개발자에 의해 관찰 될 것이다 어려운 방법 ! 그러나 우리는 “goto는 위험하다”라고 들었습니다, heh!

마지막으로 내가 언급했듯이 try/ 최적화를 비활성화하도록 설계된catch 이점이 있습니다 ! 당신이 원한다면 그것은 디버깅 보조 도구입니다 ! 그것이 그것이 설계된 이유이고 그것이 사용되어야하는 것입니다 …

그것도 긍정적 인 점이라고 생각합니다. 멀티 스레드 애플리케이션을위한 안전하고 건전한 메시지 전달 알고리즘을 손상시킬 수있는 최적화를 비활성화하고 가능한 경쟁 조건을 포착하는 데 사용할 수 있습니다.;) 이것이 try / catch를 사용하는 유일한 시나리오입니다. 그것조차 대안이 있습니다.


무엇 최적화는 수행 try, catchfinally비활성화?

AKA

어떻게하다 try, catchfinally보조 디버깅 유용?

쓰기 장벽입니다. 이것은 표준에서 비롯됩니다.

12.3.3.13 Try-catch 문

다음 형식의 문장 stmt 의 경우 :

try try-block
catch ( ... ) catch-block-1
...
catch ( ... ) catch-block-n
  • 의 명확한 할당 상태 V 의 시작 부분에 시도-블록 의 명확한 할당 상태와 동일한 V 의 시작 부분에 STMT .
  • 의 명확한 지정 상태 V 의 시작 캐치 블록 I (임의위한 )의 확정적 할당 상태와 동일 V 의 시작 STMT .
  • 의 명확한 할당 상태 V 의 최종 지점에서 STMT은 (그리고 경우에만) 경우 확실히 할당 V가 확실히의 엔드 포인트에 할당 시도 블록 마다 캐치 블록-I (매에 내가 1에 N ).

즉, 각 try명령문 의 시작 부분에서 :

  • try명령문 에 들어가기 전에 보이는 객체에 대한 모든 할당은 완료되어야하며, 시작시 스레드 잠금이 필요하므로 경쟁 조건을 디버깅하는 데 유용합니다!
  • 컴파일러는 다음을 허용하지 않습니다.
    • try명령문 이전에 확실히 할당 된 사용하지 않는 변수 할당 제거
    • 내부 할당을 재구성하거나 통합합니다 (예 : 아직 수행하지 않은 경우 첫 번째 링크 참조).
    • 이 장벽을 넘어 할당을 호이스트하거나, 나중에 사용되지 않을 것으로 알고있는 변수에 할당을 지연하거나 (아마도) 다른 최적화를 가능하게하기 위해 나중에 할당을 선제 적으로 앞으로 이동합니다.

비슷한 이야기가 각 catch진술에 적용됩니다. 당신 내 가정 try(의이 말을하자 문 (또는 생성자하거나 호출 기능 등) 당신이 그렇지 않으면 무의미한 변수에 할당 garbage=42;), 컴파일러는 그 문은 프로그램의 관찰 행동에 얼마나 관련이없는 상관없이 제거 할 수 . 블록에 들어가기 전에 할당이 완료 되어야합니다 catch.

그 가치에 finally대해 비슷하게 비하하는 이야기를 들려 줍니다 .

12.3.3.14 Try-finally 문

다음 형식의 trystmt 의 경우 :

try try-block
finally finally-block

try-block 시작 부분에있는 v 의 명확한 할당 상태 는 stmt 시작 부분에있는 v 의 명확한 할당 상태와 동일합니다 .
finally-block 시작 부분에있는 v 의 명확한 할당 상태 는 stmt 시작 부분에있는 v 의 명확한 할당 상태와 동일합니다 .
stmt 의 끝점에서 v 의 명확한 할당 상태는 다음 중 하나 인 경우에만 확실히 할당됩니다. o vtry-block 의 끝점에서 확실히 할당됩니다.
o v

명확히의 종점에 할당 된 마지막 블록
(예로서, 제어 흐름 전달한다면 고토 문)이 그 안에 시작된다 시도 블록 및 외측 단부 시도 블록은 , 다음 V가 아니라 확실히 그것에 할당 된 것으로 간주 vfinally-block 의 끝점에 확실히 할당 된 경우 제어 흐름 전송 . (이 경우에만 해당되는 것은 아닙니다 .이 제어 흐름 전송에서 다른 이유로 v 가 확실히 할당 된 경우 에도 확실히 할당 된 것으로 간주됩니다.)

12.3.3.15 Try-catch-finally 문

trycatchfinally 서술문에 대한 명확한 할당 분석 :

try try-block
catch ( ... ) catch-block-1
...
catch ( ... ) catch-block-n
finally finally-block

명령문이 tryfinally 문이 trycatch 문인 것처럼 수행됩니다 .

try {
    try
    try-block
    catch ( ... ) catch-block-1
    ...
    catch ( ... ) catch-block-n
}
finally finally-block


답변

자주 호출되는 메서드 내부에 있는지는 말할 것도없이 애플리케이션의 전반적인 동작에 영향을 미칠 수 있습니다.
예를 들어, Int32.Parse를 사용하면 다른 방법으로 쉽게 잡을 수있는 항목에 대한 예외가 발생하므로 대부분의 경우 나쁜 습관으로 간주합니다.

따라서 여기에 쓰여진 모든 것을 결론
짓기 위해 : 1) try..catch 블록을 사용하여 예상치 못한 오류를 포착합니다. 성능 저하가 거의 없습니다.
2) 피할 수 있다면 예외 오류에 예외를 사용하지 마십시오.


답변

그 당시 많은 사람들이 이것에 대해 물어 보았 기 때문에 얼마 전에 이것에 대한 기사를 썼습니다. http://www.blackwasp.co.uk/SpeedTestTryCatch.aspx 에서이 코드와 테스트 코드를 찾을 수 있습니다 .

결론은 try / catch 블록에 대해 약간의 오버 헤드가 있지만 너무 작아서 무시해야한다는 것입니다. 그러나 수백만 번 실행되는 루프에서 try / catch 블록을 실행하는 경우 가능하면 블록을 루프 외부로 이동하는 것을 고려할 수 있습니다.

try / catch 블록의 핵심 성능 문제는 실제로 예외를 포착 할 때입니다. 이로 인해 애플리케이션이 눈에 띄게 지연 될 수 있습니다. 물론 일이 잘못되면 대부분의 개발자 (및 많은 사용자)는 일시 중지를 곧 일어날 예외로 인식합니다! 여기서 핵심은 정상적인 작업에 예외 처리를 사용하지 않는 것입니다. 이름에서 알 수 있듯이, 그들은 예외적이며 던져지지 않도록 할 수있는 모든 것을해야합니다. 올바르게 작동하는 프로그램의 예상 흐름의 일부로 사용해서는 안됩니다.