2009-08-13 2 views
4
비싼 방법이 될 수 있습니다

델리게이트를 성능에 대해 지나치게 나쁜 생각으로 사용하고 있습니까?

if (IsDebuggingEnabled) { 
    instance.Log(GetDetailedDebugInfo()); 
} 

GetDetailedDebugInfo()를, 그래서 우리는 우리가 디버그 모드에서 실행하는 경우를 호출 할 :

는 다음과 같은 코드를 생각해 보자.

의 의

지금, 청소기 대안이 뭔가를 코드하는 것입니다

instance.Log(() => GetDetailedDebugInfo()); 

.LOG는()와 같은 정의되는 경우 :

public void Log(Func<string> getMessage) 
{ 
    if (IsDebuggingEnabled) 
    { 
     LogInternal(getMessage.Invoke()); 
    } 
} 

내 관심사는 성능이다, 예비 시험은 '아무튼 두 번째 사례가 특히 비싸다는 것을 보여 주지만 부하가 증가하면 어떤 놀라움에 빠지기를 원하지 않습니다.

가이 경우에 적용되지 않기 때문에 아, 그리고 조건부 컴파일을 제안하지 마십시오.

(P.S : 나는 미묘한 버그가있는 경우 이렇게 저를 비난하지 않는 질문 텍스트 영역을 물어 StackOverflow의에서 직접 코드를 작성하고 당신이 요점을 파악, 컴파일되지 않습니다 :

+0

증가 된로드를 시뮬레이트하기에 충분히 쉽습니다. 테스트 케이스를 설정하여 두 옵션을 수만 번의 반복 작업을 수행하는 타이트한 루프 내에서 실행하고 비교할 수 있습니다. – Amber

답변

3

그런 경우 성능과 관련하여 몇 가지 문서가 필요 하겠지만 내 코드를 개선하는 방법에 대한 제안이있는 것 같습니다. 아무도 내 P.S. - 당신에게 포인트가 없습니다.

public static bool IsDebuggingEnabled { get; set; } 


    static void Main(string[] args) 
    { 
     for (int j = 0; j <= 10; j++) 
     { 
      Stopwatch sw = Stopwatch.StartNew(); 
      for (int i = 0; i <= 15000; i++) 
      { 
       Log(GetDebugMessage); 
       if (i % 1000 == 0) IsDebuggingEnabled = !IsDebuggingEnabled; 
      } 
      sw.Stop(); 
      Console.WriteLine(sw.ElapsedMilliseconds); 
     } 

     Console.ReadLine(); 
     for (int j = 0; j <= 10; j++) 
     { 
      Stopwatch sw = Stopwatch.StartNew(); 
      for (int i = 0; i <= 15000; i++) 
      { 
       if (IsDebuggingEnabled) GetDebugMessage(); 
       if (i % 1000 == 0) IsDebuggingEnabled = !IsDebuggingEnabled; 
      } 
      sw.Stop(); 
      Console.WriteLine(sw.ElapsedMilliseconds); 
     } 
     Console.ReadLine(); 
    } 

    public static string GetDebugMessage() 
    { 
     StringBuilder sb = new StringBuilder(100); 
     Random rnd = new Random(); 
     for (int i = 0; i < 100; i++) 
     { 
      sb.Append(rnd.Next(100, 150)); 
     } 
     return sb.ToString(); 
    } 

    public static void Log(Func<string> getMessage) 
    { 
     if (IsDebuggingEnabled) 
     { 
      getMessage(); 
     } 
    } 

타이밍이 두 버전 사이 정확하게 일치하는 것 :

그래서 나는 간단한 테스트 케이스를 썼다. 첫 번째 경우는 145ms이고 두 번째 경우는 145ms가됩니다.

내 질문에 대한 답변입니다.

0

전화의 getMessage 위임 Invoke를 호출하는 대신 직접 호출 할 수 있습니다.

if(IsDebuggingEnabled) 
{ 
    LogInternal(getMessage()); 
} 

getMessage에도 null 체크를 추가해야합니다.

+0

첫 번째 주석에 Invoke를 호출하는 대신 대리인을 직접 호출하는 것이 더 빠르다는 잘못된 진술이 포함되었습니다. 그러나, 나는 틀렸고 그 주석을 삭제했다. 작은 콘솔 응용 프로그램을 작성하여 Func 유형의 대리자를 직접 호출하거나 Invoke를 호출하여 호출했습니다. 그런 다음 ildasm 도구를 사용하여 생성 된 IL을 살펴본 결과 IL은 두 호출 모두 동일했습니다. callvirt 인스턴스! 0 클래스 [System.Core] System.Func'1 :: Invoke() –

-3

대리인이 새로운 스레드를 작성한다고 생각합니다. 따라서 성능이 향상 될 수 있습니다. Dav가 제안한 테스트 실행을 설정하고 앱에 의해 생성 된 스레드 수를주의 깊게 살펴 보려면 프로세스 탐색기를 사용할 수 있습니다.

잠깐만! 나는 고쳤다. 위임자는 'BeginInvoke'를 사용할 때만 스레드를 사용하므로 위의 주석은 사용중인 방식에 적용되지 않습니다.

+10

신념을 재고해야합니다. 대리인은 스레드와 완전히 관련이 없습니다. –

+3

대리인 *을 사용하여 멀티 스레드 작업을 쉽게 할 수 있습니다. 아마도 이것은 혼란이 어디서 오는 것인지입니다. –

5

아니요, 성능 저하가 없어야합니다. 결국, 성능은 최전선에 있지 않은 디버그 모드에서만 호출하게 될 것입니다. 실제로, 은 람다를 제거하고 불필요한 중간 익명 메소드의 오버 헤드를 제거하기 위해 메소드 이름을 전달할 수 있습니다.

디버그 빌드에서이 작업을 수행하려는 경우 [Conditional("DEBUG")] 속성을 log 메소드에 추가 할 수 있습니다.

+0

어, 호출 여부와 관계없이 람다가 변수를 포착하여 스택에서 힙으로 끌어 올려야한다는 사실은 어떻습니까? JIT는 레지스터의 캐싱 필드 값이 훨씬 다양하기 때문에 변수가 캡처 될 때 추가 메모리로드 및 저장을 수행 할 가능성이 높습니다. 실제로 빡빡한 루프에서 차이점을 확인할 수 있습니다. –

+1

@Pavel : 일반적으로 이것은 사실입니다. 그러나 OP의 목적을 위해 캡처 된 변수는 없습니다. –

+0

조건부 컴파일이이 경우에 적용되지 않는다고 언급했습니다. 필자가 샘플로 작성한 코드는 간단한 테스트 케이스 일뿐, 제작 코드는 로깅 및 디버그 모드와 완전히 관련이 없지만 비슷한 방식으로 작동해야합니다. – andreialecu

1

또한이 작업을 수행 할 수 있습니다

// no need for a lambda 
instance.Log(GetDetailedDebugInfo) 

// Using these instance methods on the logger 
public void Log(Func<string> detailsProvider) 
{ 
    if (!DebuggingEnabled) 
     return; 

    this.LogImpl(detailsProvider()); 
} 

public void Log(string message) 
{ 
    if (!DebuggingEnabled) 
     return; 

    this.LogImpl(message); 
} 

protected virtual void LogImpl(string message) 
{ 
    .... 
} 
0

표준 답변 : 당신이 그것을 할 해하는 경우

  • , 당신은 가야 해.
  • 반복하면 10^9 번, 스톱워치는 얼마나 걸리는 지 알려주는 &입니다.
  • 프로그램이 큰 경우 다른 곳에서 더 큰 문제가 발생할 가능성이 있습니다.
3

성능에 차이가 있습니다. 최적화의 중요성은 나머지 코드에 따라 달라 지므로 최적화를 시작하기 전에 프로파일 링을 권장합니다.

말했다 데 그 첫 번째 예 :

if (IsDebuggingEnabled) 
{ 
    instance.Log(GetDetailedDebugInfo()); 
} 

IsDebuggingEnabled는 읽기 전용 후 검사가 그것은 절대 변하지 않을 수 있습니다 알고 멀리 jitted됩니다 정적 인 경우. 즉, IsDebuggingEnabled가 false 인 경우 위의 샘플은 성능에 영향을주지 않습니다. 이는 JIT가 완료된 후 코드가 사라지기 때문입니다.

instance.Log(() => GetDetailedDebugInfo()); 

public void Log(Func<string> getMessage) 
{ 
    if (IsDebuggingEnabled) 
    { 
     LogInternal(getMessage.Invoke()); 
    } 
} 

이 메소드는 instance.Log가 호출 될 때마다 호출됩니다. 어느 쪽이 더 느릴지.

하지만이 마이크로 최적화로 시간을 보내기 전에 응용 프로그램을 프로파일 링하거나 성능 테스트를 실행하여 이것이 실제로 응용 프로그램의 병목인지 확인해야합니다.

관련 문제