浅谈C\C++代码优化中的一些小技巧

c++中atan精度不够的情况如何处理 atan函数在遇到较大的值时不能返回一个精确的值,从而导致转换为角度后失真的解决办法 阅读详情

转自:http://blog.youkuaiyun.com/connor_lele/article/details/25566855

编写C\C++程序快两年,对于代码优化有很深的感触。个人认为,代码优化是一门很深的学问,而作为码农,我们总是在实践中不断更新自己对它的认识和理解。对于这个很大很深的问题,我只是根据自己的所学所见以及所悟来谈谈代码优化中的效率问题,希望对初学者有一些帮助,如有不对的地方还望指正。

一、定义

所谓代码优化是指在不改变程序运行结果的前提下,使最终生成的目标代码的运行时间更短,时空效率得到优化。这里,我结合自己的图像处理方面的一些经历谈谈如何如何减少运行时间、提高运行效率。个人认为主要包括硬件加速和软件加速两个方面。

二、硬件加速

顾名思义,硬件加速指的是充分利用计算机现有的的硬件资源编写代码,进而提高代码的运行效率。据我所知,常见的适合计算机的硬件加速方法有:GPU,OpenMP以及SSE。

GPU

GPU(Graphic Processing Unit)是图像处理器,它是显卡的大脑,早期主要用于电脑的2D\3D图形计算。随着科技的发展,GPU开始在并行计算等方面得到广泛的应用,其优势在于它包含很多小的独立处理单元,这些处理单元可以并行运算。这一特点决定了GPU相对于CPU而言在并行数据的处理上具有更大的优势,如图像处理中的计算梯度、反转灰度等运算。

关于GPU编程,首先需要硬件上的支持,即计算机需要有独立显卡,其次需要软件上的支持,即编程环境以及相应的GPU接口,如VS2008和NVIDIA的CUDA。CUDA是NVIDIA推出的一个GPU运算平台,我们可以理解API。一般而言,如果我们的电脑有独立显卡,在安装显卡驱动、CUDA开发包以及编程环境(如VS2008)之后,我们便可以在相应的工程项目中嵌入.cu格式的文件用于GPU编程。有关GPU编程的详细过程这里就不详细叙述了,感兴趣的同学可以参考CUDA提供的用户手册以及《GPU高性能编程CUDA实战》一书,前者比较详细,后者比较易懂。

OpenMP

百度百科的定义:OpenMP是由OpenMP Architecture Review Board牵头提出的,并已被广泛接受的,用于共享内存并行系统的多线程程序设计的一套指导性的编译处理方案(Compiler Directive),支持语言包括C语言、C++和Fortran等,适合并行处理的数据。

在VS2008下面使用OpenMP比较简单,我们需要将项目》配置属性》c/C++》语言下的OpenMP支持打开,并在工程项目中包含omp.h头文件,这样所建的项目便支持OpenMP了。

SSE

SSE(Streaming SIMD Extensions,单指令多数据流扩展)指令集是Intel在Pentium III处理器中率先推出的。由SSE部分和MMX部分组成。SSE部分负责处理浮点数,MMX 部分专门计算整数。SSE部分可以同时处理四个浮点数。VS2008下需要包含emmintrin.h头文件。举个例子,如需要计算向量中每个元素相乘的结果,其运算速度是原始方法的4倍。需要注意的是,SSE需要数组的首地址是16的整数倍。

  • 原始方法

    for(int i = 0; i < nSize; i++)
    {
        *pResult = (*pArray1)*(*pArray2);
        pArray1++; pArray2++; pResult++;
    }
  • SSE方法
    __m128 *p1 = (__m128*)pArray1;
    __m128 *p2 = (__m128*)pArray2;
    __m128 *p3 = (__m128*)pResult;
    for(int i = 0; i < nSize; i++)
    {
        *p3 = _mm_mul_ps(p1,p2);
        p1++; p2++; p3++;
    }

三、软件加速

