龙神教你“如何做系统性能优化”

性能优化的目标是什么?不外乎两个:

时间性能:减小系统执行的时间 空间性能:减小系统占用的空间

一、代码优化 做代码优化前,先了解下硬件Cache: (1)Cache Level:通常来说L1、L2的Cache集成在CPU里,L3的Cache放在CPU外; (2)Cache Size:它决定你能把多少东西放到Cache里,有Size就有竞争,就有替换,才有所谓优化的空间; (3)Cache Type:I-Cache(指令),D-Cache(数据),TLB(MMU的Cache);

代码层次的优化主要从以下两个角度考虑问题: (1)I-Cache优化:精简code path,简化调用关系,减少冗余代码等等; (2)D-Cache优化:减少D-Cache的miss数量,增加有效数据访问。

以下是一些技巧,可供参考: (1)Code adjacency(把相关代码放在一起) 这里有两层含义:一是相关源文件要放在一起;二是相关函数在object文件里面,也应该相邻。 这样,可执行文件被夹在到内存里时,函数位置也是相邻的,同事还符合模块化编程的要求:高内聚,低耦合。 (2)Cache line alignment(cache对齐) 对齐Cache以减少潜在的一次读写,但这可能意味着内存的浪费,需要从空间和时间两方面衡量。 (3)Branch prediction(分支预测) 如果能预测那段代码有更高的执行概率,就能减少跳转次数(调整if和else的顺序?)。 (4)Data prefetch(数据预取) 由CPU自动完成。 (5)Register parameters(寄存器参数) (6)Lazy computation(延时计算) 最近不用的变量,不要急着去初始化(意味着可能执行复杂的构造),如果某个分支跳出了函数,这些动作就浪费了。 COW(copy-on-write)就是一种延时计算的技术。 (7)Early computation(提前计算) 有些变量,计算一次就够了,任何加减乘除都会消耗CPU指令,尽量使用常数,而不是24*60*60来表示一天的秒数。 (8)Inline(内联函数) (9)Macro(宏定义) (10)Allocation on stack(局部变量) 避免在栈上申请大数组,其初始化和销毁的代价很高。 (11)Per-cpu data structure(非共享数据结构) 避免共享量的锁,在thread local里,多核情况下使用局部变量会带来好处。 (12)Reduce call path or call trace(减少函数调用层次) (13)Read&write split(读写分离) (14)Recude duplicated code(减少冗余代码)

其中,(1)(6)(7)(8)(9)(10)(11)(12)(14)这些优化方式最为常见。

二、工具优化 “工欲善其事,必先利其器”,如果没有工具的支持,性能优化难以实施,谁也不知道哪些地方是严重影响性能的主要矛盾。 使用性能优化的工具,需要考虑以下问题: (1)使用工具是否需要重新执行编译? (2)工具本身对测量结果的影响:工具对性能的影响必须在一个可接受的范围以内。

工具能解决的问题: (1)建立性能基线,以作对比; (2)帮助定位性能瓶颈; (3)帮助验证优化方案;

性能测试工具一般由这么几种: (1)收集CPU的性能计数; (2)利用编译器的功能,在函数入口和出口加回调函数; (3)在代码中加入时间测量点。

在Linux下,通常会用到的有: (1)Oprofile 它已经加入Linux内核代码库,但通常需要重新编译内核,参考如下 http://oprofile.sourceforge.net/news/ http://people.redhat.com/wcohen/Oprofile.pdf (2)KFT and Gprof KFT是kernel的一个patch,只对kernel有效;Gprof是gcc里面的一个工具,只对用户空间的程序有效。这两个工具都需要重新编译代码,它们都用到了gcc里面的finstrument-functions选项。编译时会在函数入口,出口加回调函数,而且inline函数也会改成非inline的。它的工作原理可以参考: http://blog.linux.org.tw/~jserv/archives/001870.html http://blog.linux.org.tw/~jserv/archives/001723.html http://elinux.org/Kernel_Function_Trace http://www.network-theory.co.uk/docs/gccintro/gccintro_80.html

三、系统优化 从系统层面去优化系统往往有更为明显的效果,优化之前,可以思考,是否能够通过扩展系统来达到提高性能的目的: (1)Scale up:使用更强的硬件; (2)Scale out:使用更多的组件;

如果升级硬件的方法就能解决问题,为什么还要使用修改代码,调整架构这样大风险的举措呢?(需要考虑成本) 以下是一些常用的系统优化的方法: (1)Cache Cache干什么?保存已经执行过的结果。 Cache为什么有效?避免已计算过的开销,获取更快的访问。 Cache的难点在哪里?一是快速匹配;二是Cache容量有效,需要较好的替换策略; Cache在哪些情况下有效?时间局部性。即当前计算的结果,后续有可能使用到,如果没有时间局部性,反而对架构有害。

