您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

软件性能优化那些事

时间:2021-01-07 09:31:07  来源:  作者:

性能在软件工程诞生时就占据着非常重要的位置,如何用更少的硬件资源来支撑更多的功能、来完成更多的任务是软件工程师的职责,也是用来衡量一个软件工程师技艺高低的标准。这跟炒菜是一个道理,同样的食材,在饭店的师傅手里跟普通人手里的做法是有很大的差别的;食堂师傅是工程化的做法,讲究是短时间炒制更多的菜,而且味道不能掉;而普通人则比较随意,没有什么章法,而且可能只是爱好或者放松的渠道,所以不用考虑炮制的性能如何,能不能达到预计的目标。简单来说,厨师是靠手艺去赚钱,而其他人只是玩玩。那么程序员的手艺体现在哪呢?可以肯定的说,性能调优是最重要的一环,也是最基本的一环,甚至我觉得只有掌握这个技能后才能够进阶做构架或者做设计,否则如果反其道而行之则会举步维艰。

这时你可能会说,我是觉得性能是很重要,但是怎么去学呢?IT是个发展迅速的行业,每年涌现的新技术,新框架数不胜数,多如繁星,往往还没学懂JAVA,Go都快被淘汰了……一茬接一茬,无从下手。如果你常常有这个感觉,那么这篇文章可能对你有帮助。

性能优化的套路

先说结论,性能优化或者说如何能够开发出效率更高的程序来(当然硬件资源一样且使用率一样的条件下),只有两条路:

1. 掌握局部性原理

2. 掌握基本的算法设计与分析

在现有计算机构架下(冯诺依曼构架)这是调优的充分必要条件,换句话说就是,如果你写了一个性能比别人好的程序,那么只有两种情况,要么是局部性原理用得更好,要么是就是采用了更好的算法;同时,如果你想要写出比别人好的程序,那么你也只有两条路可以走,运用更底层的局部性优化设计,或者设计更好的算法来解决这个问题。

局部性原理

如果你在软件工程领域“阅历”足够丰富,你就会有恍然大悟的感觉,因为这种例子实在太多:linux中的硬盘缓冲、页缓存,redis中元素较少的时候用ziplist代替hashmap可能还会带来性能的提升,内存的减少;Kafka依赖的磁盘缓存,分区存储机制,MySQL的B+树索引;更底层的CPU的Cacheline技术,分页技术等等其实都是用了局部性原理来作为理论基础的。那么什么是局部性原理呢?

简单来说分为三个要点:

  1. 程序总是按照“批”来处理数据,这样效率最高;比如:CPU读取内存是按照批来处理如:一个cacheline=64/128byte,内存与硬盘按照4KB来读取。因为计算机结构的特点,各级设备(CPU到内存到硬盘与外设)之间的访问速度是有数量级的差别;所以就注定不能一个个字节来读取;这个现象决定计算机优化的方向,被称为冯诺依曼瓶颈
  2. 最近访问过的资源(内存位置),可能近期内还会被访问,这个叫做时间局部性,对应的解决方案是缓存
  3. 当前位置内存被访问过,那么大概率旁边的内存也会被后续访问,这个叫做空间局部性,对应的解决方案是预加载

所以推导的顺序是:正是因为现代计算机有冯诺依曼瓶颈所以需要用缓存与预加载来提高程序的性能,否则会因为IO过重,资源得不到利用而效率偏低。

还是拿做饭这件事举例子。当我们去买菜的时候,会尽可能的减少去菜市场的时间,也肯定不可能发现什么菜没有了再临时去菜市场购买,这样“IO”就太重了,要使用局部性原理来解决。就炒菜的例子来说,我们要炒青椒炒肉这个菜;首先是买菜,我们发现在买青椒的时候可以把葱姜蒜也都一并买了,因为蔬菜区往往是相邻的;而买肉的时候,尽量也把酱油醋都一并买了,它们也是很可能也是挨着的;这种在空间上相邻的提前加载这就是“预加载”了;而回到家,我们开始炒菜的时候,厨房的布置往往是炒菜的锅子跟油盐酱醋是分开放置的,但是炒菜的时候要经常加盐、放酱油,所以为了减少来回取油盐的时间,会在炒菜的时候将配菜与油盐放置在炒锅的附近,加快炒菜的速度,等炒完后又重新放回去,这个就叫做“缓存”的思维——把经常使用的资源放在工作较近的地方,等使用完再释放。