个人认为,所谓的软件加速是值用最小的计算代价达到所需的目标,一般而言,软件加速需要对语言本身比较熟悉,同时需要了解相关问题的快速算法,这样才能够写出高效率的算法。这里,我结合自己写代码过程的体会从三个方面介绍一下软件加速的几种方法,包括变量、函数等方面的精简,去除冗余代码以及使用快速算法等。

变量、函数等方面的精简
  • 用尽量小的数据类型

    能用bool型变量不用char型;能用char型变量不用int型,虽然有时不同数据类型的运算速度可能相同;如,在表示图像数据时,由于图像数据一般为0-255之间,对其进行的大部分运算也都在0-255之间,这时我们只需使用uchar型即可。

  • 减少运算强度

    尽量使用移位、取余运算代替乘法、除法等; 如b=a/4可替代为b=a>>2,b=a%4可替代为b=a&3,b=3*a可替代为b=a<<1+a。

    避免不必要的整数除法; 如avg=sum/w/h可替代为avg=sum/(w*h)。

    使用增量和减量操作符; 如a=a+1可替代为a++。

  • 循环优化

    拆解小循环,即不要将一些可列举的情况写成循环,特别是该小循环嵌套在大循环中情况。

    switch语句中概率较大的情况放在靠前的位置。

  • 使用内联函数

去除冗余代码

所谓的去除冗余代码是指程序在不知不觉中进行了很多重复的运算,而这些重复运算其实可以通过巧妙的安排进行避免。这里说两个图像处理中常见的方法:查找表和积分图。

  • 查找表

    所谓的查找表比较通俗的说法就是已经赋好值的数组。当我们在程序中需要用到查找表中的某种情况时,只需要调用相应的数组元素即可,如计算图像的梯度。正常情况下,我们计算完一个像素点水平方向和垂直方向的差值dx和dy之后,通过公式sqrt(dx*dx+dy*dy)和atan2(dy,dx)便可分别得到梯度的幅值和方向。但是,这种方法存在很大的冗余,它需要频繁地调用开方函数和反正切函数,而这个代价是很大的。仔细想想,由于像素值属于[0,255],那么dx和dy便属于[-255,255],其情况是可列举的。如果我们把所有(dx,dy)对应的梯度的幅值和方向提前计算好,并放到一个数组构成查找表,那么当给定一个像素点的dx和dy,我们只需要索引到其对应的数组元素即可。相对于原始方法而言,这样能够大大地减少运算量,提高代码效率。

  • 积分图

    积分图指计算图像中每个像素点对应的左上角像素值之和。最直观的方法可能是依次遍历每个像素点,然后计算该像素点左上角的像素值之和。这种方法虽然简单、直观,但是计算复杂,没有利用当前已有像素点和的信息。这里,给一种简单,有效的方法,它充分利用了当前已有像素点和的信息。

    for(i = 0; i < w; i++)
    {
        offset = i;
        pResult[offset] = (int)data[offset];
        for(j = 1; j < h; j++)
        {
            offset += w;
            pResult[offset] = pResult[offset-w]+(int)data[offset];
        }
    }
    for(j = 0; j < h; j++)
    {
        offset = j*w;
        pResult[offset] = (int)data[offset];
        for(i = 1; i < w; i++)
        {
            offset++;
            pResult[offset] += (int)data[offset];
        }
    }

    可见,积分图的计算是有技巧的。那么积分图有什么用了,其实积分图的出现本身也是为了加快一些问题的运算速度,如大量计算图像中局部区域的像素和。

使用已有的快速算法

许多问题(例如查找、排序以及傅里叶变换等)其实在学术界都有相应的快速算法,我们所需要做的其实就是搜索,常见问题的快速算法可能baidu一下就能搞定,对于一些特定的问题,我们可能需要google出相应的论文,并根据论文思想编写相应的快速算法。下面简要地列举查找、排序以及傅里叶变换等问题的快速算法。

  • 查找

    包括二分查找、分块查找以及哈希查找等。

  • 排序

    包括快速排序、堆排序以及归并排序等。

  • 傅里叶变换

    可以用fft实现,目前比较最快的fft算法开发包应该是fftw,matlab应该用的就是这个开发包。

总结

