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

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

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

顺便提一句。我不仅限于 C#。因此,我愿意接受最适合此类问题的编程语言的建议。出于几个原因,除了 LISP。

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


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

我已经运行了一些性能测试,并且我可以看到,当禁用一个 if 时,使用两个 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;
        }
    }
4

4 回答 4

2

我通常不会考虑这样的优化。做了多少工作DoValueXRelatedStuff()?超过 10 到 50 个处理器周期?是的?这意味着您将构建一个相当复杂的系统来节省不到 10% 的执行时间(这对我来说似乎很乐观)。这很容易降至不到 1%。

没有其他优化的余地了吗?更好的算法?你真的需要消除只需要一个处理器周期的单个分支(如果分支预测是正确的)?是的?你不应该考虑用汇编程序或其他更特定于机器的东西而不是使用.NET来编写代码吗?

您能否给出 的顺序N、典型方法的复杂性以及通常评估为真的表达式的比率?

于 2009-08-31T22:06:27.193 回答
1

如果发现评估if语句的开销值得动态发出代码的努力,我会感到惊讶。

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

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

于 2009-08-31T21:56:05.747 回答
1

如果你真的很喜欢代码优化——在你做任何事情之前——运行分析器!它将向您展示瓶颈在哪里以及哪些领域值得优化。

另外 - 如果语言选择不受限制(LISP 除外),那么在性能方面没有什么能比汇编程序更好;)

我记得通过使用汇编程序重写一些内部函数(比如你拥有的函数)来实现一些性能魔术。

于 2009-08-31T22:08:21.620 回答
0

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

即它运行的时间是否足以打扰你?

如果是这样,找出真正需要时间的东西,而不是你猜的。是我用来查看时间流逝的快速、肮脏和高效的方法。

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

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

于 2009-09-28T01:03:14.737 回答