【问题标题】:C++ Function pointers vs SwitchC++ 函数指针与开关
【发布时间】:2010-04-19 00:44:54
【问题描述】:
  • 哪个更快:函数指针还是开关?

switch 语句大约有 30 个cases,由从 0 到 30 的枚举无符号整数组成。

我可以做到以下几点:

class myType
{
    FunctionEnum func;
    string argv[123];
    int someOtherValue;
};
// In another file:
myType current;
// Iterate through a vector containing lots of myTypes
 // ... for ( i=0; i < myVecSize; i ++ )
    switch ( current.func )
    {
           case 1:
            //...
            break;
           // ........
           case 30:
             // blah
            break;
    }

每次都使用func 进行切换。 switch 的好处还在于我的代码比 30 个函数更有条理。

或者我可以这样做(不太确定):

class myType
{
    myReturnType (*func)(int all, int of, int my, int args );
    string argv[123];
    int someOtherValue;
};

然后,我将有 30 个不同的函数,一开始将指向其中一个的指针分配给 myType。

  • 什么可能更快:Switch 语句或函数指针?

每秒调用次数:大约 1000 万次。 我不能只是测试它——那需要我重写整个事情。目前正在使用开关。

我正在构建一个解释器,我希望它比 Python 和 Ruby 更快 - 每个时钟周期都很重要!

【问题讨论】:

标签: c++ performance


【解决方案1】:

Switch 语句通常使用跳转表来实现。我认为程序集可以简化为一条指令,这将使其非常快。

确定的唯一方法是尝试两种方式。如果您无法修改现有代码,为什么不制作一个测试应用并在那里尝试呢?

【讨论】:

  • 除非你的编译器做得很糟糕,否则 switch 至少和函数指针一样快。使用函数指针只是选择了一种实现 switch 的策略,这可能是最好的一种,而且编译器无论如何都会选择,但是会产生参数传递的开销。编译器可能会看到更好的方法,尤其是。如果任何case 主体有任何共同的代码。我会很乐意推荐 OP 只使用switch,而不是浪费时间尝试两种方式。可以安全地假设 switch 可能会编译成好的代码。如果它是分析中的热点,请检查 asm。
【解决方案2】:

我认为在大多数情况下差异可以忽略不计 - 您的情况可能是个例外。你为什么不把一个简单的原型应用程序放在一起来明确地衡量每个解决方案的性能?我的猜测是,它不会花费超过一个小时的工作......

但是,还要考虑代码的清晰度和可维护性。恕我直言,很明显,函数指针解决方案在这方面胜出。

更新: 另请注意,即使一种解决方案的速度比另一种解决方案的速度快两倍,它仍然不一定能证明重写代码的合理性。您应该首先分析您的应用程序以确定在这些开关中实际花费了多少执行时间。如果它是总时间的 50%,则有理由对其进行优化。如果只有几个百分点,优化将是浪费精力。

【讨论】:

  • 唯一的开关盒只有10-20行长。函数真的不会更具可读性。写这篇文章需要一个多小时,可能一天——我没有那么多时间。我的问题并不真正取决于我的应用程序的架构 - 它只是函数指针与最低级别的开关性能。
  • 遗憾的是,你没有告诉我任何新的东西:我应该编写我的应用程序的两个版本来测试它,这是不可能的,而且单独的原型也不能很好地反映它的最终版本.
  • 我可能完全遗漏了一些东西,但在我看来,测试应用程序需要大约 3 个类:一个带有开关的单个类方法的实现,另一个使用函数指针的相同方法,以及一个测试类(甚至是 main 方法),它执行每个方法 1000 万次并测量执行时间。
  • 我不认为函数指针在清晰度和可维护性方面受到青睐。除非您需要灵活性,否则 IMO 函数指针较差。如果有一组(小)固定的有效延续,函数指针解决方案不能诚实或强制执行。另一方面,切换 n 个案例,每个案例都直接调用另一个函数,这清楚地表明会发生什么,这不是可维护性问题。
【解决方案3】:

Péter Török 有一个正确的想法,即尝试两者并为它们计时。您可能不喜欢它,但不幸的是,这就是现实情况。 “过早优化”的口号是有原因的。

只要不牺牲清晰度,我总是赞成从一开始就使用性能最佳实践。但在这种情况下,你提到的任何一个选项都不是一个明显的胜利。在大多数情况下,这种微小的变化不会产生可衡量的效果。将有几个主要瓶颈完全控制整个系统的速度。在现代计算机上,这里和那里的一些指令基本上是不可见的,因为系统被内存缓存未命中或管道停顿阻塞,这些很难预测。例如,在您的情况下,一个额外的缓存未命中可能会决定哪种方法会更慢。

现实世界的解决方案是使用分析器评估整个系统的性能。这将向您显示代码中的“热点”在哪里。通常的下一步是进行算法更改,以通过更好的算法或通过缓存来减少对热代码的大量调用。只有当一小部分代码点亮分析器时,才值得进行像这样的小型微优化。那时,您必须尝试不同的事情并测试对速度的影响。如果不衡量更改的效果,即使是专家,您也很可能使情况变得更糟。

话虽如此,我猜在你的情况下,如果参数很少,特别是如果每​​个案例的主体都很大的话,函数调用可能会稍微快一点。如果 switch 语句不使用跳转表,那可能会更慢。但它可能会因编译器和机器架构而有很大差异,所以我不会花太多时间在它上面,除非你以后有确凿的证据证明它是你系统的瓶颈。编程既关乎重构,也关乎编写新代码。

【讨论】:

  • 好的cmets,艾伦,但“热点”的概念让我很困扰。这似乎来自于一个旧观念,即如果花费了太多时间,那么可能会超支的唯一方法是 PC 寄存器在某些需要更改的地方花费太多时间。基于该概念欺骗任何分析器都非常容易,而且调用图也好不到哪里去。 stackoverflow.com/questions/1777556/alternatives-to-gprof/…
【解决方案4】:

写口译员很有趣,不是吗?我可以猜测函数指针可能会更快,但是当您进行优化时,猜测不会让您走得太远

如果你真的想从这头野兽身上吸走每一个循环,它需要多次通过,而且猜测不会告诉你要修复什么。 Here's an example of what I mean.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-30
    • 2015-04-15
    • 2018-07-09
    • 1970-01-01
    相关资源
    最近更新 更多