문제

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

도움이 되었습니까?

해결책

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

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

다른 팁

여기서 확인해야 할 세 가지 사항은 다음과 같습니다.

  • 첫째, 실제로 코드에 try-catch 블록이 있으면 성능 저하가 거의 또는 전혀 없습니다.애플리케이션에 이러한 항목이 포함되지 않도록 하려는 경우에는 이 점을 고려해서는 안 됩니다.성능 적중은 예외가 발생한 경우에만 적용됩니다.

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

  • 나는 이것이 예외를 다시 발생시키려는 경우 일반적인 조언이 다음과 같은 이유 중 하나라고 생각합니다. throw; 예외를 다시 발생시키거나 새로운 예외를 생성하는 대신 모든 스택 정보가 다시 수집되는 반면 간단한 발생에서는 모두 보존됩니다.

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

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

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

      • CLR은 지역성을 개선하기 위해 자주 사용되지 않는 코드와 데이터를 자주 사용되는 데이터에서 멀리 배치하려고 시도하므로 추위를 더 많이 느끼게 되므로 불리하게 작용합니다.
      • 하드 페이지 오류로 인한 비용이 다른 모든 것보다 작아질 것입니다.
  • 일반적인 캐치 상황은 깊은 경우가 많으므로 위의 효과가 확대되는 경향이 있습니다(페이지 오류 가능성 증가).

비용의 실제 영향은 당시 코드에서 어떤 일이 벌어지고 있는지에 따라 많이 달라질 수 있습니다.존 스키트는 여기에 좋은 요약이 있습니다, 유용한 링크가 있습니다.나는 예외로 인해 성능이 크게 저하되는 지점에 도달하면 성능 이상의 예외 사용 측면에서 문제가 있다는 그의 말에 동의하는 경향이 있습니다.

내 경험상 가장 큰 오버헤드는 실제로 예외를 발생시키고 처리하는 것입니다.나는 누군가가 일부 개체를 편집할 수 있는 권한이 있는지 확인하기 위해 다음과 유사한 코드를 사용하는 프로젝트에서 작업한 적이 있습니다.이 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();
}

테스트 데이터베이스가 테스트 데이터로 가득 차면 새 양식을 여는 동안 눈에 띄게 속도가 느려집니다.

그래서 나는 그것을 다음과 같이 리팩터링했습니다. 나중에 Quick 'n Dirty 측정에 따르면 약 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배 정도 느립니다.

자주 호출되는 메서드 내에 있는 경우 응용 프로그램의 전반적인 동작에 영향을 미칠 수 있다는 점은 말할 것도 없습니다.
예를 들어, Int32.Parse를 사용하면 그렇지 않으면 쉽게 잡을 수 있는 항목에 대해 예외가 발생하므로 대부분의 경우 나쁜 습관이라고 생각합니다.

따라서 여기에 작성된 모든 내용을 결론적으로 말하면 다음과 같습니다.
1) 예상치 못한 오류를 포착하려면 try..catch 블록을 사용하세요. 성능 저하가 거의 없습니다.
2) 피할 수 있다면 예외 오류에 대한 예외를 사용하지 마십시오.

당시 이에 대해 문의하시는 분들이 많았기 때문에 얼마 전 이에 대한 글을 썼습니다.해당 코드와 테스트 코드는 다음에서 찾을 수 있습니다. http://www.blackwasp.co.uk/SpeedTestTryCatch.aspx.

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

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

나는 만들었습니다 블로그 항목 작년에 이 주제에 대해.확인 해봐.요점은 예외가 발생하지 않으면 시도 블록에 대한 비용이 거의 없다는 것입니다. 그리고 내 노트북에서는 예외가 약 36μs였습니다.예상보다 적을 수도 있지만 결과가 얕은 스택에 있다는 점을 명심하세요.또한 첫 번째 예외는 정말 느립니다.

컴파일러 오류 메시지, 코드 분석 경고 메시지 및 일상적으로 허용되는 예외(특히 한 곳에서 발생하고 다른 곳에서 허용되는 예외)가 없는 코드를 작성, 디버그 및 유지 관리하는 것이 훨씬 쉽습니다.더 쉽기 때문에 코드는 평균적으로 더 잘 작성되고 버그도 줄어듭니다.

나에게는 프로그래머와 품질 오버헤드가 프로세스 흐름에 try-catch를 사용하는 것에 대한 주요 주장입니다.

예외로 인한 컴퓨터 오버헤드는 상대적으로 미미하며 일반적으로 실제 성능 요구 사항을 충족하는 응용 프로그램의 능력 측면에서 아주 작습니다.

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

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