在Linux内核中,我们可以看到很多的buffer与cache,这些数据结构都是局部性原理的具体使用实例。比如,我们知道内存跟磁盘之间IO速度相差5个数量级。那么往往会出现,一个进程刚写入磁盘的数据,又要被其他进程读取,那么一来一回CPU都在等待磁盘读取数据,十分浪费资源,那么如果将磁盘的数据放入磁盘写buffer,读的时候优先从buffer中读取,而buffer都是在内存中,这样就弥补了这个性能的gap,这就是“时间局部性”原理的使用,也就是我们常说的“缓存”,或者“空间换时间”;

Mysql数据库有个特点就是数据是存在磁盘上,读取记录时需要将数据从磁盘加载到内存然后进入CPU计算得出结果,所以会横跨三个IO边界——磁盘、内存与CPU,它们之间都有好几个数量级的延迟,所以IO性能的平衡就十分的重要。其中比较典型的就是B+树这种针对外部存储型的数据结构的引入,它十分贴合磁盘IO的“口味”。磁盘IO有个特点,就是读取写入都很慢,但是可以一次读取大量的数据。根据这个特点,B+树采用多叉树的结构进行设计,这样做相比二叉树来说可以减少树的高度,这样带来的好处是每一层数据都可以是在物理空间相邻的,从而可以通过最少的IO次数加载更多的数据到内存;而这些数据往往是业务相关的,所以一次IO读取的数据可以供后续很多计算工作使用而不用重复IO。比如,一个表有20列,那么每一行数据就有20个字段,这些字段往往在磁盘中是连续存放的,当我们通过id查询其中某个字段a的值时,Mysql其实会将整个记录都取出来,加载到内存,而程序访问完a字段,再访问记录中其他字段时就不需要磁盘IO了,直接读取内存中记录的缓存就行,这就是“空间局部性”的使用实例,也就是我们常说的“预加载”,系统预热。

算法分析与设计

这是计算机科学的范畴,也是最引人注目的领域,就好比武侠小说里面的武功,华丽的招式比不过强大的内功,练招式容易练内功难,一旦内功深厚,招式什么的都是分分钟被秒杀,就好比《一拳超人》中的琦玉老师,专治各种花里胡哨。而我想说的是,算法设计技术就是软件工程领域的内功心法。当然我远远没有达到可以对这个领域品头论足的程度,就想抛砖引玉,介绍点皮毛,希望对有天赋的同学有所启发。

数据结构

数据结构之所以重要是因为不同的数据结构表现出来的数学性质是构成特定算法的基础。就好比各种化学元素可以搭建各种性质不同的化合物,而不同性质的化合物正式化学工程所依赖的基础。计算机科学的数据结构可以大致分为两种,一种是数组,另一种是链表。两种性质截然相反的“物质”。

  1. 数组——线性性能最好,支持随机访问,按照索引取数组中的元素时间复杂度是O(1),而插入与删除元素时间复杂度是O(n);
  2. 链表——扩展性能最好,支持动态的增减元素,插入、删除元素的时间复杂度是O(1),而检索元素的时间复杂度变成了O(n);

(注:O标记是数学语言,用于标记一个函数的增长快慢,是一个渐进界函数,具体细节可以参考屈婉玲教授的详细解释)

这个世界有时候是惊人对称的,对称是美妙的。这样一对性质完全互斥的结构就是构成所有高等数据结构的基础。就像中国传统文化中的太极一样,你中有我我中有你,互相补充,世间万物的运行法则皆可解释。

举几个例子,有些高等数据结构具有耀眼的数学特性,比如“堆”,或者叫做优先级队列、二叉堆,就是以数组作为基础的。它在插入、删除、查询元素时的性能可以稳定在O(logn)量级;在互联网行业中,只有logn级别的算法可以适用,因为当数据规模急剧增加的时候,对数函数能够很好的平稳压力,所以,"logn"的算法对互联网行业贡献巨大,具有整流器的作用。比如,在微服务流量控制,大数据流处理、topN、高性能定时器都有很多应用。而堆只有在数组实现的算法中才能保持这个特性,如果用链表就会退化为O(nlogn),失去魔力。