个人感觉硬件加速相对而言简单,对于个人能力要求相对于较低,但是可能需要一定的硬件成本,而软件加速需要时间和知识的积累,对于个人能力要求较高,但是不需要烧钱。至于效果,硬件加速和软件加速属于两种不同方法,我们需要根据实际情况判断那种方法更适合,例如,很多时候我们花费了很大的精力进行代码优化后的速度还比不上简单GPU加速后的速度。

实战篇 | 22 C++ 高性能计算—运算加速 小知识:如何取存在数组中的元素指针 怎么样处理需要放在一个容器里放在元素的指针,那个指针该怎么取出来? 我们先来看一段代码 咋看起来没什么问题,可是当程序运行之后,你会发现输出来的东西完全不是你想用的,为什么 在上面这个for循环中,我创建了一个局部变量student,然后我把这个student push到一个vector里,这其实是一次拷贝。然后我把这个指针的地址存到另一个v... 阅读详情

相关推荐

C语言执行效率如何保证,看这一文就够了!

来自公众号:嵌入式ARM嵌入式开发基本都会选择C语言这是因为C语言有出色的可移植性能在多种不同体系结构的软/硬平台上运行虽然代码的复用性差代码的维护性差扩展性很差但,C语言简洁紧凑使用灵...

编程与实战的博客 8439

螺旋方阵

#include<stdio.h> int dx[] = {0, 1, 0, -1},dy[] = {1, 0, -1, 0}; //用于进行数组方向的判断,也就是数组变化时相应的值的变化 int res[105][105]; int main() { int n; scanf("%d", &n); int x = 0, y = 0, d = 0; //(x,y)表示当前坐标,d是进行方向切换的开关 for(int i = 0; i < n

weixin_52530507的博客 460

配置 NVIDIA RTX 5090 + sm_120 + flashattention,已跑通一个大模型 ~~

本文介绍了在Ubuntu 22.04系统下为NVIDIA RTX 50系显卡搭建深度学习环境的完整流程。重点包括:1)安装适配50系显卡(sm_120架构)的CUDA 12.9驱动和Toolkit,需注意禁用系统自带驱动;2)配置环境变量解决nvcc命令识别问题;3)通过源码编译方式安装支持sm_120的PyTorch nightly版本(cu129),解决官方二进制包不兼容问题。整个过程涉及显卡驱动安装、CUDA环境配置、PyTorch适配等关键步骤,并提供了详细的验证方法和常见问题解决方案。文中特别强调

qq_36674060的博客 6160

关于开方sqrt的优化算法

float sqrt(float x) { if(x==0) return 0; float result=x; float xhalf=0.5f*result; int i=*(int*)&result; i=0x5f375a86-(i>>1); result=*(float*)&i; result=result*(1.5f-xhalf*result*result); r

问路的博客 2181

SpotBugs实战:10大类Bug检测规则详解与代码优化实例(附避坑清单)

本文深入解析SpotBugs在Java开发中的实战应用,详细讲解10大类Bug检测规则与代码优化实例,包括NPE防御、多线程安全、性能优化等关键问题。通过具体代码示例和避坑清单,帮助开发者提升代码质量,有效集成到CI/CD流程中。

weixin_42665255的博客 197

Google Earth Engine:LandTrendr (Landsat Collection 2)全更新使用(代码调用+UI交互)

本文介绍了使用Landsat Collection 2进行连续变化监测的分析方法,包含代码实现和可视化界面两部分。代码部分通过Google Earth Engine平台运行LandTrendr算法,设置参数包括时间范围、光谱指数、变化检测阈值等,最终生成变化幅度和检测年份的可视化图层,并将结果导出至Google Drive。可视化界面部分展示了基于LandTrendr的土地覆盖变化结果,结合Landsat影像进行分析。整个方法实现了从数据处理到结果展示的完整流程,适用于森林覆盖等环境变化监测。

此星光明博客 211

走迷宫(详细分析)

以一个 m*n 的方阵表示迷宫,0 和 1 分别表示迷宫中的通路和障碍。设计一个程序,对任意设定的迷宫,求出一条从入口到出口的最短通路,或得出没有通路的结论。

2301_79532295的博客 2493

【异常】Claude CLI / Claude Code 命令行工具 “claude 不是内部或外部命令“ 报错完整解决方案

摘要:Windows系统执行Claude命令报错"'claude'不是内部或外部命令",主要原因是环境变量配置问题。解决方案包括:1)通过npm全局安装时需加-g参数并重启终端;2)检查Node.js环境及npm全局路径是否加入系统PATH;3)对于Claude Code独立安装版,需手动添加.local/bin路径到环境变量。常见错误包括安装不完整、权限不足或路径未自动配置,按照对应场景排查即可解决。