전자는 수년에 걸쳐 Microsoft MVP의 몇 가지 블로그 게시물에서 다루어졌으며, 여러분이 쉽게 찾을 수 있을 것이라고 믿습니다. 그러나 StackOverflow는 관심을 갖고 있습니다. 너무 많이 ~에 대한 콘텐츠 그래서 그 중 일부에 대한 링크를 다음과 같이 제공하겠습니다. 필러 증거:

  • 성능에 미치는 영향 try/catch/finally (그리고 2부), Peter Ritchie는 최적화를 탐색합니다. try/catch/finally 비활성화합니다(표준의 인용문을 사용하여 이에 대해 더 자세히 설명하겠습니다).
  • 성능 프로파일링 ParseTryParseConvertTo 작성자: Ian Huff는 "예외 처리가 매우 느리다"고 노골적으로 말하고 구멍을 뚫어 이 점을 보여줍니다. Int.Parse 그리고 Int.TryParse 서로 반대...그렇게 주장하는 사람에게 TryParse 용도 try/catch 무대 뒤에서 이것은 약간의 빛을 비춰줄 것입니다!

또한 있습니다 이 답변 이는 디스어셈블된 코드를 사용하거나 사용하지 않은 경우의 차이점을 보여줍니다. try/catch.

거기엔 너무나 분명한 것 같아 ~이다 코드 생성 시 노골적으로 관찰할 수 있는 오버헤드, Microsoft를 높이 평가하는 사람들도 그 오버헤드를 인정하는 것 같습니다!그래도 나는, 인터넷을 반복하다...

예, 하나의 사소한 코드 줄에 대해 수십 개의 추가 MSIL 지침이 있으며 이는 비활성화된 최적화도 포함하지 않으므로 기술적으로는 미세 최적화입니다.


나는 몇 년 전에 프로그래머의 생산성(거시적 최적화)에 중점을 두어 삭제된 답변을 게시했습니다.

CPU 시간의 여기저기서 몇 나노초를 절약하지 못한다고 해서 인간이 수동으로 최적화하는 데 누적된 많은 시간을 보상할 수 없기 때문에 이는 불행한 일입니다.당신의 상사는 어떤 것에 대해 더 많은 비용을 지불합니까?당신의 시간은 1시간입니까, 아니면 컴퓨터를 실행한 채 1시간입니까?어느 시점에서 우리는 플러그를 뽑고 이제 그냥 해야 할 때라고 인정해야 할까요? 더 빠른 컴퓨터를 구입하세요?

분명히, 우리는 그래야 합니다. 우선순위 최적화, 우리 코드뿐만 아니라!나의 마지막 대답에서 나는 두 코드 조각 사이의 차이점을 그렸습니다.

사용 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 혼란을 야기할 수 있다 3차원, 동일한 메서드의 다른 부분뿐만 아니라 유지 관리 개발자가 관찰할 수 있는 다른 클래스 및 메서드로 스택을 이동할 수 있기 때문입니다. 어려운 방법!그런데 "goto는 위험하다"고 하더라고요!

마지막에 제가 언급한 것은, try/catch 이점이 있습니다. 최적화를 비활성화하도록 설계되었습니다!당신이 원한다면 그것은 디버깅 지원!그것이 바로 그것이 디자인된 것이고, 그것이 사용되어야 하는 것입니다...

그것도 긍정적인 점인 것 같아요.다중 스레드 응용 프로그램에 대한 안전하고 정상적인 메시지 전달 알고리즘을 손상시킬 수 있는 최적화를 비활성화하고 가능한 경쟁 조건을 포착하는 데 사용할 수 있습니다.) 이것이 try/catch를 사용하기 위해 제가 생각할 수 있는 유일한 시나리오입니다.심지어 대안도 있습니다.


최적화의 기능 try, catch 그리고 finally 장애를 입히다?

일명

어떻게 try, catch 그리고 finally 디버깅 보조 도구로 유용합니까?

그들은 쓰기 장벽입니다.이것은 표준에서 나온 것입니다.

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 문

에 대한 노력하다 성명 stmt 형식:

try try-block
finally finally-block

• 명확한 할당 상태 V 처음에 시도 블록 의 확정 할당 상태와 동일합니다. V 처음에 stmt.
• 명확한 할당 상태 V 처음에 마지막으로 차단 의 확정 할당 상태와 동일합니다. V 처음에 stmt.
• 명확한 할당 상태 V 끝점에서 stmt 다음 중 하나에 해당하는 경우에만 확실히 할당됩니다.영형 V 확실히 끝점에 할당되어 있습니다. 시도 블록영형 V 확실히 끝점에 할당되어 있습니다. 마지막으로 차단제어 흐름 전송(예: 이동 명령문)은 다음 범위 내에서 시작됩니다. 시도 블록, 그리고 다음 외부에서 끝납니다. 시도 블록, 그 다음에 V 또한 다음과 같은 경우 해당 제어 흐름 전송에 확실히 할당된 것으로 간주됩니다. V 확실히 끝점에 할당되어 있습니다. 마지막으로 차단.(이것은 유일한 경우가 아닙니다. V 이 제어 흐름 전송에서 다른 이유로 인해 확실히 할당된 경우에도 여전히 한정적으로 할당된 것으로 간주됩니다.)

