我知道如何通过动态代码生成来提高性能,但我不确定哪种方法是解决这个问题的最佳方法。

假设我有一堂课


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 函数一起使用,所以我不能使用 if、while 等。函数体内。所以在这种情况下我需要以适当的方式改变这个方法。

另一种可能性是将必要的代码创建为字符串并动态编译它。但如果我能采用现有的方法并进行相应的修改,那对我来说会更好。

还有 Reflection.Emit,但我不想坚持使用它,因为它很难维护。

顺便提一句。我并不局限于 C#。因此,我愿意接受最适合此类问题的编程语言的建议。由于一些原因,LISP 除外。

一项重要的澄清。DoValue1RelatedStuff() 不是我的算法中的方法调用。这只是一些基于公式的计算,而且速度相当快。我应该这样写


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

我已经运行了一些性能测试,我可以看到,使用两个 if,当其中一个被禁用时,优化方法比使用冗余 if 快大约 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;
        }
    }
有帮助吗?

解决方案

我通常会别想这样的优化。多少工作没有DoValueXRelatedStuff()吗?超过10至50个处理器周期?是?这意味着你要建立一个相当复杂的系统,节省10%以下的执行时间(这似乎相当乐观对我来说)。这可以很容易下降到小于1%。

有没有空间其他优化?更好的算法?一个你真的需要消除单一分行采取只有一个处理器周期(如果分支预测是正确的)?是?你是不是应该想想汇编或具体的,而不是使用.NET别的更多的机器编写代码?

你能给N的顺序,所述的典型方法的复杂性,和表达式通常评估为真?的比率

其他提示

我会惊讶地发现一种场景,其中评估的开销 如果 statements 值得付出努力来动态发出代码。

现代CPU的支持 分支预测分支预测, ,这使得小代码段中的分支开销接近于零。

您是否尝试过对代码的两个手动编码版本进行基准测试,其中一种包含所有 if 语句,但为大多数语句提供零值,另一种删除所有相同的 if 分支?

如果你是真正进入代码优化 - 你做任何事情之前 - 运行探查!它会告诉你在哪里的瓶颈和哪些领域值得优化。

此外 - 如果语言的选择并不限于(除了LISP)则事击败汇编在性能方面)

记得通过使用汇编重写一些内部函数(像您有一个)实现某些性能的魔法。

在你做任何事情之前, 你真的有问题吗?

IE。它运行的时间是否足以打扰您?

如果是这样,找出实际上需要时间的事情, 不是你猜的那样. 这是我用来查看时间去向的快速、肮脏且高效的方法。

现在,您正在谈论解释与编译。解释后的代码通常比编译后的代码慢 1-2 个数量级。原因是口译员不断地弄清楚下一步该做什么, 然后忘记, ,编译代码时 只知道.

如果您处于这种情况,那么付出翻译的代价以获得编译代码的速度可能是有意义的。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top