唐杰忠是谁?唐杰忠是我国著名相声演员,先后与马季和姜昆合作。那么, 唐杰 又是谁? 唐杰现任 赛灵思(Xilinx)数据中心架构师,和冬瓜哥的Title是一样的,真是门当户对啊。鉴于老唐的长相不太好贴上来怕吓坏各位,冬瓜哥的也一样对不起观众,所以就不贴照片了。
事情的起因是这样的,在ssdfans群里,冬瓜哥和往常一样在帮助大家回答问题。在回答一个SSD上文件系统mount的参数的问题时,按照唐杰的说法“俺不知深浅地插了一句嘴,该打。然后就为了拍瓜哥一个马屁,就说瓜哥应该写一个SSD性能测试的指导,结果瓜哥说自己不是专业人士不能写,俺又不明就里地说俺可以写一个”。冬瓜哥一看唐杰这老哥们挺能装逼的,于是壮了壮胆,顺嘴来了一句:“如果唐总写了,鄙人就也写一篇作为回应”,结果唐杰这哥们还真写了,40分钟之后,这老哥竟然发了个word文档到群里,并自称“快枪手”,哎呦我擦,这哥们逼格彰显啊!冬瓜哥既然有言在先,那就得接这个大招了。唐总把自己的文章称为砖,说是想以此抛砖引冬瓜哥的玉。
结尾,唐总还没忘了装个逼,其声明了“ 在谈这个具体内容之前,俺要强调,俺的砖全是脑中所记,因为不方便上网,而且也是砖,就比较散漫,写到哪里算哪里了”。哎呦,这简直就是在暗示冬瓜哥说:你小子如果敢去网上参考一堆资料,那就是没逼格! 行嘞,冬瓜哥当然不用去网上参考,这一点可以保证,下面的回应文字都是冬瓜哥直接脑出。冬瓜哥就佩服直接脑出的人,因为脑出需要足够的积累和逻辑思维,对于那些拿着别人做好的ppt每走一页加上点文字再走一页的,噗~~。
好了,冬瓜哥这就来和唐总说个相声!。文中黑色字体是唐总原文,蓝色字体是冬瓜哥的文字。
----------------唐总 文章开始 ----------------
工具篇:
Fio 目前最常用的性能测试工具,主要是linux下。这个工具是目前我用过的最能生成IO请求的工具。Fio可以用比较小的代价在短时间能生产IO,这个能力的确没有其他工具可以比。因此文中缺省的测试工具就是它了。
Vdbench:SUN遗留下来的工具,因为基于Java,他的可移植性比fio好,同时支持压缩和dedup的测试,因此也是企业测试的首选。
Iometer:Windows平台为主,因为是gui的,因此脚本化比较难,之前windows比较流行的时候,用的多,现在在数据中心就呵呵了。
SQLIO和orion都是数据库厂商的工具,主要测试8K的随机I/O和1M的顺序I/O,是DBA的最爱,作为SSD性能狗必须跪舔。
Sysbench因为可以测试MySQL,也是新贵的最爱,要知道霸爷到ali的第一个blog就是sysbench.
Workload篇:
I/Osize: 4K 是主流,8K是MSSQL和Oracle,16K是MysqlInnodb。
QueueDepth:这个缺省是32,因此目前的SSD,特别是SATA的SSD,NCQ是32,因此不能太少。 唐总,这个地方您老的逼格应该深入发挥一下才是啊,可能是为了追求快抢快射而省略了吧,鄙人就帮您老补充补充吧。SATA盘上的Queue Depth的确是32,对于固态盘来讲32的确太低,这个没办法。但是,内核IO协议栈里的QD可并不是说必须与SATA盘上的QD匹配且就算再大也没用。每一层的QD其实是独立的,因为这些层次间的Queue都是异步联动的。比如如果内核里QD=SATA盘NCQ的Depth=32,那么一旦Host端运行的线程较多,在IO线程之间穿插了不少计算线程的话,那么很有可能会出现内核队列被充满后,线程轮转到计算线程,期间又被消耗空并且持续为空一段时间的情况,从而导致吞吐量瞬时下降。如果内核QD调节为比如256,那么就可以在计算线程运行期间在队列中继续保有IO,维持底层的吞吐量。也就是说,队列深度越高,让底层吞吐量持续平稳的几率越大,当然这都得看上层应用的环境,多少个IO线程,每个IO多少个outstanding io depth等。
随着队列长度的增加,吞吐量不断上升到一个极限便保持不变,达到了底层的最大并发度;而IO平均时延则加速上升,
大到一定程度之后,远超过底层的吞吐量极限,此时吞吐量保持不变,IO平均时延则线性上升(以每个IO时延的0.5倍为步进累积)。排队能够让底层充分的并发,不管是物理并发(真的有多条路)还是逻辑并发(只有一条路但是是流水线处理方式),代价就是平均时延上升。在 《【冬瓜哥手绘】IO时延你被骗了多久》 一文中,大家可以看到单笔IO的时延是固定的,多笔IO排队之后,随着队列长度加长,平均时延就会上去,其消耗在了等待时间上,而执行时间是不变的。
Numjobs:生成I/O的engine的数量,如果thread=1就是几个线程,否则就是进程,建议线程。Numjobs可以等于CPU的core就行,不算HT。
Numjobs和Queue Depth定义了I/O的workload的压力,基本上是 fio numjobsXQD = vdbench的thread。 唐总,谢谢哈!这里您老又留给冬瓜哥装逼的机会了,您老应该自己装才对啊,这样冬瓜哥就很难接招了。这里有个很明显的疑问,到底是用更多的线程数量而每个线程的QD或者叫Outstanding IO数量少一些呢,还是用更少的线程数量但是每个线程发出多一些的Outstanding IO呢?两者相乘的总QD是一样的,如何选择呢?这得从内核协议栈的队列数量说起。Linux老内核里的SCSI协议栈里为每个块设备只维护了一个总队列,位于块层,由IO Scheduler负责入队,当然IO Scheduler上方会维护多个子队列以供接收多个进程的IO然后再以对应的策略向总队列中入队。最终的总队列深度在256附近。所以,多个线程会将IO入队到同一个队列,此过程就必须加锁,线程数越多,相对锁效率越低;另外,加锁的变量会在多个CPU核心的缓存内形成乒乓效应,因为每次更改某个变量会导致对应缓存行变为M(Modified)状态,同时会有一笔广播发送到其他核心将对应缓存行改为I(Invalid)状态,其他核心抢锁时,该缓存行会从M态的缓存转发给I态的缓存并将两边同时改为S(Share)态,过程很耗时间。所以多CPU芯片间的乒乓效应会导致更高的时延。综上所述,用少量线程同时每个线程outstanding io数量较多这种方案更好。
I/O 引擎: 基本上是libaio,这个是冲IOPS的,对于延时为主的测试,建议psync。 嗯,唐总说的好,但是唐总显然是故意又留了让冬瓜哥装逼的机会。说到同步和异步IO,这里有个高逼格的知识可能不少人不知道。冬瓜哥就给来说道说道。异步IO为什么能冲IOPS,就是因为两个原因:IO路径是异步流水线化的、IO路径上也有物理并发的。前文中提到过,流水线并不能降低时延,其只能增加逻辑并发量。当然,如果上层某笔操作底层被分割为多笔操作,那么这多笔操作如果并发执行,结果就会体现为该上层操作的时延降低对应的倍数,相对于非流水线处理来讲。不过对于底层的IO来讲,该规则不适用,底层一笔IO分隔开并发执行相比不分割,其效果几乎没有差别。为什么唐总说如果测时延或者保证时延最低要用同步IO方式呢,因为单线程同步IO可以保证IO路径被该IO独占,没有排队,上文中也说到了排队会以0.5倍固有时延步进抬升。所以,如果你的应用只能发出同步IO,那么对不起,IO路径上的并发度的效果将荡然无存,没有用,增加同步IO场景的性能,只能通过降低时延的方式,将协议栈变短变薄,底层最好直接用SSD。这就是很多系统在压测时候表现彪悍,但是实际场景下惨不忍睹的一个最关键原因,时延太高。
DirectI/O: 这个是本地测试标配,但是要注意网络I/O可能只支持buffer I/O。 唐总,NFS路径也是可以DirectIO的。Direct IO可以绕过系统page cache,避免一些由于IO不对齐导致的效率问题,以及降低在不命中时的时延。通常使用DIO的应用自身会在用户态维护缓存,page cache这点效果不会增加多少效果,反而增加了时延。
随机顺序和IO比例方面:
1. 测试建议先做随机,再做顺序,因为顺序可能有cache的影响。先做写,再做读,因为SSD的特性,写入量大之后会性能下降。
2. I/O读写比例,数据库基本是7:3, 9:1, 对于网络存储,建议的比例是5:5
文件系统篇:
1. Ext2无视,基本上是ext3和ext4。缺省的格式化流行,如果基于md,要考虑I/O的对齐问题。
2. Mount的时候,不管XFS,EXT3/4都建议noatime,noadirtime和nobarrier。Discard这东西还是免了,真心不知道trim的用处。 冬瓜哥来解释一下这些参数。noatime和noadirtime就是当应用访问文件或者目录的时候(包括Open( )/Read( )/Write( )等),文件系统会更新对应文件/目录的访问时间(access time,atime),这两个参数就是关闭这个步骤,从而降低访问时延。 唐总,为啥你会说不知道Trim的用处呢,Trim能让SSD释放出更多空块,会提升写性能啊。不过Trim是否打开并不影响读写IO速度,但是会影响删文件、截断(Truncate)文件的速度。Barrier的意思就是屏障,有些应用为了保证数据的时序一致性,会在关键时刻点把之前已经下发的但是还在缓存中并未刷盘的IO强制刷下去之后,再发出关键的一笔IO,该IO必须在之前的所有IO完成后才可以刷盘,否则由于底层的的缓存以及非原子性操作,很有可能出现该IO刷了盘而其他IO没刷盘之前系统掉电的情况,此时底层系统IO时序不一致。所以Barrier操作底层对应了大量的write through模式的写IO,以及IO形成顺序化提交,具有了同步IO的特征,同步IO模式无法发挥出系统的吞吐量。
3. 其他的,在测试文件系统的时候,要注意在写入的时候会有锁的问题,因此建议使用nrfiles来生成多个文件。 如果是单纯的覆盖写,不需要锁,谁写了算谁的,FS不会在这个层面加锁,只有对元数据操作的时候需要加锁,比如扩充/截断了文件的长度等等。对覆盖写操作的锁需要由应用显式的来调用lock()、lockfile()等功能。
操作系统篇:
1. 对于基于SATA的SSD,对于block层的修改是必须的,必须参考之前LSI nytro的推荐,基本上是:
a) 增大queue的深度
b) Disable预读
c) Disable和随机发生器的关系
唐总,什么是“随机发生器”?这是哪家的黑科技啊。
d) 还有就是IO affinity, 建议使用的配置是使用生成I/O请求的CPU来响应I/O,降低context 切换的影响。
2. 比较懒的可以用RHEL的tuned-adm来设置。
3. 这里还有一个就是操作系统的I/O监控:
a) Iostat–dmx /dev/sda 1 这个是必须的命令
b) Mpstat–P ALL 1, 看CPU的使用情况,如果都在softirp,说明你I/O生成有问题,使用numactl来控制。如果都在iowait,说明你的I/O设备的确不行。如果都在sys,说明跑的很正常。不可能都在user,因为fio的I/O请求的数据直接就丢掉了,不可能有usr。
冬瓜哥给大家普及普及。softirp表示软中断,出现在IO完成处理的下半部分。硬中断和软中断相当于中断处理流程上的两步流水线,第一步要求中断服务程序很迅速的响应外部硬件的硬中断,第二步则由中断服务程序调用下游的处理逻辑继续处理该中断的后续步骤,这就是softirq,后台异步的处理IO完成流程。softirq是目前多数高速外部IO设备,包括网卡的惯用方式之一,也就是不可避免的必须有。但是如果softirq只在一个cpu上执行,性能就比较差,可以使用IPI (Inter Processor Interrupt)也就是处理器间中断的方式向其他CPU派发软中断,从而达到并发执行。所以唐总说的“ 如果都在softirq ”可能是想说“如果softirq都集中在某个cpu”。但是这并不是IO生成有问题,而是中断服务程序处理的不高效,是接收端的事情。至于“ 用numactl来控制 ”,冬瓜哥就纳闷这个场景下与numactl有什么关系了。期待唐总再写个文章说道说道。
其他,iowait表示线程发出IO请求之后被阻塞挂起的情况占了多少比例,这个比例越高,证明底层设备越迟迟无法返回IO,时延太高。sys高说明当前在运行的应用发出大量的系统调用(对外部设备利用比较多)而自己却没干多少事。usr高则相反,自己很勤劳的在计算基本上不去麻烦OS提供的功能,这是个好事情。
c) 高级一点的,可以用perf top看看系统调用的时间,如果都在spinlock上,说明你的IO文件有互斥了。
spinlock证明多个线程在抢锁,存在冲突的共享资源而且频繁访问。但是唐总所说的 ” 说明你的IO文件有互斥了” 就让冬瓜哥不理解了,如果多线程同时范根同一个文件还加锁,这种锁并不是spinlock,而是通过lockfile(), lock()这类调用对文件或者字节加锁,FS加锁之后,是个稳定态,被排他的线程收到锁冲突返回后,一般会被设计为等待一段时间再发起open,write, read,或者lock调用,而不是spinlock,spinlock谁啊,读写文件是系统调用。除非多个线程自己搞了个共享变量,这可以spinlock。
硬件系统篇:
1. BIOS里面,CPU的C states是必须disable,测性能,省毛电。 唐总,最终线上的话,不还是得考虑省电么,不看最终应用场景的测试不都是耍流氓么,就像您老说的“ 一定要上应用,这个是对自己和公司负责的行为”,所以这个C States鄙人觉得就别动了。
2. HT也是要关的,但是在NVME SSD中,HT貌似不错,请自行研究。 唐总卖了个关子,也不知道是深藏不露,还是虚张声势。冬瓜哥也不知道HT对SSD场景的影响所在,这个非常底层,HT是单个核心内部存有两套线程上下文资源,可以用同一个核心将两个线程的机器码穿插交织的执行以充分利用流水线。至于哪种场景在机器码方面更利于交织,冬瓜哥相信唐总也是说不清楚的:)。
3. VT之类,都是必须关的。Dell/HP/IBM都有BIOS推荐的设置。
4. NUMA,这东西对于性能真心没好处,还是一点要避免。BIOS能关就关,不能关的话,用numactl来指定吧。 如果在BIOS里disable NUMA,那么OS将不会感知到底层的CPU/RAM拓扑,内存分配将按照原有方式分配。同时,目前多数服务器是这么实现的:BIOS里关闭NUMA除了会在ACPI表中向OS通告当前系统并不是NUMA架构之外,还会在底层硬件的地址映射电路上对内存地址做interleave,也就是OS分配的比如1MB空间,会被按照缓存行为粒度被条带化到系统的多个NUMA node挂接的物理RAM里,也就是底层强制对OS看到的内存空间做条带化,条带深度是一个cache line,以便均衡OS对RAM的分配。这样做的目的是防止OS顺序分配内存时这些被分配的空间全部挤在一个CPU芯片所挂接的RAM里而导致性能不佳。但是,任何性能问题,都不是某个点优化一下就可以解决的,都是要全局配合的。有些应用抱怨说开了NUMA性能反而下降了,两个原因,要么没精细的定制好策略,要么就是应用的线程和内存访问形式的确不适合NUMA,但是冬瓜哥感觉后者这个原因是个悖论,即使关闭了NUMA,NUMA架构依然是存在的,只不过方式是如上文所述的那样去分配内存,那么也就是说,即使打开NUMA,通过numactl来制定对应的策略,依然可以实现如关闭NUMA类似的效果。所以,别怨NUMA不行,是你自己不行,或者说懒得弄。数据库类程序都推荐关闭NUMA的原因是因为默认情况下打开NUMA之后OS会给每个进程的内存分配到该进程运行的CPU挂接的RAM,容易导致拥挤从而swap,性能下降。关闭NUMA之后,底层硬件对所有地址做条带化,系统内所有RAM得到均衡的利用,降低了swap的负面效果,代价则是访存时延并不是那么好了,但是对吞吐量影响不算大,当然这也这取决于应用的访存模式,缓存命中率,读还是写等一系列因素。
5. 还有就是硬件的中断,不要用irqbalance,一定要用irq_affinity来做mask。 irqbalance是个后台进程,通过对系统核心softirq派发模块动态配置而动态的实现中断均衡。调节irq_affinity那就是全手动静态配置了。
6. 对于PCIE slot,尽可能让两个设备在不同的CPU上,用taskset,或者numactl来进行分离。 NVMe协议栈和底层驱动以及NVMe设备天然支持中断绑定,这样可以节省很多跨CPU芯片间流量。
最后一点,测试只是测试,一定要上应用,这个是对自己和公司负责的行为。 车已经到了嘉兴,俺也吐了两页,希望可以挤出瓜哥的玉了。找个信号好的地就发了。嘿嘿。
-----------唐总原文结束-------------
唐总说了Host内部的一些注意事项,冬瓜哥借唐总的文章再补充补充,介绍一下外部设备对IO方面的优化。
外部设备的IO优化篇:
冬瓜哥以Memblaze的PBlaze4 NVMe SSD为例来向大家介绍。Memblaze(忆恒创源)作为国内知名固态存储提供商,曾经推出过国内第一块NVMe SSDPblaze4,能够达到将近800K的IOPS,成果国内闪存厂商性能的新标杆。
那么Memblaze到底是怎样达到如此高的性能的?除了借助给力的控制器硬件之外,在软件上的几个优化手法,也是值得其他厂商参考借鉴的。
手法之1 :开启了WB缓存,并用一定量的板载电容保障突然掉电后的脏数据刷盘。不少厂商的产品都是RAMless设计,也就是没有板载RAM或者板载RAM非常少,仅用于本地代码运行而不用于数据缓存。
由于数据直接写入后端Flash也可以达到相对于机械盘来讲较高的性能,所以RAMless多数原因是为了节省成本,将FTL表放在主机端RAM,并在主机端实现磨损均衡及垃圾收集计算。但是RAMless的确不利于性能的发挥,尤其是写性能,WB的缓存不仅仅对机械盘存储系统颇有用处(可以说是必须的,否则就没有传统机械盘存储系统),对于Flash介质依然奏效,由于NAND Flash存在写放大,而高压力场景下对写IO抖动非常敏感,所以WB缓存的存在事半功倍。
手法之2 :全局FTL映射。PBlaze4 SSD后端可以在全部的Flash die颗粒之间实现物理页到逻辑页的映射,这样做的好处无疑是可以实现更广泛的全局磨损均衡以及更好的性能。而带来的挑战则是FTL表的维护和搜索过程变得复杂,但是得益于其16核心并行的控制器硬件的强大处理能力,使得PBlaze4可以承接这个挑战。
手法之3 :Flash通道精细化QoS。PBlaze4控制器后端有16个Flash通道,每个通道下可以挂接多大8个die(Lun),每个Lun有一个深度为4的硬件队列。正如上文所述,队列的存在有利于增加吞吐量,但是会对时延带来影响,尤其是对于NAND Flash这种不怕读怕写怕擦的特殊介质,这些不同的IO如果混在杂一起不加以控制的话,会导致严重抖动和不可预测。
-
写一个页: 1.5ms, 最差 3 ms
-
写位于pSLC上的元数据页: 500us, 最差 1 ms
-
读一个页: 200 us, 最差 400 us
-
擦一个块:5ms, 最差 10 ms
比如,擦除操作需要至少堵住队列5ms,这会严重提升队列后面IO的时延。PBlaze4的固件利用了16核心中的2个核心来专门做IO Scheduler并向Lun队列中填充IO,每个Scheduler各管一半的后端通道。其IO Scheduler的策略是:
-
每个队列中最多2个用户IO和2个元数据写IO
-
队列中不能同时出现用户IO写与块擦除IO
其次,了解IO Scheduler核心思想的人都清楚,IO写比IO读往往更容易完成,尤其是下游IO路径上有WB缓存的场景下,这样的话随着写IO密集应用发送更多的写IO入队,读IO可能会被饿死导致应用IO超时。PBlaze4的Scheduler采用 读优先策略,但也不绝对,否则会影响写性能,其内部采用特定算法,在一定场合下会优先,且适当照顾到写和擦除。
另外,由于PBalze4在Flash颗粒间做了Raid,IO Scheduler还要保证尽量整条写,也就是针对多个Die的条带写IO尽量并行下发,从而更快的释放内部的XOR Buffer。
手法之4 :全流水线负反馈控制。为了进一步保证平稳的IO时延,PBalze4内部采取了全流水线负反馈控制技术。写时延抖动的根因是由于内部的GC垃圾回收过程,也就是块擦除IO,每个擦除动作需要5~10ms结束,严重影响IO时延。负反馈技术根据当前IO时延作为输入,反馈到计算引擎,调节GC的频率,从而动态维持写时延的恒定。
可以看到采用了该技术之后,IO写时延非常平稳。
最后,是砖是玉都不重要,都是带引号的。期待唐老总接招。
其他相关阅读(点击可直接进入):
《【冬瓜哥画PPT】最完整的存储系统接口/协议/连接方式总结》