C优化篇之减少运算量

(编程实用技巧)如何减少内存占用和提高运算速度?(C语言) 减少C语言程序内存占用可以从以下几个方面入手:1. **合理选择数据类型**:- 根据实际需求选择适当的数据类型,避免过大类型造成不必要的内存消耗。例如,如果只需要表示较小的整数,可以使用`uint8_t`而非`int`。2. **内存对齐和结构体优化**:- 注意结构体成员的排列顺序,减少内存对齐带来的填充字节。合理安排结构体中各成员的顺序和大小,以减小整体结构体大小。3. **位段(Bitfields)**:- 如果需要存储一组布尔标志,可以考虑使用位段来将多个标志打包到一个整数中。 阅读详情

    程序优化的另一个出发点是减少运行过程中的运算量,有两个大的思路:

    1)把部分计算量转移到离线,或者说把一部分工作挪到程序之外,人为处理,以减轻程序本身压力。比如查表、浮点转定点以及其他数学算法的优化等。

    2)分析和剔除代码中的多余水分,由于编译器能把一些简单的无效语句剔除,所以程序员可以做文章的地方一般就是循环体。

查表

   有些算法输入有限离散整数,输出固定的数据集合,这种模块本质只是提供一个有限数据集或者说常量数组,在程序里现场计算完全是浪费CPU资源,完全可以事先算好,形成数据表放在内存数据区,运行中只要查表而无需计算,是一种空间换时间的策略。如:

    long factorial(int i)

    {

      if (i == 0) {  return 1;  }

      else {  return  i * factorial(i - 1);  }

    }

    新代码:

    static long factorial_table[] = {1, 1, 2, 6, 24, 120, 720  /* etc */ };

    long factorial(int i)

    {

      return factorial_table[i];

    }

浮点转定点

    很多CPU没有专门针对浮点运算的硬件,而是通过软件库模拟浮点运算,效率非常低。如果程序中有大量浮点运算,应该用定点替代。基本原理是,确定某运算模块的浮点输入数据集的范围,在此基础上缩放2Q,把浮点数缩放为定点整数,然后进行整数运算,之后再把结果重新转换回浮点。简单说:

    float Mod1(float x, float y) 

    {

       float z;

        //系列x,y的浮点运算,得到结果z 

       return z;

    }

    假设x,y 的数据集范围都在[0.125,1 ],函数可大体改成

    float Mod1(float x, float y)

    {

       int ix =(int)x*8;

       int iy=(int)y*8;

       int iz;

       //系列 ix,iy的整数运算得到结果iz

      return (float )iz/float(8*8);

    }

    这一过程的原理在DSP资料中有详细论述,关键在于为不同模块的浮点运算确定合适的小数点变换位置,使变换后的运算既不溢出整数范围,又能保持足够精度,即所谓合理定标。这部分内容大家可自行查阅参考相关资料☺。

循环优化

    循环往往是profiler工具标识的程序计算集中的“热点”,因而是软件优化的重点对象,着眼点包括:

a. 优化计数器访问

    C的while /for循环中都需要一个计数器(counter),这个counter最好设计成递减到零,而不要用递增计数,两个原因:一是与0做比较的判断指令在某些芯片(ARM)中附加在算术运算指令之后,合2条指令为1条,这样每次递减循环少用一条判断指令(参考ARM汇编);二是递减循环不必保存counter的最大极限值,如果这个极限值是一个变量,那么用递增计数就要多占用一个寄存器。

