我正在尝试实施第一个计算器。我的旧代码(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解决方案更方便。 :)
但我仍然怀疑代码速度和速度。性能。它表现得更快吗?
答案 0 :(得分:1)
我个人更喜欢函数指针版本。它更直接地编码你想要的东西:一组操作,每个操作都是一个功能。
如果有的话,速度的差异实际上可以忽略不计。参数传递不应该有任何不同,因为指针和int
在大多数系统上的大小相同,并且您的枚举很可能在内部使用与int
相同的大小存储。如果有的话,取消引用指针并调用这些函数可能比执行切换更快,这很可能归结为一系列比较和条件跳转。
当然,有人可能会说你的操作如此简单,以至于将它们存储在一个函数中是过度的和不必要的高抽象。我不同意,因为这些函数提供了程序的基本核心,并且您希望它们尽可能纯粹地和均匀地处理。如果你需要使其中一个更复杂,它可能会膨胀switch
所有函数,但它不会影响这个基于函数指针的设计。
因此,从本质上讲,我只看到了新方法的好处。
答案 1 :(得分:0)
所以我最后的想法是不要使用这样的指针,使用大内存消耗变量。
答案 2 :(得分:0)
首先 - 作为一般规则,您拥有有限的资源。在许多情况下,您最宝贵的资源是编程时间。 在这种情况下,您应该使用最简单/最易读的版本 。从你的问题的描述看起来你已经开始过早优化(除非它是优化中的玩具示例)。
那说在某些情况下你关心表现。通常那么你有现有的实现,但性能不符合目标(比如 - 计算需要几天而不是几小时)。我怀疑“简单的计算器”接近这些问题,但我们为了争论而假设它。然后你可以使用宏观优化的整个分支 - 即你应该更多地考虑大图(算法等的变化),而不是小图片(函数指针与案例)。首先,你需要找出造成减速的原因(如果某些事情需要10%的时间运行,你加速50%,总体改善将是5%,另一方面,如果你改进其余的程序25%的表现将提高22%。
在更罕见的情况下,即使经过这样的优化,性能目标也足够高,以至于您的代码仍然与之不匹配。只有当你的程序在很多机器上运行并且运行一年或多年时,通常都是这样 - 例如,蛋白质折叠值得以这种方式进行优化,而大多数程序,甚至是流行的程序都不是。通常在这一点上你需要知道:
如果您处于此状态,则可能是case / switch表现更好,因为它允许在使用仅使用BTB的函数指针时更好地利用分支预测。另一方面,如果您的架构和您使用的是非常简单的C编译器,则case / switch可能会溢出I-cache(因此函数指针的性能会更好)。
总结一下:
虽然在某些情况下你通常需要进行优化,但最好从宏观优化开始并认识到什么是减速来源。如果你的程序正在等待I / O(我会怀疑计算器),那么没有人会知道你的程序是否更快地回复了ns。同样,如果你的解析器/标记器是瓶颈,执行的优化也无济于事。
如果宏观优化不充分,并且运行时间的1%可能值得工作几周,那么您可能需要了解处理器和编译器的微优化。