2009-08-31 2 views
2

동적 코드 생성으로 성능을 향상시킬 수있는 방법이 있지만이 문제를 해결하는 가장 좋은 방법은 무엇인지 모르겠습니다.성능을위한 동적 컴파일

내가


class Calculator 
{ 
    int Value1; 
    int Value2; 
    //.......... 
    int ValueN; 

    void DoCalc() 
    { 
    if (Value1 > 0) 
    { 
     DoValue1RelatedStuff();  
    } 
    if (Value2 > 0) 
    { 
     DoValue2RelatedStuff();  
    } 
    //.... 
    //.... 
    //.... 
    if (ValueN > 0) 
    { 
     DoValueNRelatedStuff();  
    } 
    } 
} 

DoCalc 방법은 낮은 수준에있는 클래스가 그것을 계산하는 동안 여러 번이라고 가정하자. 또 다른 중요한 측면은 ValueN이 처음에만 설정되고 계산 중에는 변경되지 않는다는 것입니다. 그래서 DoCalc 메서드의 if 함수 중 많은 것이 ValueN 중 많은 수가 0이므로 불필요합니다. 따라서 동적 코드 생성이 성능을 향상시키는 데 도움이 될 수 있기를 바랍니다. 예를 들어

내가 방법을


    void DoCalc_Specific() 
    { 
    const Value1 = 0; 
    const Value2 = 0; 
    const ValueN = 1; 

    if (Value1 > 0) 
    { 
     DoValue1RelatedStuff();  
    } 
    if (Value2 > 0) 
    { 
     DoValue2RelatedStuff();  
    } 
    .... 
    .... 
    .... 
    if (ValueN > 0) 
    { 
     DoValueNRelatedStuff();  
    } 
    } 

을 만들고 최적화를 컴파일하는 경우는 C# 컴파일러에 전환 만 필요한 물건을 유지하는만큼 영리하다. 그래서 ValueN의 값을 기반으로 런타임에 이러한 메서드를 만들고 계산 중에 생성 된 메서드를 사용하려고합니다.

표현식 트리를 사용할 수는 있지만 표현식 트리는 간단한 람다 함수에서만 작동하므로 함수 본문 내부에서 if, while 등을 사용할 수 없습니다. 따라서이 경우 적절한 방법으로이 메서드를 변경해야합니다.

또 다른 가능성은 문자열로 필요한 코드를 만들고이를 동적으로 컴파일하는 것입니다. 그러나 기존의 방법을 취하여 그에 맞게 수정할 수 있다면 훨씬 더 나을 것입니다.

리플렉션이 있습니다. 전송하지 마세요. 유지하기가 매우 어려워서 붙잡고 싶지 않습니다.

BTW. 나는 C#에 국한되지 않는다. 그래서 이런 종류의 문제에 가장 적합한 프로그래밍 언어에 대한 제안에 대해 열려 있습니다. 두 가지 이유로 LISP를 제외하고.

하나의 중요한 설명. DoValue1RelatedStuff() 내 알고리즘에서 메서드 호출이 아닙니다. 공식 기반의 계산 일 뿐이지 만 꽤 빠릅니다. 나는 몇 가지 성능 테스트를 실행 한이


if (Value1 > 0) 
{ 
    // Do Value1 Related Stuff 
} 

처럼 작성해야 내가 두 IFS 하나는 최적화 방법은 경우 중복보다 약 2 배 빠른 사용할 수 없을 때 볼 수 있습니다.

여기에 내가 테스트에 사용 된 코드는 다음과 같습니다


    public class Program 
    { 
     static void Main(string[] args) 
     { 
      int x = 0, y = 2; 

      var if_st = DateTime.Now.Ticks; 
      for (var i = 0; i < 10000000; i++) 
      { 
       WithIf(x, y); 
      } 
      var if_et = DateTime.Now.Ticks - if_st; 
      Console.WriteLine(if_et.ToString()); 

      var noif_st = DateTime.Now.Ticks; 
      for (var i = 0; i < 10000000; i++) 
      { 
       Without(x, y); 
      } 
      var noif_et = DateTime.Now.Ticks - noif_st; 
      Console.WriteLine(noif_et.ToString()); 

      Console.ReadLine(); 

     } 

     static double WithIf(int x, int y) 
     { 
      var result = 0.0; 
      for (var i = 0; i < 100; i++) 
      { 
       if (x > 0) 
       { 
        result += x * 0.01; 
       } 
       if (y > 0) 
       { 
        result += y * 0.01; 
       } 
      } 
      return result; 
     } 

     static double Without(int x, int y) 
     { 
      var result = 0.0; 
      for (var i = 0; i < 100; i++) 
      { 
       result += y * 0.01; 
      } 
      return result; 
     } 
    } 
+0

'System.Linq.Expressions'를 사용하고자한다면 항상 조건부로 표현 트리를 만들 수 있습니다. 처음에 썼던 것과 완전히 똑같지는 않겠지 만, 거의 확실하게 작동 할 것이고 System.Reflection.Emit보다 유지 보수가 가능할 것이고, 또한 더 나은 성능을 발휘할 수있는 좋은 기회가 있습니다. –