b.把循环内的重复运算提取到循环之外

    仔细观察,把循环内部确定的分支或计算提到循环外,以消除重复,如:

    for(i=0; i<MAX; i++){

      if (n== 0)    a(i) = a(i) + b(i) * c ;

      else    a(i) = 0;

    }

    这里n是否为0的判断和循环内其它运算没有关系,没必要每次重复判断,可改为:

    if (n == 0) {

      for(i = 0;i < MAX; i++)  a(i) = a(i) + b(i) * c;

    }else{

      for(i= 0;i <k; i++)   a(i) = 0 ;

    }

    代码似乎多了,但执行效率要高,再比如:

    int GetCRC(char *instr)

    {

      int a;

      int x = -1;

      for(a = 0; a<strlen(instr);a++)    {      x+=*(int*)((int)instr+a);      }

      return x;

    }

    strlen是一个函数,编译器根据已有条件并不知道strlen结果始终不变,所以会笨笨的重复计算。很明显应增加一个局部变量int b,在for循环前计算b=strlen(instr),把循环变为for(a=0;a<b;a++)。消除这类重复计算,既提升代码的质量,也为低碳环保,绿色地球做出了贡献:)

c.循环展开

    循环展开可以减小循环次数,降低循环判断的开销。如:    for(i=0; i<100; i++) {   temp = temp*(array[i]);  }

   这个循环内部运算很简单,但每次i<100的判断必不可少,这样循环中很大比例的指令消耗在循环结束条件的判断上,因此可以展开这个循环,变为:

    temp = temp*(array[0]);

    ......

    temp = temp*(array[99]);

    这样展开虽然看上去把原来两句话增加到100句,但实际可以减少100条判断指令。

    再举例有一幅24位真色彩图像,每像素包含R、G和B三分量,RGB分量各占8位,在0~255间取值。下面函数实现图像取反色,即每像素每分量都被255减。

    void NegPixel(Uint8* InPixel, Uint8 *OutPixel, int Width, int Height)

    {

      int sum = Width * Height * 3;

      for(int i=0;i<sum;i++)  {   OutPixel[i]=255-InPixel[i];   }

    }

   循环体中的i<sum判断占了相当比例,于是展开:

    void NegPixel (Uint8 * InPixel, Uint8 * OutPixel,int Width, int Height)

    {

      int sum = Width * Height ;

      for(int i =0;i<sum;i+=3)

      {

        OutPixel[i]=255-InPixel[i];

        OutPixel[i+1]=255-InPixel[i+1];

        OutPixel[i+2]=255-InPixel[i+2];

      }

    }

    循环次数变为原来三分之一,减少了2*sum/3个条件判断指令,这种部分展开能兼顾优化和代码密度。那如果循环次数是变量或某素数,怎么部分展开呢?首先确保第一次循环不超过数组边界,如原先循环长度为n,展开3次,可将循环限制设为n-2,使循环体内数组最大索引不超过数组长度n。如:

    void function(int array[],int *dest, int len)

    {

      int temp=1;

      for(int i=0;i<len;i++){        temp=temp*(array[i]);       }

      *dest = temp;

    }

    展开为

    void function(int array[],int *dest, int len)

    {

      int temp=1;

      int limit=len-2;

      for(int i=0;i<limit;i+=3){    temp=temp*(array[i])*(array[i+1])*(array[i+2]);     }

      for(; i < len; i++){    temp = temp * array[i];     }

      *dest = temp;

    }

    所以如果循环展开k次,就把上限设为n-k+1,最大循环索引将会比n小,最后再处理掉余下的最后几个元素。

    不过天下没有免费的午餐,循环展开固然能降低分支判断的开销,却会增加代码量,所以最优展开位置要具体分析。且还要注意,在有指令cache的CPU上,如果循环展开后代码超出cache line,展开代码可能导致cache miss,因此这时展开循环有可能反而变慢。另外一些编译器在优化级别足够高时会自动展开循环。所以是否要手工展开也要具体情况具体分析。

一些C代码优化方法 指针方法的优点是,array的地址每次装入地址p后,在每次循环中只需对p增量操作。在数组索引方法中,每次循环中都必须根据t值求数组下标的复杂运算。 如果表很大,不好写,就写一个init函数,在循环外临时生成表格。 (2)、求余运算 a=a%8; 可以改为: a=a&7; 说明:位操作只需一个指令周期即可完成,而大部分的C编译器的“%”运算均是调用子程序来完成,代码长、执行速度慢。通常,只要求是求2n方的余数,均可使用位操作的方法来代替。 (3)、平方运算 a=pow(a, 2.0); 可以改为: a=aa; 阅读详情

