当前位置: 首页 > news >正文

同事用“与运算“改了这几行代码,运行效率直接起飞~


正文


大家好,我是bug菌~

1

问题背景

最近由于项目指标的需求,查了下程序各个部分的运行效率,发现一直用的环形缓冲区在耗时占比中还挺突出,于是过了一遍代码并尝试着去优化一下,没想到改动不大却得到了较大的效率提升。

如下是之前环形缓冲区的一些代码片段:

#define BUFFER_SIZE 512 uint16_t buffer[BUFFER_SIZE]; uint16_t index = 0; ...... index = (index + 1) % BUFFER_SIZE; ......

当程序高频率的调用含有取模的运算接口时执行时间超出了设计预期,同时在低优化等级(毕竟如果编译器进行了各种优化,那就不好聊下去了)下对取模运算进行了相关sysclock的测量,确实也是效率不高,于是我打算用更高效的运算方式把它替换掉。

2

与运算代替取模

当然了,与运算至少全面替代取模运算没那么容易,毕竟如果能够完全替代,也不会有人用取模了,当时对于嵌入式行业我觉得最有意思的是它并不需要非常的通用,嵌入式只需要在特定的领域,特定的工况下能做到极致就可以了,有取舍才能在有限的资源下把平台充分利用起来。

同样的思路取模运算确实很强大,但是我并不需要利用它所覆盖的方方面面,所以当除数是2的幂(即n = 2^k)时,与运算同样可以满足我的需求:

// 当 n 是 2 的幂(n = 2^k)时 a % n = a & (n - 1) // 等价的情况(n是2的幂) a % 8 == a & 7 // 8 = 2^3 a % 16 == a & 15 // 16 = 2^4 a % 32 == a & 31 // 32 = 2^5

我们知道% 运算通常需要除法指令,开销较大,而& 运算只需要按位与,速度快很多。

所以对应环形缓存区只需要优化下:

#define BUFFER_SIZE 512 // 必须为2的幂 #define BUFFER_MASK (BUFFER_SIZE - 1) // 511 = 0x1FF uint16_t buffer[BUFFER_SIZE]; uint16_t index = 0; ...... index = (index + 1) & BUFFER_MASK; // 快速回绕 ......

3

再细致一点

聊到这里,来龙去脉应该讲清楚了,其实不管了是在这一次的环形缓存区的优化中有所感悟这种方法,只要是在当除数是2的幂时这种方式都能大大提高效率,特别是一些实时性应用场景,一通百通。

比如说你要进行ADC窗口滑动:

samples[sample_index] = adc_read(); ...... // sample_index = (sample_index + 1) % WINDOW_SIZE; //直接方式 sample_index = (sample_index + 1) & WINDOW_MASK; ......

一些限制和风险我们也要非常有数,一些bug大部分都是因为我们没有提前想到:

1、在性能关键路径且除数是2的幂时,才考虑使用与运算替代取模运算,其他地方其实无关痛痒也没必要替换,所以可以做一些防御性检测:

#ifndef IS_POWER_OF_TWO #define IS_POWER_OF_TWO(x) (((x) & ((x) - 1)) == 0) #endif #define QUEUE_SIZE 128 #if !IS_POWER_OF_TWO(QUEUE_SIZE) #error "QUEUE_SIZE must be power of two for optimization" #endif #define QUEUE_MASK (QUEUE_SIZE - 1)

2、如果是处理负数大概率会出问题,要留意。

最后

好了,今天就跟大家分享这么多了,如果你觉得有所收获,一定记得点个~

唯一、永久、免费嵌入式技术知识分享平台

推荐专辑 点击蓝色字体即可跳转

MCU进阶专辑

嵌入式C语言进阶专辑

“bug说”专辑

专辑|Linux应用程序编程大全

专辑|学点网络知识

专辑|手撕C语言

专辑|手撕C++语言

专辑|经验分享

专辑|电能控制技术

专辑 | 从单片机到Linux

http://www.jsqmd.com/news/273041/

相关文章:

  • 亲测好用!专科生毕业论文TOP10 AI论文平台测评
  • java 环境配置(详细教程)
  • 深入 TCP 核心:握手、挥手、滑动窗口与并发服务器实战
  • 10个秘密:软件开发者的晋升路径大揭秘
  • 不仅仅是浏览器渲染:揭秘 Botasaurus 高效的 HTTP 请求封装
  • 如何打开命令提示符
  • 网络安全学习路线图:从零基础到全栈工程师
  • 如何避免职业倦怠:软件测试工程师的终极自救手册
  • nginx的JavaScript魔力:njs简介与实践
  • 网络安全学习路线图(2026版详解)
  • 建站知识:域名/ 空间/ IP/ 端口之间的关系
  • 【全网最全】网络安全自学路线图:从零到专家,附资源清单与避坑指南
  • 2026年职业蓝图:从码农到CTO的加速计划
  • 耐达讯自动化Profibus总线光纤中继器在连接测距仪中的应用
  • 精:C# 的四种委托实现详解
  • 震惊!90%的AI从业者忽略了这些职业发展技巧
  • 国产DevSecOps工具崛起:安全左移战略下的技术创新与市场重构
  • 耐达讯自动化Profibus总线光纤中继器:光伏逆变器通讯的“稳定纽带”
  • ‌软件测试员如何年薪翻倍的5个策略
  • 多商户+多端一体化:商城系统的核心功能架构与源码实现
  • AI技术分析股票财报
  • 开源商用级的多端多商户万能DIY商城系统全功能源码指南
  • 5个步骤:从软件测试新手到行业专家的2026指南
  • 全维度拆解!长沙AI搜索优化公司实力排行 - 亿仁imc
  • 2026年天津婚姻纠纷律师联系电话推荐:高效解决婚姻难题 - 品牌推荐
  • GPT-5.2 与 Sora 2 震撼发布:Python 开发者如何用“上帝视角”构建下一代多模态应用?(附全链路源码)
  • 为什么AI开发者必须规划职业转型?2026年生存法则
  • 护眼台灯怎么选?7款实测对比推荐—学生护眼闭眼入专业读写榜首 - 资讯焦点
  • 如何为不同拍摄场景选存储卡?2026年CFexpress B卡品牌全面评测与推荐 - 品牌推荐
  • 2026年天津继承诉讼律师电话推荐:解决继承纠纷指南 - 品牌推荐