从处理器的角度来说,条件分支会导致指令流水线的中断,所以控制语句需要严格保存状态,因为处理器是很难直接进行逻辑判断的,有可能它会执行一段时间,发现出错后再返回,也有可能通过延时等手段完成控制流的正确性。所以笔者在想,使用过多的if _else,除了可读性差,是否还会对执行效率造成影响呢?
于是笔者决定采用职责链重构if控制语句,笔者猜想,如果采用设计模式既可以避免if分支,提高执行效率,同时可以让程序具备解耦合性和可维护性,是否是一种更优解呢?
为了验证笔者的猜想,更好的优化c语言程序,笔者使用职责链模式对一段if_else程序进行了重构。
程序的性能标准是执行时间,计算程序时间的程序来源于:C语言 计算程序运行时间(精确到毫秒/微秒)_c语言计算运行时间-CSDN博客
以下是笔者本人用c编写的责任链设计模式:
#include<stdio.h>
#include<stdlib.h>
#include<memory.h>
#include<string.h>
#include <windows.h>
typedef int (*Handler)(int);
typedef struct task_node
{
Handler handler;
struct task_node* next;
}taskN;
int task1(int a) {
if (a == 1) {
//printf("a == 1"); printf会耗费大量时间,为了不影响测试,所以只留下判断语句
return 0;
}
return -1;
}
int task2(int a) {
if (a == 2) {
return 0;
}
return -1;
}
int task3(int a) {
if (a == 3) {
return 0;
}
return -1;
}
int task4(int a) {
if (a == 4) {
return 0;
}
return -1;
}
int task5(int a) {
if (a == 5) {
return 0;
}
return -1;
}
int task6(int a) {
if (a == 6) {
return 0;
}
return -1;
}
int task7(int a) {
if (a == 7) {
return 0;
}
return -1;
}
int task8(int a) {
if (a == 8) {
return 0;
}
return -1;
}
int task9(int a) {
if (a == 9) {
return 0;
}
return -1;
}
int task10(int a) {
if (a == 10) {
return 0;
}
return -1;
}
int task11(int a) {
if (a == 11) {
return 0;
}
return -1;
}
int task12(int a) {
if (a == 12) {
return 0;
}
return -1;
}
taskN* initi() {
taskN* tas1 = malloc(sizeof(taskN));
taskN* tas2 = malloc(sizeof(taskN));
taskN* tas3 = malloc(sizeof(taskN));
taskN* tas4 = malloc(sizeof(taskN));
taskN* tas5 = malloc(sizeof(taskN));
taskN* tas6 = malloc(sizeof(taskN));
taskN* tas7 = malloc(sizeof(taskN));
taskN* tas8 = malloc(sizeof(taskN));
taskN* tas9 = malloc(sizeof(taskN));
taskN* tas10 = malloc(sizeof(taskN));
taskN* tas11= malloc(sizeof(taskN));
taskN* tas12= malloc(sizeof(taskN));
tas1->handler = task1;
tas2->handler = task2;
tas3->handler = task3;
tas4->handler = task4;
tas5->handler = task5;
tas6->handler = task6;
tas7->handler = task7;
tas8->handler = task8;
tas9->handler = task9;
tas10->handler = task10;
tas11->handler = task11;
tas12->handler = task12;
tas1->next = tas2;
tas2->next = tas3;
tas3->next = tas4;
tas4->next = tas5;
tas5->next = tas6;
tas6->next = tas7;
tas7->next = tas8;
tas8->next = tas9;
tas9->next = tas10;
tas10->next = tas11;
tas11->next = tas12;
return tas1;
}
void process(taskN* bol, int a) {
int i = bol->handler(a);
while (i != 0)
{
i = bol->handler(a);
bol = bol->next;
}
}
void main() {
double run_time;
LARGE_INTEGER time_start; //开始时间
LARGE_INTEGER time_over; //结束时间
double dqFreq; //计时器频率
LARGE_INTEGER f; //计时器频率
QueryPerformanceFrequency(&f);
dqFreq = (double)f.QuadPart;
QueryPerformanceCounter(&time_start);
taskN* bol = initi();
int a = 12;
for (int i = 0; i <= 50000000; i++)
{
/*
if (a == 1) {
}
else if (a == 2) {
}
else if (a == 3) {
}
else if (a == 4) {
}
else if (a == 5) {
}
else if (a == 6) {
}
else if (a == 7) {
}
else if (a == 8) {
}
else if (a == 9) {
}
else if (a == 10) {
}
else if (a == 11) {
}
else if (a == 12) {
}
else {
}
*/
/*
process(bol, a);
*/
switch (a) {
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
case 8:
break;
case 9:
break;
case 10:
break;
case 11:
break;
case 12:
break;
default:
break;
}
}
QueryPerformanceCounter(&time_over); //计时结束
run_time = 1000000 * (time_over.QuadPart - time_start.QuadPart) / dqFreq;
//乘以1000000把单位由秒化为微秒,精度为1000 000/(cpu主频)微秒
printf("\nrun_time:%fus\n", run_time);
/*为了偷懒,程序就懒得free了*/
}
使用if_else时:
a=1,第一个循环执行:
a=12,最后一个循环执行:
两者时间基本没区别,在循环语句中
笔者还测试了只有if( a== 1)这一个判断语句下的性能:
可以看出基本没什么区别。
使用case时:
a=12时如下,与a=1时的结果几乎没有区别:
switch语句除了比较简洁一点,执行速度完全没法比。
使用责任链时:
任务优先级在责任链上过高(a=1):
任务优先级在责任链过低(a=12):
可以看出,职责链的速度非常慢,特别是任务优先级低时,执行速度几乎是if_else的150倍。
综上得出,if_else的执行速度是最快的,如果判断的时间复杂度过大,而且判断语句的数量较少,使用if_else,性能可能会更好。
但是,上述的前提是建立在判断语句的数量较少的情况下。
现在让我们考虑这样一种情况:不是判断a从1到12,而是从a到10000呢?程序等价于下图:
再附加一个条件,大多数情况下,a的值都是1。
虽然if_else的耗时比较短,但是在大量的循环下,耗时也不容小觑。因为a大多数情况是1,其他的判断分支只有小概率发生,此时我们使用责任链就可以完美跳过这些语句的判断:
(程序用for循环表示中间的分支,写得不准确,理解意思就好)
当然,读者可能认为if else if在这里也可以跳过中间的语句,也能实现优先级,但是如果改成这样呢:
这种情况下,if else if只能执行其中一个,显然不行,使用大量的if,就变成了上面的情况,我们不得不将原先的代码大量删除并重写,这十分让人难受。这样一对比,对于部分实现的if分支,使用责任链的优点有什么呢?如下图:
在责任链任务里进行改动,可以选择责任链终止的位置,跳过后面的任务,当然,通过goto等跳转指令也是可以实现的,但是if语句很难做到这样灵活,对于复杂的业务场景,很难在原先的基础上进行拓展,责任链的解耦合性和可拓展性是非常好的优点。
考虑复杂的业务场景,责任链明显优于传统的if_else。
尽管如此,if_else还是十分友好的,特别是在mcu等产品的开发中,使用设计模式的资源花费简直是奢求,毕竟设计模式是针对高级语言的,大多数情况下if_else往往是最优解。不过,笔者认为大量使用if_else的前提是项目不需要长期维护,一般情况下还是要多使用设计模式,例如在linux内核中就有源码使用了责任链模式,这是从性能,解耦合性和可读性等多方面考虑,值得开发者学习。如果是小型项目,对维护没有要求,而且控制语句的数量较少,为了性能和简便性可以考虑直接使用if_else分支。但是如果大型项目中含有大量if分支,可以考虑使用设计模式重构。