1. 本转载文章对设计者在性能估算上非常有参考价值;2. 负责技术运营、运维的同学前期早参与开发设计是非常有必要的,特别是一些经验丰富的同学在线上大流量下见多识广,对一些性能、瓶颈值比较熟悉;3. 开发的系统适合运营生产环境是一个上线的准入条件,否则运营成本巨大,即使上线了也是问题服务,不如在上线前就把关解决掉。
给定一个问题,往往会有多种设计方案,而方案评估的一个重要指标就是性能,如何在系统设计时估算而不是程序执行时测试得到性能数据是系统架构设计的重要技能。性能估算有如下用途:
1) 多种设计方案选择;
2) 评价程序实现是否足够优化;
3) 向框架/服务提供方提出性能要求的依据;
通过查看程序运行时CPU及网络的使用情况来评价程序是否足够优化,这也是一种很重要的方法。然而,这种方法掩盖了不优化的实现,如O(N)的算法被错误实现成O(N^2),网络收发冗余数据等。
性能评估需要假设程序的执行环境,如集群规模及机器配置,集群上其它服务占用资源的比例。首先,我们需要知道一些常见硬件的大致性能参数:
L1 cache reference 0.5ns
Branch mispredict 5ns
L2 cache reference 7ns
Mutex lock/unlock 100ns
Main memory reference 100ns
Send 1M bytes over 1Gbps network 10ms
Read 1M sequentially from memory 0.25ms
Round trip within data center 0.5ms
Disk seek 8~10ms
Read 1MB sequentially from disk 20~25ms
标记为红色性能参数比较常用,其中,磁盘的性能指标专指分布式平台专用的大容量磁盘,寻道时间为8~10ms,顺序读取速率为40~50MB。某些产品线使用SCSI磁盘或者Flash盘,性能较好,评估时需查看硬件的性能参数。磁盘和网络都有一个特征,一次读写的数据量越大性能越好,这是由硬件特征及底层软件算法决定的,如tcp慢连接和磁盘寻道时间长。
对硬件性能指标有了初步认识以后,我们可以做出一些简单的判断,如:
某后台开发同学:某检索程序A单客户端同步读取每秒可以达到18000/s。
问:是否批量读取?
答:是,每批读取10个记录。
由于tcp Round trip时间为0.5ms,读取请求个数的理论极限为2000/s,而A的开发同学却说单客户同步读取可以达到18000/s,可以断定A开发同学指的是批量读取方式。且这已经是单机能够做到的极限值了。
下面我们通过几个实例说明如何进行性能评估。
1. 1GB 的4字节整数,内存排序时间为多少?
拿到这个问题,我们往往会计算CPU运算次数,如快排的运算次数为1.4 * N * log(N),其中1.4为快排的系数,再根据CPU的运算频率计算出排序耗时。不过这种方法很土也不是很准,Jeff Dean告诉我们可以这样估算:排序时间 = 比较时间(分支预测错误) + 内存访问时间。快排过程中会发生大量的分支预测错误,所以比较次数为2^28 * log (2^28) ≈ 2^33,其中约1/2的比较会发生分支预测错误,所以比较时间为1/2 * 2 ^ 32 * 5ns = 21s,另外,快排每次找到分割点都需要一遍内存移动操作,而内存顺序访问性能为4GB/s,所以内存访问时间为28 * 1GB / 4GB = 7s。因此,单线程排序1GB 4字节整数总时间约为28s。
2. DTS设计的性能指标分析
DTS总体设计中给出的性能指标为:
系统配置:50台4核8GB内存12路SATA硬盘,同样数量的客户端;
Table:row name:16-byte,column:16-byte,value:1KB;64KB data block;no compression;
Random reads (in disk): 1KB/item*300item/s*50=15MB/s
Random reads (in memory):1KB/item*4000item/s*50=200MB/s
Random writes:1KB/item*2000item/s*50=100MB/s
Sequential reads(in disk):1KB/item*1000item/s*50=50MB/s
Sequential writes:1KB/item*2000item/s*50=100MB/s
先看磁盘中的随机读取性能,由于在DTS的设计中每个随机读写都要读取一个64KB的大块,而磁盘中读取64KB数据时间为:磁盘寻道时间 + 读取时间 = 8ms + 64KB / 50MB/s = 20ms。所以每秒读取300个记录要么是批量读取,要么是异步读取。由于每台机器有12个SATA大容量磁盘,随机读的理论值为12 * 50 = 600个/s。设计为每秒读取300个是考虑到有负载平衡等因素简单地打了个对折。
再看内存中的随机读取。一般来说,内存操作都是每秒1W~10W。由于网络发送小数据有较多overhead且DTS内存操作有较多的内存开销,所以保守设计为单机每秒读取4000个记录。
其它的可类似分析。性能分析可能会很复杂,因为不同的情况下决定性能的瓶颈不一样,有的时候是网络,有的时候是磁盘,有的时候甚至是机房的交换机。这种性能分析的经验是需要慢慢积累的。
最后,我们再看看一个例子。该程序R是一个MapReduce应用,MapReduce可以简单地分为几个过程:Map处理时间 + shuffle和排序时间 + reduce处理时间,虽然shuffle、map处理和排序可以部分并行,但性能估算的时候不必考虑。假设50台机器,原始输入为50G,30个策略时R应用的map处理时间为100s,R应用的reduce处理时间为60s,shuffle的中间结果数据量为300G,reduce输出的最终结果大小为600M。
Map处理时间 = 输入读取时间 + Map处理时间 + 输出中间结果时间
其中,输入读取时间 = 50G / 2.5G = 25s (50台机器,假设每台机器读取带宽为50M/s),
R应用的map处理时间 = 60s,
输出中间结果时间 = 300G / 15G = 20s (50台机器,每台机器12个磁盘,假设用满6个磁盘,带宽为6 * 50M = 300M)
所以,Map处理时间 = 25s + 60s + 20s = 105s
Shuffle和排序时间 = shuffle时间 + 排序时间
其中,shuffle时间 = 300G / 2G = 150s (50台机器,假设每台机器的读取和写入带宽均为40M,单机总带宽为80M)
排序时间 = 单机排序6G的时间,假设每条记录为1KB = 排序比较时间 + 访问时间,约为25s
所以,shuffle和排序的时间 = 150s + 25s = 175s
Reduce处理时间 = R应用的reduce处理时间 + 最终结果输出时间
其中,R应用的reduce处理时间 = 100s,
最终结果输出时间 = 600M / 500M (50台机器,单机写DFS假设时间为10M/s) = 1s (忽略)
所以,R一遍MapReduce的估算时间 = Map处理时间 + shuffle和排序时间 + Reduce处理时间 = 105s + 175s + 100s = 380s,当然,MapReduce过程中还有框架的开销和其它应用的影响,我们可以简单地认为影响为20%,所以总时间 = 380s + 380s * 20% = 456s,约为7~8 min。
当然,R实际的性能估算不会如此简单,实际估算时需要考虑每台机器上启动的Map和Reduce个数等因素,且需要根据实验的结果不断地验证和重新调整估算。但是,我们至少可以保证,估算的结果和实际不会相差一个数量级,估算结果可以用来指导初期的设计和Map/Reduce Worker的个数、Map/Reduce任务数选择,评估应用的可优化空间并作为向MapReduce框架提供小组提出需求的依据。
性能估算是大规模系统设计中较难掌握的技能,开始性能估算时可能估计得很不准,不过不要气馁,通过在项目中不断练习,大规模系统的分析和设计能力可以提升一个档次。
给定一个问题,往往会有多种设计方案,而方案评估的一个重要指标就是性能,如何在系统设计时估算而不是程序执行时测试得到性能数据是系统架构设计的重要技能。性能估算有如下用途:
1) 多种设计方案选择;
2) 评价程序实现是否足够优化;
3) 向框架/服务提供方提出性能要求的依据;
通过查看程序运行时CPU及网络的使用情况来评价程序是否足够优化,这也是一种很重要的方法。然而,这种方法掩盖了不优化的实现,如O(N)的算法被错误实现成O(N^2),网络收发冗余数据等。
性能评估需要假设程序的执行环境,如集群规模及机器配置,集群上其它服务占用资源的比例。首先,我们需要知道一些常见硬件的大致性能参数:
L1 cache reference 0.5ns
Branch mispredict 5ns
L2 cache reference 7ns
Mutex lock/unlock 100ns
Main memory reference 100ns
Send 1M bytes over 1Gbps network 10ms
Read 1M sequentially from memory 0.25ms
Round trip within data center 0.5ms
Disk seek 8~10ms
Read 1MB sequentially from disk 20~25ms
标记为红色性能参数比较常用,其中,磁盘的性能指标专指分布式平台专用的大容量磁盘,寻道时间为8~10ms,顺序读取速率为40~50MB。某些产品线使用SCSI磁盘或者Flash盘,性能较好,评估时需查看硬件的性能参数。磁盘和网络都有一个特征,一次读写的数据量越大性能越好,这是由硬件特征及底层软件算法决定的,如tcp慢连接和磁盘寻道时间长。
对硬件性能指标有了初步认识以后,我们可以做出一些简单的判断,如:
某后台开发同学:某检索程序A单客户端同步读取每秒可以达到18000/s。
问:是否批量读取?
答:是,每批读取10个记录。
由于tcp Round trip时间为0.5ms,读取请求个数的理论极限为2000/s,而A的开发同学却说单客户同步读取可以达到18000/s,可以断定A开发同学指的是批量读取方式。且这已经是单机能够做到的极限值了。
下面我们通过几个实例说明如何进行性能评估。
1. 1GB 的4字节整数,内存排序时间为多少?
拿到这个问题,我们往往会计算CPU运算次数,如快排的运算次数为1.4 * N * log(N),其中1.4为快排的系数,再根据CPU的运算频率计算出排序耗时。不过这种方法很土也不是很准,Jeff Dean告诉我们可以这样估算:排序时间 = 比较时间(分支预测错误) + 内存访问时间。快排过程中会发生大量的分支预测错误,所以比较次数为2^28 * log (2^28) ≈ 2^33,其中约1/2的比较会发生分支预测错误,所以比较时间为1/2 * 2 ^ 32 * 5ns = 21s,另外,快排每次找到分割点都需要一遍内存移动操作,而内存顺序访问性能为4GB/s,所以内存访问时间为28 * 1GB / 4GB = 7s。因此,单线程排序1GB 4字节整数总时间约为28s。
2. DTS设计的性能指标分析
DTS总体设计中给出的性能指标为:
系统配置:50台4核8GB内存12路SATA硬盘,同样数量的客户端;
Table:row name:16-byte,column:16-byte,value:1KB;64KB data block;no compression;
Random reads (in disk): 1KB/item*300item/s*50=15MB/s
Random reads (in memory):1KB/item*4000item/s*50=200MB/s
Random writes:1KB/item*2000item/s*50=100MB/s
Sequential reads(in disk):1KB/item*1000item/s*50=50MB/s
Sequential writes:1KB/item*2000item/s*50=100MB/s
先看磁盘中的随机读取性能,由于在DTS的设计中每个随机读写都要读取一个64KB的大块,而磁盘中读取64KB数据时间为:磁盘寻道时间 + 读取时间 = 8ms + 64KB / 50MB/s = 20ms。所以每秒读取300个记录要么是批量读取,要么是异步读取。由于每台机器有12个SATA大容量磁盘,随机读的理论值为12 * 50 = 600个/s。设计为每秒读取300个是考虑到有负载平衡等因素简单地打了个对折。
再看内存中的随机读取。一般来说,内存操作都是每秒1W~10W。由于网络发送小数据有较多overhead且DTS内存操作有较多的内存开销,所以保守设计为单机每秒读取4000个记录。
其它的可类似分析。性能分析可能会很复杂,因为不同的情况下决定性能的瓶颈不一样,有的时候是网络,有的时候是磁盘,有的时候甚至是机房的交换机。这种性能分析的经验是需要慢慢积累的。
最后,我们再看看一个例子。该程序R是一个MapReduce应用,MapReduce可以简单地分为几个过程:Map处理时间 + shuffle和排序时间 + reduce处理时间,虽然shuffle、map处理和排序可以部分并行,但性能估算的时候不必考虑。假设50台机器,原始输入为50G,30个策略时R应用的map处理时间为100s,R应用的reduce处理时间为60s,shuffle的中间结果数据量为300G,reduce输出的最终结果大小为600M。
Map处理时间 = 输入读取时间 + Map处理时间 + 输出中间结果时间
其中,输入读取时间 = 50G / 2.5G = 25s (50台机器,假设每台机器读取带宽为50M/s),
R应用的map处理时间 = 60s,
输出中间结果时间 = 300G / 15G = 20s (50台机器,每台机器12个磁盘,假设用满6个磁盘,带宽为6 * 50M = 300M)
所以,Map处理时间 = 25s + 60s + 20s = 105s
Shuffle和排序时间 = shuffle时间 + 排序时间
其中,shuffle时间 = 300G / 2G = 150s (50台机器,假设每台机器的读取和写入带宽均为40M,单机总带宽为80M)
排序时间 = 单机排序6G的时间,假设每条记录为1KB = 排序比较时间 + 访问时间,约为25s
所以,shuffle和排序的时间 = 150s + 25s = 175s
Reduce处理时间 = R应用的reduce处理时间 + 最终结果输出时间
其中,R应用的reduce处理时间 = 100s,
最终结果输出时间 = 600M / 500M (50台机器,单机写DFS假设时间为10M/s) = 1s (忽略)
所以,R一遍MapReduce的估算时间 = Map处理时间 + shuffle和排序时间 + Reduce处理时间 = 105s + 175s + 100s = 380s,当然,MapReduce过程中还有框架的开销和其它应用的影响,我们可以简单地认为影响为20%,所以总时间 = 380s + 380s * 20% = 456s,约为7~8 min。
当然,R实际的性能估算不会如此简单,实际估算时需要考虑每台机器上启动的Map和Reduce个数等因素,且需要根据实验的结果不断地验证和重新调整估算。但是,我们至少可以保证,估算的结果和实际不会相差一个数量级,估算结果可以用来指导初期的设计和Map/Reduce Worker的个数、Map/Reduce任务数选择,评估应用的可优化空间并作为向MapReduce框架提供小组提出需求的依据。
性能估算是大规模系统设计中较难掌握的技能,开始性能估算时可能估计得很不准,不过不要气馁,通过在项目中不断练习,大规模系统的分析和设计能力可以提升一个档次。