性能优化指南:性能优化的一般性原则与方法( 三 )


对于可能被重复创建和销毁,并且创建和销毁成本较高的对象,例如进程和线程,也可以进行缓存 。对应的缓存形式有单例、资源池(连接池、线程池) 。
对于计算结果的缓存,也需要考虑缓存失效 。对于pure来说,固定的输入就有固定的输出,缓存不会失效 。但是,如果计算受到中间状态和环境变量的影响,缓存的结果可能无效,如本文所述:
#
并发
一个人做不完,就找两个人做 。并发不仅增加了系统的吞吐量,还减少了用户的平均等待时间 。
这里的并发是指广义的并发,粒度包括多机(集群)、多进程、多线程 。
对于无状态的服务(状态是指需要维护的上下文环境,用户请求依赖于这些上下文环境),集群可以用来很好的扩展,增加系统的吞吐量,比如挂载nginx后的web

性能优化指南:性能优化的一般性原则与方法

文章插图
对于有状态的服务,也有两种形式,每个节点提供相同的数据,比如mysql读写分离;每个节点只提供部分数据,例如在
在分布式存储系统中,() 和 () 都有助于并发 。
绝大多数的web,要么使用多进程,要么多线程处理用户请求,以充分利用多核CPU,而在IO阻塞的地方,也适合使用多线程 。较新的协程 ( , ) 也是一种并发 。
懒惰的
将计算推迟到必要时,可以避免冗余计算甚至根本不计算,请参见:
这个想法太棒了!
批处理,合并
当有 IO(网络 IO、磁盘 IO)时,合并操作和批处理操作往往可以提高吞吐量和性能 。
我们最常见的是批量读取:每次读取数据时多读取,以备不时之需 。例如,GFS 会从 GFS 中读取更多的 chunk 信息;例如,在分布式系统中,如果一个中心化节点生成复杂的全局 ID,我们的应用程序可以一次请求一批 ID 。
尤其是当系统中存在单点时,缓存和批处理从本质上减少了与单点的交互,是降低单点压力的一种经济有效的方式
在前端开发中,经常会出现资源的压缩和合并,这也是思路 。
对于网络请求,网络传输时间可能比请求的处理时间长很多,所以需要合并网络请求,比如bulk和redis 。在写文件的时候,也可以批量写入,减少IO开销,GFS就是这样做的 。
更高效的实施
同一个算法必然有不同的实现,所以会有不同的表现;有的实现可能是time-for-space,有的可能是space-for-time,需要根据自己的实际情况权衡 。
程序员喜欢造轮子,拿来练习用是可以理解的,但是在项目中,使用成熟的、经过验证的轮子往往比自造轮子要好 。当然,不管你是用别人的轮子还是自己的工具,当出现性能问题的时候,要么优化,要么更换 。
例如,我们有一个场景,大量复杂的嵌套对象被序列化和反序列化 。一开始使用的是()自带的json模块 。即使出现性能问题,也无法优化 。在线查看并更换 。变成ujson,性能好很多 。
上面的例子是无损的,但是一些更高效的实现也可能是有损的 。例如,如果发现性能问题,很可能会考虑 C 扩展,但也会带来可维护性和灵活性的损失 。有坠机风险 。
缩小解空间
缩小解空间意味着计算是在较小范围的数据上执行的,而不是遍历整个数据 。最常见的是索引 。通过索引,可以快速定位数据 。数据库的优化主要是索引的优化 。
如果有本地缓存??,那么使用索引也会大大加快访问速度 。但是,索引更适合多读少写 。毕竟指数的建设也需要消耗 。
此外,在游戏服务器端,使用的分割线和AOI(点阵算法)也是减少解空间的方法 。
性能优化和代码质量