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分支,可以考虑使用设计模式重构。
相关文章:
c程序杂谈系列(职责链模式与if_else)
从处理器的角度来说,条件分支会导致指令流水线的中断,所以控制语句需要严格保存状态,因为处理器是很难直接进行逻辑判断的,有可能它会执行一段时间,发现出错后再返回,也有可能通过延时等手段完成控制流的正…...
前端开发技术之CSS(层叠样式表)
盒模型(Box Model) CSS盒模型描述了如何计算一个元素的总宽度和高度。 它包括以下几个部分: 1. 内容(Content):元素的实际内容,比如文本或图片。 2. 内边距(Padding)&…...
go语言day20 使用gin框架获取参数 使用自定义的logger记录日志
Golang 操作 Logger、Zap Logger 日志_golang zap-CSDN博客 目录 一、 从控制器中获取参数的几种形式 1) 页面请求url直接拼接参数。 2) 页面请求提交form表单 3) 页面请求发送json数据,使用上下文对象c的BindJSON()方法接…...
DHCP笔记
DHCP---动态主机配置协议 作用:为终端动态提供IP地址,子网掩码,网关,DNS网址等信息 具体流程 报文抓包 在DHCP服务器分配iP地址之间会进行广播发送arp报文,接收IP地址的设备也会发送,防止其他设备已经使用…...
TCP为什么需要四次挥手?
tcp为什么需要四次挥手? 答案有两个: 1.将发送fin包的权限交给被动断开发的应用层去处理,也就是让程序员处理 2.接第一个答案,应用层有了发送fin的权限,可以在发送fin前继续向对端发送消息 为了搞清楚这个问题&…...
MySQL 索引相关基本概念
文章目录 前言一. B Tree 索引1. 概念2. 聚集索引/聚簇索引3. 辅助索引/二级索引4. 回表5. 联合索引/复合索引6. 覆盖索引 二. 哈希索引三. 全文索引 前言 InnoDB存储引擎支持以下几种常见索引:BTree索引,哈希索引,全文索引 一. B Tree 索引…...
Neutralinojs教程项目实战初体验(踩坑指南),干翻 electron
Neutralinojs 项目实战初体验(踩坑指南),干翻 electron Neutralinojs 官方文档 卧槽卧槽,!这个年轻人居然用浏览器把电脑关机了_哔哩哔哩_bilibili正是在下 本教程搭建的是纯原生项目,没有和其它前端框架…...
【轻松拿捏】Java-List、Set、Map 之间的区别是什么?
List、Set、Map 之间的区别是什么? 一、List 二、Set 三、Map 🎈边走、边悟🎈迟早会好 一、List 有序性:List 保持元素的插入顺序,即元素按添加的顺序存储和访问。允许重复:List 可以包含重复的元素。…...
用户史订单查询业务
文章目录 概要整体架构流程技术细节小结 概要 在电商、金融、物流等行业中,用户历史订单查询是一项常见的业务需求。这项功能允许用户查看他们过去的交易记录,包括但不限于购买的商品、服务详情、交易金额、支付状态、配送信息等。对于企业而言…...
第8节课:CSS布局与样式——掌握盒模型与定位的艺术
目录 盒模型:网页布局的基础盒模型的属性盒模型的示例 定位:控制元素位置定位的类型定位的示例 实践:使用CSS布局创建响应式网页结语 CSS布局是网页设计中的基石,它决定了网页元素的排列和分布。盒模型和定位是CSS布局中的两个核心…...
electron 主进程和渲染进程
最近在整理electron 相关的项目问题,对自己来说也是温故知新,也希望能对小伙伴们有所帮助,大家共同努力共同进步。加油!!!! 虽然最近一年前端大环境不好,但是大家还是要加油鸭&#…...
redis的高可用及性能管理和雪崩
redis的高可用 redis当中,高可用概念更宽泛一些。 除了正常服务以外,数据量的扩容,数据安全。 实现高可用的方式: 1、持久化 最简单的高可用方法,主要功能就是备份数据。 把内存当中的数据保存到硬盘当中。 2、主…...
php基础语法
文章目录 1. PHP(1) 安装php 2. 基础语法(1) 格式(2) 输出语法(3) 注释(4) 变量(无变量类型自动识别)(5) 输入获取(6) 定界符(7) 换行 3. 基本数据类型(1) 字符串(2) 整数(3). 浮点数(4). boolean类型(5). 数组(6). null值 4. 运算符(1) 算术运算符(2) 比较运算符(3) 逻辑运算符…...
js抓取短信验证码发送
油猴(Tampermonkey)是一个流行的浏览器扩展,它允许用户在浏览器中运行自定义的JavaScript脚本。下面是一个简单的示例脚本,用于收集网站上发送短信验证码的API请求,并以JSON格式存储这些信息。请注意,这个脚本需要根据实际网站的API请求进行调整,因为不同的网站可能有不…...
视频怎么加密?常见的四种视频加密方法和软件
视频加密是一种重要的技术手段,用于保护视频内容不被未经授权的用户获取、复制、修改或传播。在加密过程中,安企神软件作为一种专业的加密工具,可以发挥重要作用。 以下将详细介绍如何使用安企神软件对视频进行加密,并探讨视频加密…...
聚焦全局应用可用性的提升策略,详解GLSB是什么
伴随互联网的快速发展和全球化趋势的深入,企业对网络应用的需求日渐增长。为满足全球范围内用户大量的访问需求,同时解决容灾、用户就近访问以及全球应用交付等问题,GLSB(全局负载均衡)也因此应运而生。那么GLSB是什么…...
无水印下载视频2——基于tkinter完成头条视频的下载
在数字化时代的浪潮中,视频内容以其丰富性和便捷性,逐渐成为了我们获取信息和娱乐的重要途径。尤其是在短视频平台上,各种创意十足、内容精彩的视频层出不穷,更是吸引了数以亿计的用户。然而,随着视频内容的增加&#…...
Java学习Day17:基础篇7
继承 Java中的继承是面向对象编程中的一个核心概念,它允许我们定义一个类(称为子类或派生类)来继承另一个类(称为父类或基类)的属性和方法。继承提高了代码的复用性,使得我们不必从头开始编写所有的代码&a…...
Vue3 Pinia的创建与使用代替Vuex 全局数据共享 同步异步
介绍 提供跨组件和页面的共享状态能力,作为Vuex的替代品,专为Vue3设计的状态管理库。 Vuex:在Vuex中,更改状态必须通过Mutation或Action完成,手动触发更新。Pinia:Pinia的状态是响应式的,当状…...
手撕数据结构02--二分搜索(附源码)
一、理论基础 二分搜索,也称折半搜索、对数搜索,是一种在有序数组中查找某一特定元素的搜索算法。 二分搜索是一种高效的查找算法,适用于在已排序的数组中查找特定元素。它的基本思想是通过不断将搜索区间对半分割,从而快速缩小…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...
