性能

2009-08-31 63 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个数量级。其原因是,解释不断找出下一步该怎么做,然后忘记,而编译的代码才知道

如果你在这种情况下,那么它可能是有意义的支付转换,从而获得编译代码的速度的代价。