(2)Lazy Computing 理念就是,不要做多余的事情,最常见的例子就是COW(copy-on-write): http://en.wikipedia.org/wiki/Copy-on-write COW干什么?写时复制。 COW为什么有效?节省内存复制时间,均匀内存分配时间。 COW的难点在哪里?一是引用计数的使用;二是确认哪些内存是可以共享的。

(3)read ahead/pre-fetch预读 http://en.wikipedia.org/wiki/Readahead 预读干什么?提前准备所需要的数据。 预读为什么有效?减少等待内存的时间,相当于把多个操作集合成一个。 预读在哪些情况下有效?空间局部性。

(4)Asynchronous异步 http://en.wikipedia.org/wiki/Asynchronous_I/O 异步干什么?异步是一种通信方式,请求与应答分离。 异步为什么有效?消除等待时间。 异步的难点是什么?如何实现分布式状态机,如何使用回调,使异步时间到达后继续执行。 异步在哪些情况下有效?状态之间不能有强依赖关系。

(5)Polling轮询 http://en.wikipedia.org/wiki/Polling_(computer_science)

(6)Static memory pool(内存池) http://en.wikipedia.org/wiki/Static_memory_allocation 内存池干什么?提前分配内存以获取更好的性能,只是适应性可能会降低,并可能造成内存浪费。 内存池为什么有效?避免重复内存申请、释放开销。 内存池的难点是什么?分配多大的内存池,如何避免浪费都是需要考虑的问题。 内存池在哪些情况下有效?一是固定大小的内存需求,二是快速的分配与释放需求。

四、总结 性能优化只是系统的一个方面,它可能会和系统的其他要求有冲突,比如 可读性:性能优化不能影响可读性,谁愿意维护不怎么漂亮的代码; 模块化:性能优化往往需要打破模块的边界,想想这是否值得; 可移植:隔离硬件相关的代码,尽量使用统一的API; 可维护:许多性能优化的技巧,会导致后来维护代码的人崩溃;

需要在性能优化和上述的几个要求之间做出tradeoff,不能一意孤行。

原文发布于微信公众号 - 架构师之路(road5858)

原文发表时间:2016-05-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏杨建荣的学习笔记

和开发同学讨论的一个技术问题(r8笔记第73天)

今天下午的时候,有一位开发同事找我,说有一个技术问题想请教一下。 当然正如他所说,这个问题比较奇怪,而且已经影响了他的测试流程,他说有一个表查看对应的表空...

2735
来自专栏进击的程序猿

袖珍分布式系统(三)

本文是Distributed systems for fun and profit的第三部分,本文是阅读该文后的一些记录。

622
来自专栏达摩兵的技术空间

异步IO(一)

在web2.0的时候,其实前端就很熟悉异步编程了,只不过那时大家使用的是ajax(典型的网络请求)实现的,还有一些前端的事件机制(针对事件定义回调事件)。

964

所有您需要了解的关于Elasticsearch 5.0:索引管理

我们看到两种主要的Elasticsearch索引使用模式 - 全局索引和滚动索引。多年来,Elasticsearch增加了一些功能,可以极大地改善这些模式的工作...

2883
来自专栏WeTest质量开放平台团队的专栏

[分享干货晒技术]Unity 手游内存优化分享

Mono下的foreach使用需谨慎。频繁调用容易触及堆上限,导致GC过早触发,出现卡顿现象。

692
来自专栏跨界架构师

分布式系统中的必备良药 —— 全局唯一单据号生成

  我们作为一个软件系统,肯定到处充满着各种单据,也必然需要有各种单据号与之对应。比如:电商行业的订单号、支付流水号、退款单号等等。SCM的采购单号、进货单号、...

992
来自专栏技术分享

数据分表小结

本次拆分主要包括订单和优惠券两大块,这两块都是覆盖全集团所有分子公司所有业务线。随着公司的业务飞速发展,不管是存储的要求,还是写入、读取的性都基本上到了警戒水位...

590
来自专栏架构师之路

58龙哥教你“如何做系统性能优化”(纯干货)

如何做系统性能优化 性能优化的目标是什么?不外乎两个: 时间性能:减小系统执行的时间 空间性能:减小系统占用的空间 一、代码优化 做代码优化前,先了解下硬件Ca...

2884
来自专栏Golang语言社区

C++ 实现银行排队服务模拟

教程简介:使用 C++对银行排队服务进行模拟,以事件驱动为核心思想,手动实现模板链式队列、随机数产生器等内容,进而学习概率编程等知识。作为可选进阶,这个模型同时...

34512
来自专栏恰同学骚年

操作系统核心原理-3.进程原理(中):进程调度

PS:在多进程并发的环境里,虽然从概念上看,有多个进程在同时执行,但在单个CPU下,在任何时刻只能有一个进程处于执行状态,而其他进程则处于非执行状态。那么问题来...

675

扫码关注云+社区