相关推荐

OJ1061: 顺序输出各位数字(C语言)(谈程序优化问题及pow函数耗时问题)

题目描述 输入一个整数,从高位开始逐位分割并输出各位数字。 输入 输入一个正整数n,n是int型数据 输出 依次输出各位上的数字,每一个数字后面有一个空格,输出占一行。例如,输入 12345 ,输出 1 2 3 4 5 样例输入 Copy 12345 样例输出 Copy 1 2 3 4 5 提示 注意整数运算避免使用double类型的函数如pow()。 本题可先用一个循环计算出最高位的位权h,然后再用一个循环,循环内容为: 输出最高位(n/h)、扔掉最高位(n = n%h)、降低最高位位权(h = h/1

weixin_51770717的博客 2354

C语言程序设计之四种方法求n的阶乘

C语言程序设计之四种方法求n的阶乘

qq_53481973的博客 4万+

【nn.Conv3d】三维卷积参数量与运算量:从公式推导到代码验证

本文深入解析了PyTorch中nn.Conv3d的三维卷积参数量与运算量计算方法,通过公式推导和代码验证详细展示了如何计算和优化模型参数。文章结合视频处理等实际应用场景,提供了参数量计算公式、运算量(FLOPs)分析方法以及性能优化实战建议,帮助开发者高效处理三维数据并优化模型性能。

weixin_33207473的博客 169

11个教程中部常被提及的JavaScript小技巧

1、过滤唯一值 Set类型是在ES6中新增的,它类似于数组,但是成员的值都是唯一的,没有重复的值。结合扩展运算符(…)我们可以创建一个新的数组,达到过滤原数组重复值的功能。 const array = [1, 2, 3, 3, 5, 5, 1]; const uniqueArray = [...new Set(array)]; console.log(uniqueArray); // [1, 2...

liaoweiping 280

C语言代码优化规范及常见错误举例

如何优化C语言代码(程序员必读) 1、选择合适的算法和数据结构       应该熟悉算法语言,知道各种算法的优缺点,具体资料请参见相应的参考资料,有很多计算机书籍上都有介绍。将比较慢的顺序查找法用较快的二分查找或乱序查找法代替,插入排序或冒泡排序法用快速排序、合并排序或根排序代替,都可以大大提高程序执行的效率。.选择一种合适的数据结构也很重要,比如你在一堆随机存放的数中使用了大量的插入和

小石头 3349

c语言 pow优化_C语言性能优化

1、基本优化(1)全局变量全局变量绝不会位于寄存器中。使用指针或者函数调用,可以直接修改全局变量的值。因此,编译器不能将全局变量的值缓存在寄存器中,但这在使用全局变量时便需要额外的(常常是不必要的)读取和存储。所以,在重要的循环中我们不建议使用全局变量。如果函数过多的使用全局变量,比较好的做法是拷贝全局变量的值到局部变量,这样它才可以存放在寄存器。这种方法仅仅适用于全局变量不会被我们调用的任意函数...

weixin_34805229的博客 1453

c语言程序循环运行速度优化方案

c语言程序循环运行速度优化方案

CCthinker的博客 3031

C语言代码优化方案(深度好文)

转自公号:嵌入式云IOT技术圈1、选择合适的算法和数据结构选择一种合适的数据结构很重要,如果在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多。数组与指针语句具有十分密切...

编程与实战的博客 2251

三维卷积实战解析:从参数量与运算量看nn.Conv3d的设计与优化

本文深入解析了三维卷积(nn.Conv3d)的设计与优化,重点探讨了参数量和运算量的计算方法及优化策略。通过实际案例和对比实验,展示了如何在不同应用场景中平衡模型性能和计算效率,为视频分析和动作识别等任务提供实用技巧。

weixin_29029995的博客 151

log函数快速计算c语言程序

log函数的快速计算,很有用,希望为您服务

Mac上IDEA的PlantUML插件报错‘找不到Graphviz’?手把手教你用Homebrew搞定(附阿里云镜像避坑)

本文详细解决了Mac上IDEA的PlantUML插件报错‘找不到Graphviz’的问题,通过Homebrew安装Graphviz并配置阿里云镜像源,特别针对Apple Silicon芯片用户提供了优化方案。文章还涵盖了IDEA中PlantUML插件的正确配置和常见问题排查,帮助开发者高效使用PlantUML进行UML图表设计。

weixin_42619742的博客 468

嵌入式C代码的十种优化方案

摘要:代码的优化是有侧重点的,优化是一门平衡的艺术,它往往要以牺牲程序的可读性或者增加代码长度为代价。在嵌入式开发中对程序执行速度的要求比较高,所以学好做好代码优化可以让你的代码执行效率更高。 一、选择合适的算法和数据结构 选择一种合适的数据结构很重要,如果在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多。数组与指针语句具有十分密切的关系,一般来说,指针比较灵活简洁,而数组则比较直观,容易理解。对于大部分的编译器,使用指针比使用数组生成的代码更短,执行效率更高。 在许多种情况下,可以用指针

果果小师弟的博客 3944

告别手动群发!用IDA Pro和CE搞定企业微信4.1.38.6011的HOOK开发(附完整代码)

本文详细介绍了如何利用IDA Pro和Cheat Engine进行企业微信4.1.38.6011的HOOK开发,实现自动化群发功能。从逆向工程环境配置、关键函数定位到HOOK框架设计,提供完整代码示例,帮助企业提升运营效率。

weixin_42533120的博客 479

C语言代码优化11种实用方法

点击蓝字关注我1、选择合适的算法和数据结构选择一种合适的数据结构很重要,如果在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多。数组与指针语句具有十分密切的关系,一般来说,指针比较灵活简洁,而数组则比较直观,容易理解。对于大部分的编译器,使用指针比使用数组生成的代码更短,执行效率更高。在许多种情况下,可以用指针运算代替数组索引,这样做常常能产生又快又短的代...

轻松学C语言 1647

ubuntu20.04安装UE4+Airsim环境

版本UE4.27+Airsim1.81

玖茸的博客 3380

Exposure Fusion

MertensTMO原理详解: 文章:2007年Exposure Fusion 论文链接:http://ntp-0.cs.ucl.ac.uk/staff/j.kautz/publications/exposure_fusion.pdf 论文代码:C版本:https://github.com/JawThrow/ExposureFusion python版本:https://github.com/lzocro/Exposure-Fusion matlab版本:https://github.com/Mericam

大长腿不解释 1652

C代码优化方案

1、选择合适的算法和数据结构选择一种合适的数据结构很重要,如果在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多。数组与指针语句具有十分密切的关系,一般来说,指针比较灵活简洁,而数组则比较直观,容易理解。对于大部分的编译器,使用指针比使用数组生成的代码更短,执行效率更高。在许多种情况下,可以用指针运算代替数组索引,这样做常常能产生又快又短的代码。与数组索引相比,指针一般能使代码速度

MetalSeed 2224

python-pptx库中文文档及使用样例

个人使用样例及部分翻译自官方文档,并详细介绍chart的使用

4万+

C语言判断一个数是否为素数(质数),C语言经典例题计算素数,C语言二级重点

素数又称质数。所谓素数是指除了 1 和它本身以外,不能被任何整数整除的数,例如29就是素数,因为它不能被 2~28的任一整数整除。 (思路1):因此判断一个整数m是否是素数,只需把 m 被 2 ~ m-1 之间的每一个整数去除,如果都不能被整除,那么 m 就是一个素数。 (思路2):另外判断方法还可以简化。m 不必被 2 ~ m-1 之间的每一个整数去除,只需被 2 ~ 之间的每一个整数去除就可以了。如果 m 不能被 2 ~ 间任一整数整除,m 必定是素数。例如判别 17 是是否为素数,只需使 1...

Eliauk1234的博客 7万+

芒果YOLOv7改进48:主干篇:独家首发改进:目标检测新范式骨干PPHGNetv2改进版,充分融合信息特征,提升YOLOv7检测能力

YOLOv7代码改进实践 | 独家首发改进 PPHGNetv2 改进版结构

包括YOLOv5、YOLOv7、YOLOv8等模型改进 1849

Linux系统安全学习手册

Linux反弹shell Linux系统应急响应 Linux服务器安全加固 Linux下的用户、组和权限 Linux中的gcc Linux中su和sudo的用法和区别 Linux系统python2与python3共存 Linux中grep工具的使用 Linux中环境变量的设置 Linux中常见的150个命令(干货) Linux中编写Shell脚本 LNMP环境搭建Wordpress博客 LAMP环境搭建一个Discuz论坛 Linux运维比较常用的一些脚本 Linux系统中安装软

谢公子的博客 7532

如何优化C语言代码

1、选择合适的算法和数据结构 应该熟悉算法语言,知道各种算法的优缺点,具体资料请参见相应的参考资料,有很多计算机书籍上都有介绍。将比较慢的顺序查找法用较快的二分查找或乱序查找法代替,插入排序或冒泡排序法用快速排序、合并排序或根排序代替,都可以大大提高程序执行的效率。.选择一种合适的数据结构也很重要,比如你在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多。 数组与指针语句具有十分密

liuxp_008的专栏 890

【vsomeip3 教程】基于 vSomeIP 3.5 的路由域实践:从配置到运行状态的完整指南

___ > 前言(读者定位与准备) 目标读者:在 Linux 上做分布式/车载中间件通信的 C++ 工程师。 > 先决条件:CMake、Boost 基础;理解 TCP/UDP 与多进程通信。 > 文中术语:Routing Host(路由进程)、Application、Service/Instance、Method、Event/EventGroup、SOME/IP-SD。 > 演示环境:单机多进程 & 跨主机两套对照。

探索C++编程的奥秘,分享深入的技术见解和实践,旨在激发读者创造力与解决问题的思维。 1160

C语言编程优化运行速度

1、选择合适的算法和数据结构 选择一种合适的数据结构很重要,如果在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多。数组与指针语句具有十分密切的关系,一般来说,指针比较灵活简洁,而数组则比较直观,容易理解。对于大部分的编译器,使用指针比使用数组生成的代码更短,执行效率更高。 在许多种情况下,可以用指针运算代替数组索引,这样做常常能产生又快又短的代码。与数组索引相比,指针一般能使代

计算机视觉小菜鸟的专栏 6600

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

C语言程序设计——算法优化

相信大多数计算机学者或从事与之相关工作的朋友都听过这么一句话——算法是程序的灵魂。从这句话中我们就可以看到算法在编程中的重要性。 那么算法到底是什么呢? 简单来说,算法(Algorithm)是指完成一个任务所需要的具体步骤和方法。也就是说给定初始状态或输入数据,能够得出所要求或期望的终止状态或输出数据。 在一个C语言程序中,一个好的算法可以提高程序运行的效率,尽可能的使用少的硬件资源而完成相同的目的。今天就给大家分享一下怎样写出好的算法,怎么优化算法,提高程序运行的效率。 算法的优化分为全局优化和局部优化两

Yearn*adn的博客 2813

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

C语言中的优化技巧总结

优化技巧

lushij的博客 1008

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

上一篇: 硬件篇之MMU
下一篇: 硬件篇之字节序
ipmux
博客等级 码龄24年 161粉丝 65原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值