12.3.3.15 Try-catch-finally 문

에 대한 확정 할당 분석 노력하다-잡다-마지막으로 양식에 대한 설명:

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

마치 명령문이 다음과 같이 수행됩니다. 노력하다-마지막으로 다음을 포함하는 진술 노력하다-잡다 성명:

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

나는 Hafthor를 정말 좋아해요. 블로그 게시물, 그리고 이 토론에 내 의견을 추가하자면, 데이터 레이어에서 한 가지 유형의 예외(DataAccessException)만 발생시키는 것이 항상 쉬웠다고 말하고 싶습니다.이런 식으로 내 BUSINESS LAYER는 어떤 예외가 예상되는지 알고 이를 포착합니다.그런 다음 추가 비즈니스 규칙에 따라(예:내 비즈니스 개체가 워크플로 등에 참여하는 경우) 새 예외(BusinessObjectException)를 발생시키거나 다시 발생시키지 않고 진행할 수 있습니다.

필요할 때마다 주저하지 말고 try..catch를 사용하고 현명하게 사용하세요!

예를 들어 이 메서드는 워크플로에 참여합니다.

코멘트?

public bool DeleteGallery(int id)
{
    try
    {
        using (var transaction = new DbTransactionManager())
        {
            try
            {
                transaction.BeginTransaction();

                _galleryRepository.DeleteGallery(id, transaction);
                _galleryRepository.DeletePictures(id, transaction);

                FileManager.DeleteAll(id);

                transaction.Commit();
            }
            catch (DataAccessException ex)
            {
                Logger.Log(ex);
                transaction.Rollback();                        
                throw new BusinessObjectException("Cannot delete gallery. Ensure business rules and try again.", ex);
            }
        }
    }
    catch (DbTransactionException ex)
    {
        Logger.Log(ex);
        throw new BusinessObjectException("Cannot delete gallery.", ex);
    }
    return true;
}

Michael L.의 프로그래밍 언어 화용론에서 읽을 수 있습니다.Scott은 요즘 컴파일러가 일반적인 경우에 오버헤드를 추가하지 않는다는 점, 즉 예외가 발생하지 않는 경우를 의미합니다.따라서 모든 작업은 컴파일 타임에 이루어집니다.그러나 런타임에 예외가 발생하면 컴파일러는 올바른 예외를 찾기 위해 이진 검색을 수행해야 하며 이는 새로 발생할 때마다 발생합니다.

그러나 예외는 예외이며 이 비용은 완벽하게 수용 가능합니다.예외 없이 예외 처리를 수행하고 대신 반환 오류 코드를 사용하려고 하면 아마도 모든 서브루틴에 대해 if 문이 필요할 것이며 이로 인해 실제로 실시간 오버헤드가 발생하게 됩니다.if 문은 서브루틴에 들어갈 때마다 수행되는 몇 가지 어셈블리 명령으로 변환된다는 것을 알고 있습니다.

제 영어 실력이 부족해서 죄송합니다. 도움이 되길 바랍니다.이 정보는 인용된 도서를 기반으로 합니다. 자세한 내용은 8.5장 예외 처리를 참조하세요.

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

중요하지 않은 공백을 계산하지 않으면 이 두 개의 동일한 코드 조각이 바이트 단위로 거의 정확히 동일한 길이임을 알 수 있습니다.후자는 4바이트 적은 들여쓰기를 포함합니다.그게 나쁜 일인가요?

설상가상으로 학생은 입력이 int로 구문 분석될 수 있는 동안 루프를 실행하기로 결정합니다.try/catch가 없는 솔루션은 다음과 같습니다.

while (int.TryParse(...))
{
    ...
}

하지만 try/catch를 사용하면 어떻게 보일까요?

try {
    for (;;)
    {
        x = int.Parse(...);
        ...
    }
}
catch
{
    ...
}

Try/catch 블록은 들여쓰기를 낭비하는 마술적인 방법이며, 우리는 여전히 그것이 실패한 이유조차 모릅니다!코드가 명백한 예외 오류로 인해 중단되지 않고 심각한 논리적 결함을 지나 계속 실행될 때 디버깅을 수행하는 사람의 기분이 어떨지 상상해 보십시오.Try/catch 블록은 게으른 사람의 데이터 유효성 검사/위생입니다.

더 적은 비용 중 하나는 try/catch 블록이 실제로 특정 최적화를 비활성화한다는 것입니다. http://msmvps.com/blogs/peterritchie/archive/2007/06/22/performance-implications-of-try-catch-finally.aspx.그것도 긍정적인 점인 것 같아요.다중 스레드 응용 프로그램에 대한 안전하고 정상적인 메시지 전달 알고리즘을 손상시킬 수 있는 최적화를 비활성화하고 가능한 경쟁 조건을 포착하는 데 사용할 수 있습니다.) 이것은 try/catch를 사용하기 위해 제가 생각할 수 있는 유일한 시나리오에 관한 것입니다.심지어 대안도 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top