c程序杂谈系列(职责链模式与if_else)

从处理器的角度来说,条件分支会导致指令流水线的中断,所以控制语句需要严格保存状态,因为处理器是很难直接进行逻辑判断的,有可能它会执行一段时间,发现出错后再返回,也有可能通过延时等手段完成控制流的正确性。所以笔者在想,使用过多的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分支,可以考虑使用设计模式重构。

相关推荐
友大冰9 分钟前
Go 语言已立足主流,编程语言排行榜24 年 11 月
开发语言·后端·golang
尘浮生11 分钟前
Java项目实战II基于微信小程序的原创音乐小程序(开发文档+数据库+源码)
java·开发语言·数据库·spring boot·微信小程序·小程序·maven
爱跑步的一个人15 分钟前
STL-常用排序算法
开发语言·c++·排序算法
蜜桃小阿雯22 分钟前
JAVA开源项目 微服务在线教育系统 计算机毕业设计
java·开发语言·spring boot·微服务·java-ee·开源·maven
花下的晚风22 分钟前
单元测试时报错找不到@SpringBootConfiguration
java·开发语言·单元测试
爱编程— 的小李31 分钟前
结构体(c语言)
c语言·开发语言
言之。36 分钟前
【K-Means】
算法·机器学习·kmeans
fathing44 分钟前
c# 调用c++ 的dll 出现找不到函数入口点
开发语言·c++·c#
前端拾光者1 小时前
前端开发设计模式——责任链模式
设计模式·责任链模式
前端青山1 小时前
webpack指南
开发语言·前端·javascript·webpack·前端框架