另一个例子是二叉树,这就是链表的一个使用场景。二叉树是一种树状结构,其中平衡二叉树在插入与删除的过程中只要移动logn次就能找到自己的新位置,而且代码简单易于维护(不容易写错也是工程中一个重要的考虑点,如果写得代码过于复杂就要反思下是否使用错了数据结构)。比如:红黑树就是一个综合性能很好的平衡二叉查找树;它是一个动态的数据结构,可以在动态添加与查找过程中稳定在O(logn)量级;在Linux内核中大量使用;而且在Java中的ConcurrentHashMap在冲突大的情况下,冲突元素大于8也会升级成红黑树存储冲突元素,来平衡工程与算法效率之间的矛盾。

当然,数据结构是可以融合的,比如Java中的LinkedHashMap就是融合了数组、哈希表与链表的优秀实践。普通的哈希表因为通过哈希函数将元素链接到数组的索引号上面,实现高速的查找性能,但是丢失了元素的插入顺序,而有时候我们需要这个顺序性来实现特殊的需求,比如缓存淘汰策略;而如果使用链表,形成一个插入的队列,先插入的在队列头,后插入在队列尾部;但是这样虽然保存了插入的顺序但是丢失了查找性能;为了平衡,我们可以在哈希表的基础上,每个元素再增加一个指针用来连接前后插入的元素,形成队列,这样没插入一个元素不仅在哈希表上挂载新的索引点,还要将新元素挂接到队列的尾部,而每一步都是O(1)的开销,是可以接受的,这就是LinkedHashMap的实现原理。

算法设计技术

算法设计技术是使用数据结构解决实际问题的技术,就好比化合物特性研究与化学工程的关系,一个是偏重科学特性的研究,一个是解决实际问题。为了能够让科学能够指导生产,能够造福世间,必须将科学落地,那么这个过程中最重要的一步就是对实际问题的建模,建模是对问题的模拟,越准确越能够解决好问题。那么,在算法设计层面有哪些建模方式呢?大体可以分为三个:

  1. 蛮力算法
  2. 贪心算法
  3. 分治算法
  4. 动态规划

其中,蛮力算法是对问题建模的初期阶段,是对问题的程序再现,追求的是定性,性能不一定重要,但是问题描述没问题;分治与贪心是在蛮力基础上的一次降阶,往往可以将问题优化到O(nlogn)的规模以内;而动态规划可以进一步将问题的阶降到O(n)级别。降阶是设计算法的初衷,前提是问题本身计算的各个阶段是有冗余的,有重复计算的地方,而找到这个重复的点并不容易,就拿动态规划来说吧,虽然有极致的性能但是发现递推方程并不容易,当然这一切都要经过严格的数学证明才行,这就更加难能可贵。我们这里没有考虑空间的优化,往往降阶的过程中最好保证空间的复杂度不会激增,这样才会有效。这方面我们不展开了,有很多资料可以参考,其中我推荐屈婉玲教授的算法设计分析「链接」 。

写在最后

细心的你可能会发现,为什么没有讲多线程?多线程也是常用的优化手段啊。对,工程上多线程往往可以优化程序的运行效率,但是这里有个基础就是硬件的资源并没有被充分利用,也就是说,如果因为IO导致CPU利用率不足,当然我们就要想办法去构建可以充分利用硬件资源的方法,而多线程、多路复用这些技术都是为了提高硬件的利用“带宽”的技术,所以并不在我们讨论范围之内。这篇文章还有更加详细的另一篇姐妹篇,在这里「链接」,希望对你有所帮助。


文/ThoughtWorks 肖劲



