300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 优秀架构师必须掌握的底层技术之计算性能优化

优秀架构师必须掌握的底层技术之计算性能优化

时间:2023-12-16 11:05:13

相关推荐

优秀架构师必须掌握的底层技术之计算性能优化

/group/6740468535408984588/?app=news_article&timestamp=1570376969&req_id=1006234928010026077210220F008D&group_id=6740468535408984588

上层应用开发的多了之后,对底层技术的接触就越来越少了。以至于很多人有了“底层技术无用论”的观点。很多人认为学习框架多好啊,大家都在用,跳槽的时候也能用的上。学习那些底层技术干啥,平时都用不到。

本号并不这么认为。为了让大家更加清晰的认识到这个问题,我们先举一个具体的例子,具体如下:

比如我们现在有个Web服务应用,崩溃重启后在绑定套接字的时候出现报错(socket_bind(): unable to bind address [98]: Address already in use。),导致服务端无法工作。

这个问题还是比较明确的,从提示信息可以看出是地址(端口)被占用了。你这时候可能会猜测那个程序占了端口呢?会是一些新的程序将该端口占用了吗?

其实大家都清楚,服务器端口的使用都是严格受限的,不会突然多出一个新程序使用这个端口的。也就是说,肯定是这个程序占用了自己的端口。但是可能会疑惑:“这个程序不是刚刚起来吗?!”如果你只是使用API,不懂得底层的原理,别说解决问题,可能都不知道如何下手。这个问题我们先放到这里,后面再具体解释,这里只是想说明一下底层技术的重要性。

另外一个比较典型的例子是关于前端开发的。很多人热衷于学习各种框架。框架虽然能帮助我们解决一些问题,节省开发成本并降低开发周期。但是,学习框架并不能掌握技术的根本,从而导致自己能力没有本质的提升。我们以前端框架为例,在过去的几年当中,JQuery、Bootstrap、Angular和Vue等等等等,轮番上阵。这个框架你还没用熟悉呢,结果又来了一个新的框架,让你应接不暇。而这些框架最本质的东西其实就是JS、CSS和HTML等内容,只有学会这些基础技术,才能游刃有余。如果这些基础技术不熟悉,而投入大量精力学习框架,这就好像还没学会走,就想着跑,最后自己可能摔得满头是包。

可能扯的有点远,前面的例子只是想告诉大家底层技术的重要性。对于我们搞软件开发的人来说,底层技术其实相当于大厦的地基,地基不稳,大厦是很危险的。当然,计算机技术的细分领域很多,每个领域又有自己的底层技术,因此我们不可能都有涉及。今天我们介绍的底层技术则是最为通用的技术,也就是计算、存储、网络和数据结构与算法。

关于CPU的内部结构与程序性能

计算机技术自然核心是计算了。毫不夸张的说,所有应用都要依赖于计算,小到单机小游戏,大到电商或者云计算平台。因此,计算问题自然是我们最为关心的问题了。说到计算,最主要的自然是程序的性能了,如果我们开发的程序的性能提升一倍,就相当于硬件成本降低了50%。对于互联网这种需要大量计算资源的应用,其价值可见一斑。

我们先看一个具体的例子。下面是一段C语言的代码,代码很简单,就是将二维数组中的内容做加一操作。但是如果你测试一下两段代码的耗时的话,就会发现两者有四倍的性能差异。大家可以观察一下图中两端代码的差异,并思考一下为什么有如此之大的差异。

问题先放一下,我们回到我们今天的主角,CPU。CPU是计算依赖的硬件,大家都知道计算是在CPU内完成的。我们先看一下CPU长什么样。CPU是计算机的核心单元,它负责从存储设备读取数据,经过计算后将生成的新数据再存储起来。这就好像一个大型工厂的生产车间,将原材料加工成半成品或者成品(我们后面单独用一个章节介绍CPU相关的内容)。

了解了CPU的基本功能,我们再解剖了看看它的五脏六腑长什么样。下图是一个简化的CPU内部结构图,最为核心的组件就是计算单元(ALU)、寄存器(很多寄存器)和高速缓存。另外就是通过总线接口与外部的内存进行连接。这里面最核心的组件就是ALU了,其原理很简单,就是完成加减乘除运算。

图1 CPU内部结构简图

CPU要进行运算,就需要原料,而原料需要从内存搬运。有一个事实我们需要记住,就是访问内存的代价(延时)是访问寄存器的100倍左右。最早的CPU是直接访问内存的,后来随着ALU性能的提升,发现有问题,就在ALU和内存之间增加了缓存。现代CPU缓存通常为3级缓存,分别是L1、L2和L3,其中L1和L2是CPU核独有的,而L3是同一颗CPU的多核共享的。其基本的架构如下图所示。

图2 CPU缓存架构

这里面有个关键问题是缓存的容量是远远小于主(内)存的容量的,因此,缓存中的数据通常是主存数据的很小的一部分。由于应用访问数据有区域局部性的特点,因此缓存中的数据通常是程序需要的数据,也就是ALU接下来要用的数据。另外一个需要注意的地方是从主存读取数据到缓存是有一定粒度(专业术语叫缓存行)的,当前处理器通常是64字节。如下图所示,主存中的内容被读取到缓存中。

关于上面程序问题的解答

然后,我们回到一开始的关于上面两段程序的性能问题来。上面代码中一个是逐行访问二维数组,另外一个是逐列访问二维数组。具体示意图如下图所示。

在逐行访问时,访问的地址是以4字节为单位跳跃的,由于缓存行大小是64字节,因此很容易命中缓存。而逐列访问时,每次跳跃4096字节,远远超越了缓存行的大小,从而导致数据大部分是从内存读取的。也正是因为这个,导致两个程序有四倍的性能差异。

总结

通过上面的介绍,我们应该记住两个关键点,一个是访问内存的代价比较高,因此在编程时尽量减少对内存的直接访问;另外一个是充分利用缓存的优势。关于如何做到上面两点,具体细节我们后续专门介绍。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。