奶爸的编程之路,也就一周冷个三天 7823

C/C++中移位运算符的连续使用

C/C++中移位运算符的连续使用 在C或C++中连续使用移位运算符编译器会将其自动优化。有时,当你需要将一个8bit串的第3位之外的其他位清零时,你可能想这样写: uint8_t test = 0xb5; test = test << 2 >> 7; 这时你期望的结果位0x20,然而实际上编译器给你的结果为0x05,这是经过编译器优化的结果,将0xb5直接向右移动了5位。 使用这种方法: uint8_t test = 0xb5; test = test << 2 ; t

rqX的博客 810

C语言读取CSV文件的方法1

逗号分隔值文件格式(CSV),以纯文本形式存储表格数据,它是一种通用的、相对简单的文件格式,被用户、商业和科学广泛应用。本文介绍利用C语言标准库中的一个字符串处理函数 strtok()来对CSV文件内容进行读取的基本方法

白马负金羁 1万+

代数拓扑与AI架构:AI应用架构师深入探索数学研究如何分析复杂系统的结构特性

随着人工智能技术的飞速发展,AI系统变得越来越复杂。AI应用架构师需要有效的工具和方法来理解、设计和优化这些复杂系统。代数拓扑作为数学的一个分支,提供了独特的视角和技术来分析复杂系统的结构特性。本文旨在帮助AI应用架构师深入了解代数拓扑,以及如何将其应用于AI架构设计中,提升对复杂系统的分析能力。首先,我们会介绍代数拓扑和AI架构相关的核心概念及其联系,通过有趣的故事引入主题,让读者轻松理解复杂概念。接着,深入探讨代数拓扑的核心算法原理,结合具体的编程语言代码示例详细阐述。

大模型应用工坊 671

八皇后问题

其实,很早很早之前学习c语言教程的时候,就见过这个问题,当时

dydm_13128的专栏 655

Istio ICA考试之路---3-2

部署2个测试容器,分别是test和test2.test注入标签subset=v1。这里文档中省略了metadata的3行,可以从前面任意一个yaml复制粘贴过来。通过test2测试容器访问,50%概率触发500故障注入。通过test测试容器访问,20%概率触发延迟注入。发起20次请求,一共10次被500。

Q先生 728

C++代码优化总结

※使用循环展开技术 由于循环体的内容相对简单,以至于这个循环实际执行过程中差不多一半的指令都在为检查循环执行的条件而服务。如果计算循环索引和测试循环条件的循环开销部分所占比重过大,这时就可以考虑使用一种被称作"循环展开"的方式来优化代码。所谓循环展开就是通过在每次迭代中执行更多的数据操作来减小循环开销的影响。其基本思想是设法把操作对象线性化,并且在一次迭代中访问线性数据中的一小组而非单独的某

Kelvin_Yan的专栏 1772

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

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

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

C++代码之VS优化以及omp并行加速

vs代码优化以及omp加速

保持分享欲 3167

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

提高程序运行效率的10个简单方法

对于每一个程序员来说,程序的运行效率都是一个值得重视,并为之付出努力的问题。但是程序性能的优化也是一门复杂的学问,需要很多的知识,然而并不是每个程序员都具备这样的知识,而且论述如何优化程序提高程序运行效率的书籍也很少。但是这并不等于我们可以忽略程序的运行效率,下面就介绍一下本人积累的一些简单实用的提高程序运行效率的方法,希望对大家有所帮助。 注:以C/C++程序为例 一、尽量变

