我的简单计算器没有开关盒的新想法

时间:2013-01-29 12:55:44

标签: c++ performance

我正在尝试实施第一个计算器。我的旧代码(switch-case):

enum arithmetic_type{
    add = 0,
    subtract = 1,
    multiply = 2,
    divide = 3
};

inline void calculate(double &var, double value, arithmetic_type type){

    switch(type)
    {
        case add : var += value;break;
        case subtract : var -= value;break;
        case multiply : var *= value;break;
        case divide : var /= value;break;
    }

}

我看到了“指向函数定义的指针”然后有了一个新想法:改为使用单独的函数。现在我的代码看起来像

typedef void(*arithmetic_type)(double &var, double value); //template

inline void add(double &var, double value){var+=value;} //components
inline void subtract(double &var, double value){var-=value;}
inline void multiply(double &var, double value){var*=value;}
inline void divide(double &var, double value){var/=value;}
////////////////////////////////////////////////////////////////
struct VAR
{
    double var_value;
    arithmetic_type operator_type;
    inline void calculate(double value){operator_type(var_value, value);}
};

我看到它比switch-case 简单得多。更重要的是,我将添加一些其他运算符,例如关系运算符...所以我认为这个新解决方案更清晰,而且比旧的switch-case解决方案更方便。 :)

但我仍然怀疑代码速度和速度。性能。它表现得更快吗?

3 个答案:

答案 0 :(得分:1)

我个人更喜欢函数指针版本。它更直接地编码你想要的东西:一组操作,每个操作都是一个功能。

如果有的话,速度的差异实际上可以忽略不计。参数传递不应该有任何不同,因为指针和int在大多数系统上的大小相同,并且您的枚举很可能在内部使用与int相同的大小存储。如果有的话,取消引用指针并调用这些函数可能比执行切换更快,这很可能归结为一系列比较和条件跳转。

当然,有人可能会说你的操作如此简单,以至于将它们存储在一个函数中是过度的和不必要的高抽象。我不同意,因为这些函数提供了程序的基本核心,并且您希望它们尽可能纯粹地和均匀地处理。如果你需要使其中一个更复杂,它可能会膨胀switch所有函数,但它不会影响这个基于函数指针的设计。

因此,从本质上讲,我只看到了新方法的好处。

答案 1 :(得分:0)

  1. 您使用了“内联”,因此功能代码被复制到您使用它的地方,没有任何收获。
  2. 指针肯定可以优化性能,但这里几乎不会引起注意,因为你使用的是小尺寸的变量(不像使用MB内存的巨大照片)。
  3. 代码不具有可读性,确保您可以轻松理解此代码,但将此约定想象为数千行代码。
  4. 所以我最后的想法是不要使用这样的指针,使用大内存消耗变量。

答案 2 :(得分:0)

首先 - 作为一般规则,您拥有有限的资源。在许多情况下,您最宝贵的资源是编程时间。 在这种情况下,您应该使用最简单/最易读的版本 。从你的问题的描述看起来你已经开始过早优化(除非它是优化中的玩具示例)。

那说在某些情况下你关心表现。通常那么你有现有的实现,但性能不符合目标(比如 - 计算需要几天而不是几小时)。我怀疑“简单的计算器”接近这些问题,但我们为了争论而假设它。然后你可以使用宏观优化的整个分支 - 即你应该更多地考虑大图(算法等的变化),而不是小图片(函数指针与案例)。首先,你需要找出造成减速的原因(如果某些事情需要10%的时间运行,你加速50%,总体改善将是5%,另一方面,如果你改进其余的程序25%的表现将提高22%。

在更罕见的情况下,即使经过这样的优化,性能目标也足够高,以至于您的代码仍然与之不匹配。只有当你的程序在很多机器上运行并且运行一年或多年时,通常都是这样 - 例如,蛋白质折叠值得以这种方式进行优化,而大多数程序,甚至是流行的程序都不是。通常在这一点上你需要知道:

  • 您正在优化的确切平台
  • 基本的计算机体系结构(流水线,ILP,缓存架构,缓存一致性协议等)
  • 您正在使用的编译器

如果您处于此状态,则可能是case / switch表现更好,因为它允许在使用仅使用BTB的函数指针时更好地利用分支预测。另一方面,如果您的架构和您使用的是非常简单的C编译器,则case / switch可能会溢出I-cache(因此函数指针的性能会更好)。


总结一下:

  1. 不优化
  2. 尚未优化(仅适用于专家)
  3. 虽然在某些情况下你通常需要进行优化,但最好从宏观优化开始并认识到什么是减速来源。如果你的程序正在等待I / O(我会怀疑计算器),那么没有人会知道你的程序是否更快地回复了ns。同样,如果你的解析器/标记器是瓶颈,执行的优化也无济于事。

    如果宏观优化不充分,并且运行时间的1%可能值得工作几周,那么您可能需要了解处理器和编译器的微优化。