性能

2009-08-31 66 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的值創建這樣的方法,並在計算過程中使用生成的方法。

我想,我可以用表達式樹爲,但表達式樹只適用於簡單的lambda函數,所以我不能用的東西一樣,如果,而等功能體內。所以在這種情況下,我需要以適當的方式改變這個方法。

另一種可能性是創建必需的代碼作爲字符串和動態編譯。但如果我能夠採用現有的方法並相應地修改它,對我來說會更好。

還有Reflection.Emit的,但我不希望堅持使用它,因爲它是非常難以維持。

順便說一句。我不限於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」更易於維護,並且它的表現也會更好。 – 2009-08-31 22:50:30

+0

表達式樹解決方案似乎是最有前途的。但總的來說,我想有一種工具能夠以這種方式優化任何方法。 – Max 2009-08-31 23:10:47

回答

2

我通常會別想這樣的優化。 DoValueXRelatedStuff()做了多少工作?超過10到50個處理器週期?是?這意味着你將構建一個相當複雜的系統來節省少於10%的執行時間(這對我來說似乎相當樂觀)。這可以輕鬆降低到1%以下。

其他優化沒有空間嗎?更好的算法?你是否真的需要消除只有一個處理器週期的單分支(如果分支預測是正確的)?是?你不應該考慮用匯編語言編寫代碼還是其他更具體的機器代碼而不是使用.NET?

可不可以給的N的順序,一個典型方法的複雜性,以及表達的通常結果爲true的比例是多少?

+0

DoValueXRelatedStuff()實際上不是我算法中的方法調用。這只是一些表達式計算,所以它非常快。基本上它只是一些添加,乘法和可能的劃分。 – Max 2009-08-31 23:01:07

+0

N的順序是什麼?比例表達式是否爲真? – 2009-08-31 23:06:04

+0

現在N是8。在典型情況下,真假比例是3/5。但是我需要在下一個版本中將N改爲20。 – Max 2009-08-31 23:15:35

1

這讓我感到吃驚找到這樣一個場景,評估如果語句的開銷是值得動態輸出代碼的努力。

現代CPU的支持branch predictionbranch predication,這使得小部分代碼中的分支開銷接近零。

您是否嘗試過的代碼的基準兩隻手編碼的版本,一個是擁有所有的if語句來代替,但提供零大部分消除所有這些,如果分支機構相同的價值觀,和一個?

+0

我同意,N將不得不在數百,如果不是數千,並且大多數情況下需要在實踐中省略,因爲它是運行時的一個非常重要的部分。 – 2009-08-31 21:57:36

+0

我在ASM/IL級別上做了很多年,但我相信目前的英特爾處理器會在IP(指令指針)到達之前預先確定正確的分支。任何人都知道細節? – 2009-08-31 22:01:32

+0

我不是專家,在最先進的分支預測和C#代碼和執行分支的處理器之間發生了很多東西,但我確信現代處理器將預測分支最多一次假如條件是常量(除了條件數量非常大且分支預測緩存不能存儲所有預測)。 – 2009-08-31 22:18:17

1

如果你真的到代碼優化 - 你做任何事情之前 - 運行探查!它會告訴你瓶頸在哪裏以及哪些區域值得優化。

而且 - 如果語言選擇不限(除LISP),那麼什麼都不會打彙編在性能方面,我記得重寫一些內部函數(比如你有一個實現某些性能魔法)

)使用匯編程序。

+0

在彙編程序中編寫這將是不切實際的。首先,代碼非常複雜,需要花費很多時間在asm中重寫。其次,我不能用匯編程序進行這種動態彙編。 – Max 2009-08-31 22:57:21

0

之前,你做任何事情,你真的有問題

它是否運行足夠長時間來打擾你?

如果是這樣,找出什麼是實際需要時間,不是你猜的This是我用來查看時間在哪裏的快速,骯髒和高效的方法。現在

,你所談論的解釋與編譯。解釋的代碼通常比編譯代碼慢1-2個數量級。其原因是,解釋不斷找出下一步該怎麼做,然後忘記,而編譯的代碼才知道

如果你在這種情況下,那麼它可能是有意義的支付轉換,從而獲得編譯代碼的速度的代價。