ljianhui的专栏 7万+

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

C++程序加速的12个方法

C++程序加速的12个方法 文章目录C++程序加速的12个方法1.将反复使用的数据存放在全局变量里面。2.使用多线程3.用a++和++a,a--,--a4.减少除法运算5.尽量减少值传递,多用引用来传递参数。6.循环引发的讨论1(循环内定义,还是循环外定义对象)7.循环引发的讨论2(避免过大的循环)8.局部变量VS静态变量9.避免使用多重继承10.将小粒度函数声明为内联函数(inline)11....

博客 2万+

拾趣——ios::sync_with_stdio(false)详解(提高代码运算速度)

本篇文章核心意思:在编程时cin(cout)比scanf(printf)慢,在代码里加上ios::sync_with_stdio(false) 这个语句后,cin(cout)速度就会变得和scanf(printf)一样快,降低时间复杂度 PS 嫌墨迹的看到这就可以了-_-|| 在竞赛中,遇到大数据时,往往读文件成了程序运行速度的瓶颈,需要更快的读取方式。相信几乎所有的C++学习者都在cin机器缓慢...

weixin_43899069的博客 955

C++程序加速方法

c++ 程序提高运行速度方法

juluwangriyue的博客 1598

C++程序常见的性能调优方式

转载自:http://www.708luo.com/?p=36 冗余的变量拷贝 相对C而言,写C++代码经常一不小心就会引入一些临时变量,比如函数实参、函数返回值。在临时变量之外,也会有其他一些情况会带来一些冗余的变量拷贝。 之前针对冗余的变量拷贝问题写过一些帖子,详情请点击这里。 多重过滤 很多服务都会过滤的部分结果的需求,比如游戏交谈中过滤需要过滤掉敏感词。假设现在有两个过滤词...

weixin_34007879的博客 1421

c++提升速度

原理是利用get/put char的极快速度,把一个整型变量转换成一堆字符,用getchar/putchar进行输入输出。(1).数组可以转化成vector, vector的时间比同等大小的数组时间上快了很多倍。所以在需要以下技巧,当然还有很多,因为我能力有限,先介绍这些。首先,当你写了一份程序,可时间却慢如蜗牛,接下来这几个方法,让你速度直接起飞。题目 给出n个数和一个数k,问是否有一种方法,使这些数中其中两个数的和是k。一个程序的核心就是算法,当时间过长,需考虑换算法。对于n较大,无法解决。

lmn985的博客 897

Visual Studio调试C/C++指南

前言Visual Studio(VS)是微软开发的一款集成开发环境(IDE)软件,支持C/C++、C#、VB、Python等开发语言,开发桌面、Web等应用程序。VS功能极其强大,使用极其便利,用户数量最多,被誉为"宇宙第一IDE"。熟悉地掌握基于VS的C/C++调试技术,可以大幅提升调试性能。随着VS版本的更新,其功能越来越强大,本文的内容是基于VS2019进行验证测试的,之前版本VS可能有少量特性不支持。基础。

飞鹤的程序员人生 8825

Visual Studio C++编译器优化等级详解:配置、原理与编码实践

本文将系统解析MSVC的优化等级划分、配置方法、底层优化原理,并通过实际编码案例展示不同优化策略的效果差异,为开发者提供从调试到发布的全流程优化指南。通过合理配置优化选项,并结合编码示例中的技术原理,开发者可在不同场景下实现性能、大小与稳定性的最优平衡。通过深入理解编译器优化机制,开发者不仅能充分发挥MSVC的性能潜力,更能写出兼顾效率与可维护性的高质量C++代码。:需要严格符合C++标准的场景(如金融交易系统),或依赖函数地址稳定性的插件架构。通过三个典型场景,对比不同优化等级的代码生成差异与性能影响。

码事漫谈 1788
上一篇: 关于matlab用export_fig去掉白边、保存图像等问题
下一篇: BP 算法 (Back-propagation Algorithm) 之较直观的解释
jstudy0
博客等级 码龄16年 101粉丝 11原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值