我们在做块设备调优的时候, 我们关心的是块设备是如何被访问的,也就是访问模式(比如说每次从什么地方读,每次读多少块,热点在哪里等),至于每次读写的什么数据我们并不关心. 这些模式当然可以自己去构造,但是如果能把真实应用的访问模式记录下来,并且在调优的时候能重放,我们就可以一遍又一遍的调试直到达到最佳的性能.
这个事情听起来貌似很麻烦,对吧? 幸运的是fio配合blktrace可以作这样的事情. 流程是blktrace负责录制真实应用对设备的访问模式, fio负责读入这些模式, 同时重放模拟对设备的访问.
背景资料:
blktrace使用可以参看这里
fio使用可以参看这里
在做演示前需要强调的是: 对设备的读写是模拟它的模式, 所以会破坏原来的数据,请谨慎, 我也是用虚拟的设备作演示的:
我们要演示的设备是 /dev/ram0, 设备大小128M:
DISTRIB_CODENAME=maverick |
DISTRIB_DESCRIPTION="Ubuntu 10.10" |
$ sudo blockdev --getsize /dev/ram0 |
在终端#1里面运行blktrace捕获设备的访问模式:
$ sudo blktrace /dev/ram0 |
在终端#2里面运行应用程序,这里只是简单的运行下dd在设备上写入64K数据:
$ sudo sudo dd if=/dev/zero of=/dev/ram0 count=16 bs=4096 oflag=direct |
65536字节(66 kB)已复制,9.219e-05 秒,711 MB/秒 |
回到终端#1:
按下CTRL+C终止blktrace的运行, 可以看到该设备的录制文件有了.
ram0.blktrace.0 ram0.blktrace.1 |
$ blkparse ram0 -d dd.bin >/dev/null |
$ sudo fio --name=replay --filename=/dev/ram0 --thread --direct=1 --read_iolog=dd.bin |
replay: (g=0): rw=read, bs=4K-4K/4K-4K, ioengine=sync, iodepth=1 |
got timestamp notify: 4000001, 0 |
replay: (groupid=0, jobs=1): err= 0: pid=31484 |
read : io=102400 B, bw=50000KB/s, iops=12500 , runt= 2msec |
clat (usec): min=6 , max=13 , avg= 6.56, stdev= 1.42 |
lat (usec): min=7 , max=15 , avg= 7.84, stdev= 1.60 |
write: io=65536 B, bw=32000KB/s, iops=8000 , runt= 2msec |
clat (usec): min=9 , max=39 , avg=11.88, stdev= 7.33 |
lat (usec): min=10 , max=41 , avg=13.06, stdev= 7.53 |
cpu : usr=0.00%, sys=1000.00%, ctx=1, majf=0, minf=5 |
IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0% |
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% |
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% |
issued r/w/d: total=25/16/0, short=0/0/0 |
lat (usec): 10=68.29%, 20=29.27%, 50=2.44% |
Run status group 0 (all jobs): |
READ: io=100KB, aggrb=50000KB/s, minb=51200KB/s, maxb=51200KB/s, mint=2msec, maxt=2msec |
WRITE: io=64KB, aggrb=32000KB/s, minb=32768KB/s, maxb=32768KB/s, mint=2msec, maxt=2msec |
ram0: ios=0/0, merge=0/0, ticks=0/0, in_queue=0, util=-nan% |
如果我们用strace来跟踪会看到以下的系统调用, 确认我们的重放是成功的:
…
31350 open(“/dev/ram0″, O_RDWR|O_DIRECT|O_NOATIME) = 8
31350 ioctl(8, BLKFLSBUF, 0xffffffffffffffff) = 0
31350 fadvise64(8, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
31350 lseek(8, 0, SEEK_SET) = 0
31350 write(8, “\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0″…, 4096) = 4096
31350 write(8, “\30\260!\6\233\221\264\210\0036i\325\260u\331\17\300\306A)\324\307\221\17\3308\220\v\264\247\363\35″…, 4096) = 4096
…
总结: 该模拟破坏数据有风险, 但是省去了构造应用环境的时间,在调优的时候很有帮助.
玩得开心!