+0

표현 트리가있는 솔루션이 가장 유망한 것으로 보입니다. 그러나 일반적으로 어떤 방법 으로든이 방법으로 최적화 할 수있는 도구를 갖고 싶습니다. – Max

답변

2

나는 보통 이러한 최적화에 대해 생각조차하지 않을 것이다. DoValueXRelatedStuff()의 작업량은 얼마나됩니까? 10 ~ 50 프로세서 사이클 이상? 예?즉, 실행 시간을 10 % 미만으로 줄이기 위해 복잡한 시스템을 구축하게 될 것입니다 (이는 나에게 상당히 낙관적 인 것으로 보입니다). 이것은 쉽게 1 % 이하로 내려갈 수 있습니다.

다른 최적화 할 여지가 있습니까? 더 나은 알고리즘? 분기 예측이 정확하다면 단 하나의 프로세서 사이클만을 사용하는 단일 분기를 제거해야합니까? 예? .NET을 사용하는 대신 어셈블러 또는 다른 특정 기계에 코드를 작성하는 것에 대해 생각하지 않아야합니까?

일반적인 방법의 복잡성과 일반적으로 평가되는 표현의 비율을 N의 순서로 줄 수 있습니까?

+0

DoValueXRelatedStuff()는 실제로 내 알고리즘에서 메서드 호출이 아닙니다. 그것은 단지 몇 가지 표현식 계산이므로 매우 빠릅니다. 기본적으로 추가, 곱셈, 그리고 나눗셈에 불과합니다. – Max

+0

그리고 N의 순서는 무엇입니까? 그리고 비율 표현은 거짓으로 사실입니까? –

+0

지금 N은 8입니다.전형적인 상황에서 허위 비율은 3/5입니다. 하지만 다음 버전에서는 N을 20으로 변경해야합니다. – Max

1

그것은 경우 문을 평가하는 오버 헤드를 동적으로 코드를 방출하는 노력이 가치가 어디 시나리오를 찾기 위해 나를 놀라게한다.

현대 CPU의 지원 branch predictionbranch predication은 작은 코드 세그먼트에서 분기에 대한 오버 헤드를 0으로 만듭니다.

코드의 손으로 코딩 한 두 버전, 즉 모든 if 문을 제자리에두고 있지만 대부분의 경우 0 값을 제공하는 코드와 동일한 모든 if 분기를 제거하는 코드의 벤치마킹을 시도 했습니까?

+0

나는 동의한다. N은 수천이 아니라해도 수백 개가되어야 할 것이고, 대부분의 경우는 실사에서 생략 될 필요가있다. 왜냐하면 그것이 런타임의 중요한 부분이기 때문이다. –

+0

몇 년 동안 ASM/IL 수준에서 많은 일을하지는 못했지만 현재 인텔 프로세서가 IP (명령 포인터)가 도착하기 전에 올바른 분기를 미리 결정할 것이라고 생각합니다. 누구든지 세부 사항을 알고 있습니까? –

+0

저는 최신 분기 예측의 전문가가 아니며 C# 코드와 분기를 실행하는 프로세서 사이에 많은 일이 발생하지만, 현대 프로세서는 조건이 거짓 일 때 가장 많이 분기를 예측할 것이라고 확신합니다. 상수 (조건 수는 매우 크고 분기 예측 캐시는 모든 예측을 저장할 수 없습니다). –

1

당신이 정말로하기 전에 코드 최적화를한다면 - 프로파일 러를 실행하십시오! 그것은 병목이 어디에 있는지, 그리고 어느 부분을 최적화 할 가치가 있는지를 보여줄 것입니다.

또한 - 언어 선택은 아무 것도이 성능면에서 어셈블러를 이길 것이다 (LISP 제외) 제한되지 않은 경우)

나는 당신이 가지고있는 것과 같은 몇 가지 내부 기능을 (다시 작성하여 몇 가지 성능 마법을 달성 기억) 어셈블러를 사용하여.

+0

어셈블러에서이 코드를 작성하는 것은 비현실적입니다. 우선 코드가 복잡하고 asm에서 다시 작성하는 데 너무 많은 시간이 필요합니다. 둘째, 나는 어셈블러로 동적 컴파일을 할 수 없다. – Max

0

무엇이든지하기 전에 실제로 문제가 있습니까??

즉, 불편을 겪을만큼 오래 실행합니까?

그렇다면 실제 시간 인 이 무엇인지 알아보십시오.. This은 시간이가는 곳을보기 위해 사용하는 빠르고, 더럽고, 매우 효과적인 방법입니다.

이제는 해석과 컴파일에 대해 이야기하고 있습니다. 해석 된 코드는 일반적으로 컴파일 된 코드보다 1-2 배 더 느립니다. 이유는 해석자가 다음에 수행 할 작업을 계속 알아 낸 다음을 잊어 버렸고 컴파일 된 코드 을 알고 있기 때문입니다.

이 상황에있는 경우 컴파일 된 코드의 속도를 얻으려면 번역 가격을 지불하는 것이 좋습니다.

관련 문제