【问题标题】:Which one of these approaches would yield the fastest runtime? [closed]这些方法中的哪一种会产生最快的运行时间? [关闭]
【发布时间】:2012-03-29 17:35:45
【问题描述】:

我正在进行一个简短的思想实验,希望得到一些帮助。

考虑一个类,它根据一个函数计算一些输出,该函数本身可以改变,但只能根据预定的集合来改变。

或者,一些函数列表:

double function1(double input){
    //perform some operations
    return output;
}

double function2(double input){
    //perform some operations
    return output;
}

double function3(double input){
    //perform some operations
    return output;
}

double function4... etc.

现在假设这些函数包含在某个类中,该类预先确定了每当调用该类以计算某个值时使用哪个函数

Class Calculate{

    int whichFunction = 2;

    double calculate (double input){

    //decide which function to use
    switch(whichFunction){
        case 1:
            return function1(input);
            break;
        case 2:
            return function2(input);
            break;
        case 3:
            return function3(input);
            break;
        case 4... etc..
    }

    ... predetermined functions here....
}

当然,执行此操作的第一个想法是打开值并继续前进。但是,如果我们不使用开关,而只是将函数更改为运行呢?

Class Calculate{

    Func<double> whichFunction;

    double calculate (double input){
        return whichFunction.Invoke(input);
    }

    void setFunction(int functionNumber){
        switch(whichFunction){
        case 1:
            whichFunction = function1;
        case 2:
            whichFunction = function2;
        case 3:
            whichFunction = function3;
        case 4... etc..
    }

    ... predetermined functions here....
}

考虑到您选择的函数很少更改,或者甚至可能只在构造函数中设置一次,这会避免切换实际上提供运行时速度优势,或者以这种方式调用函数的行为会抵消好处.

其他可能的选择:

  1. 级联 if-else.... 不,可能是最慢的方法。
  2. 不要使用 Func 泛型,而是使用委托。
  3. 利用事件并简单地重新分配分配给事件的功能。
  4. 不要使用 Func,而是使用方法指针。

任何想法都将不胜感激!

【问题讨论】:

  • 如果您真的关心,请测量。并且一定要考虑 JIT 预热。更妙的是,算了吧 - 可能还有一百个低垂的果实。
  • 你为什么要做一个思想实验来解决一个真实实验可以回答的问题?

标签: c# performance delegates function-pointers


【解决方案1】:

这里的决定不太可能以任何有意义的方式影响性能。我想在大多数情况下,正在执行的功能比触发它的逻辑更可能花费更多时间。

您提到的每个代码片段都会运行得非常快,即使一个比另一个快,也不会有任何明显的差距。选择最容易理解和维护的方法,然后开始在其他地方寻找性能提升如果你发现你的应用程序执行太慢

【讨论】:

    【解决方案2】:

    在不了解全局的情况下很难做出推荐。例如,您可能正在构建一个计算器,如果是这样,您可能希望为您的函数重载实际的数学运算符。

    如果您要使用几个功能并且只有一个可能适用,那么也许是分层的

    • if () {} else if () {} else if () {}

    ...结构可能会更好地为您服务(更受欢迎的预先审讯),因为结构会在您满足条件的那一刻退出执行。

    当然还有 && 和 || 的短路逻辑。要考虑的功能。这种短路可以为您节省大量时间,具体取决于您所追求的。

    【讨论】:

    • break in case 语句用于短路
    【解决方案3】:

    “正确”的答案是实际衡量和比较您的选择。 “务实”的答案是,除非你有大量的函数,否则你已经花时间思考这个问题并不重要。 “合乎逻辑”的答案是,您的第二个选项的切换频率要低得多,因此它应该更快,尽管它不是线程安全的,并且是一个尴尬的 API,会导致错误(调用者忘记设置函数或设置它如此频繁以至于它违背了目的)

    【讨论】:

      【解决方案4】:

      POITROAE。

      使用分析器!

      现代编译器可使用的优化太多,无法预测在您的情况下什么会运行得最快。一般来说,

      • 函数调用是堆栈上的一系列操作并跳转到某个位置。
      • 有些编译器会为 switch 语句的每个 case 跳转
      • 有些人会做跳台,这可能是福也可能是祸。
      • 如果您在交换机的本地范围内有变量,则可能会设置堆栈帧。
      • 等等等等等等...cases 几乎是无限的。

      也就是说,在每种情况下只有几行代码的 switch 语句可能比函数调用快。

      此外,在 X86 上,跳转和函数调用可以清除缓存和管道,因此您也需要为此付出很大的代价。

      分析器确实是现代软件的最佳选择。

      【讨论】:

        【解决方案5】:

        您的两个示例的相对性能将取决于具体的上下文:

        1. 在大多数情况下,尤其是如果calculate 被多次调用,switch 语句中的多重比较将导致许多分支将被执行多次,并可能被CPU 错误地预测。这很容易超过 Invoke 版本中函数调用的成本。
        2. 如果 function1..3 很短并且可以内联,那么编译器可能会优化“switch”版本比“Invoke”版本稍快,因为没有办法优化掉后者的功能。

        我的建议是使用“Invoke”版本,因为在大多数情况下它会带来最佳性能。

        【讨论】:

          猜你喜欢
          • 2012-04-02
          • 2017-04-11
          • 2013-06-28
          • 1970-01-01
          • 2011-03-15
          • 2014-02-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多