Tags:优化   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
此前有关厂商销售的显示器上,HDMI 2.1的标示问题闹得沸沸扬扬。按照官方的说法,HDMI 2.0标准已经不存在了,以后只有HDMI 2.1标准,后者的功能是可选的,显示器制造商只要具体说明该...【详细内容】
2021-12-24  Tags: 优化  点击:(8)  评论:(0)  加入收藏
对于一些文章页面来说,想要得到提升页面进行整体质量度,除了优化研究文章主体主要内容之外,调用相关分析文章也是企业优化的重点。那么我们具体怎么调用相关文章呢,星宇SEO今天...【详细内容】
2021-12-22  Tags: 优化  点击:(6)  评论:(0)  加入收藏
对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃。不应该大于255,默认值是5,对应于180秒左右时间。。(对于大负载而物理通信良好的网络而言,这个值偏高,可修改为2.这个值仅仅是针对对外的连接,对进来的连接,...【详细内容】
2021-12-08  Tags: 优化  点击:(23)  评论:(0)  加入收藏
App的性能优化,可以从多角度优化,包括App启动优化,App启动优化又分为main()函数执行前优化,main()函数执行后优化;包括界面卡顿优化,页面卡顿优化又包括CPU优化和GPU优化;包括安装包瘦...【详细内容】
2021-12-08  Tags: 优化  点击:(14)  评论:(0)  加入收藏
导读:网易大数据平台的底层数据查询引擎,选用了Impala作为OLAP查询引擎,不但支撑了网易大数据的交互式查询与自助分析,还为外部客户提供了商业化的产品与服务。今天将为大家分享...【详细内容】
2021-11-26  Tags: 优化  点击:(15)  评论:(0)  加入收藏
在我们使用win10操作系统的过程中,经常听到有小伙伴说win10系统没有win7系统好,win10会卡顿,今天小编来跟大家说说win10优化设置教程的介绍 - 装机吧的介绍,快来看看吧win10...【详细内容】
2021-11-22  Tags: 优化  点击:(40)  评论:(0)  加入收藏
最近发现B2B的海外推广貌似是一个挺大的缺口,很多厂长或老板不了解独立站的流量构成和玩法,无论是自然流量还是付费流量。也衍生了很多培训(咦,这是不是我副业的好机会?)所以这次...【详细内容】
2021-11-11  Tags: 优化  点击:(31)  评论:(0)  加入收藏
说明Web应用程序,MySQL数据库,数据库中有三张表:health_patient(病人表)、health_patient_account(病人账户表)、 health_patient_medical_history(病例表),视图需求是,页面分页展示病...【详细内容】
2021-11-05  Tags: 优化  点击:(32)  评论:(0)  加入收藏
摘要:如今越来越多的企业都在做网站优化,来增加品牌曝光率,从而获得客户实现销售。所以现在很多商家都会利用SEO优化做相应的营销和推广,从而引起更多的关注网站。企业做网站优...【详细内容】
2021-11-03  Tags: 优化  点击:(27)  评论:(0)  加入收藏
说起 WordPress 优化加速来可以说是个经久不衰的话题了,包括明月自己都撰写发表了不少相关的文章。基本上到现在为止明月的 WordPress 优化方案已经固定成型了,那就是 WP Supe...【详细内容】
2021-11-02  Tags: 优化  点击:(49)  评论:(0)  加入收藏
▌简易百科推荐
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(1)  评论:(0)  加入收藏
程序是如何被执行的  程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(9)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(8)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(7)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(9)  评论:(0)  加入收藏
实现效果:假如有10条数据,分组展示,默认在当前页面展示4个,点击换一批,从第5个开始继续展示,到最后一组,再重新返回到第一组 data() { return { qList: [], //处理后...【详细内容】
2021-12-17  Mason程    Tags:VUE   点击:(14)  评论:(0)  加入收藏
什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么时候来进行性能调优?(who) 怎么样进行性能调优?(How) 硬件配...【详细内容】
2021-12-16  软件测试小p    Tags:性能调优   点击:(19)  评论:(0)  加入收藏
Tasker 是一款适用于 Android 设备的高级自动化应用,它可以通过脚本让重复性的操作自动运行,提高效率。 不知道从哪里听说的抖音 app 会导致 OLED 屏幕烧屏。于是就现学现卖,自...【详细内容】
2021-12-15  ITBang    Tags:抖音防烧屏   点击:(23)  评论:(0)  加入收藏
11 月 23 日,Rust Moderation Team(审核团队)在 GitHub 上发布了辞职公告,即刻生效。根据公告,审核团队集体辞职是为了抗议 Rust 核心团队(Core team)在执行社区行为准则和标准上...【详细内容】
2021-12-15  InfoQ    Tags:Rust   点击:(24)  评论:(0)  加入收藏
一个项目的大部分API,测试用例在参数和参数值等信息会有很多相似的地方。我们可以复制API,复制用例来快速生成,然后做细微调整既可以满足我们的测试需求1.复制API:在菜单发布单...【详细内容】
2021-12-14  AutoMeter    Tags:AutoMeter   点击:(20)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条