目录
evenodd 优化记录
正在参加比赛,要求写一个 evenodd(一种校验算法,用在 raid 里时可以在掉两块盘的情况下恢复数据)的实现。在和队友对着论文实现了正确的功能后,终于能做紧张刺激的性能优化了!
机器
2核4G,硬盘带宽捉急。
性能分析
perf 报告
一、write 操作,将数据写入 raid:
Samples: 103K of event 'cpu-clock', Event count (approx.): 25916500000
Overhead Command Shared Object Symbol
80.56% swapper [kernel.kallsyms] [k] native_safe_halt
5.87% evenodd evenodd [.] cook_chunk_r2
1.97% evenodd [kernel.kallsyms] [k] copy_user_enhanced_fast_string
0.77% evenodd [kernel.kallsyms] [k] _raw_spin_unlock_irqrestore
0.64% evenodd evenodd [.] cook_chunk
0.60% evenodd [kernel.kallsyms] [k] __block_commit_write.isra.36
0.44% evenodd libc-2.28.so [.] write
0.35% evenodd [kernel.kallsyms] [k] get_page_from_freelist
0.30% evenodd [kernel.kallsyms] [k] __radix_tree_lookup
0.29% evenodd [kernel.kallsyms] [k] __add_to_page_cache_locked
2、read 操作,从坏掉两个盘的 raid 中读取数据:
Samples: 89K of event 'cpu-clock', Event count (approx.): 22494500000
Overhead Command Shared Object Symbol
77.43% swapper [kernel.kallsyms] [k] native_safe_halt
8.91% evenodd evenodd [.] try_repair_chunk
2.20% evenodd [kernel.kallsyms] [k] copy_user_enhanced_fast_string
0.90% evenodd [kernel.kallsyms] [k] _raw_spin_unlock_irqrestore
0.66% evenodd [kernel.kallsyms] [k] __block_commit_write.isra.36
0.48% evenodd libc-2.28.so [.] read
0.39% evenodd [kernel.kallsyms] [k] get_page_from_freelist
0.39% evenodd [kernel.kallsyms] [k] __radix_tree_lookup
0.29% evenodd [kernel.kallsyms] [k] __add_to_page_cache_locked
0.23% kswapd0 [kernel.kallsyms] [k] _raw_spin_unlock_irqrestore
- repair 操作,修复坏掉两个盘的 raid:
Samples: 24K of event 'cpu-clock', Event count (approx.): 6158000000
Overhead Command Shared Object Symbol
50.22% swapper [kernel.kallsyms] [k] native_safe_halt
34.35% evenodd evenodd [.] try_repair_chunk
3.76% evenodd [kernel.kallsyms] [k] copy_user_enhanced_fast_string
1.56% evenodd libc-2.28.so [.] read
0.97% evenodd [kernel.kallsyms] [k] __radix_tree_lookup
0.61% evenodd [kernel.kallsyms] [k] find_get_entry
0.56% evenodd libc-2.28.so [.] _IO_fread
0.38% evenodd [kernel.kallsyms] [k] _raw_spin_unlock_irqrestore
0.36% kswapd0 [kernel.kallsyms] [k] _raw_spin_unlock_irqrestore
0.28% evenodd [kernel.kallsyms] [k] get_page_from_freelist
总结
这里的 evenodd 实现是一个菜菜的单线程简单的实现,几乎没做性能优化。
根据 perf 报告:
耗时最多的 native_safe_halt
函数除了持续调用 HTL
指令等中断之外什么也不会干,这里应该只是在等 io 而已。这部分应该是没法优化的。
耗时第二多的 try_repair_chunk
函数是 evenodd 实现里的一个函数,用来尝试修复一个数据块。在它耗时占比最高的测试中,也仅仅占到了 34.35% 的 cpu 时间,低于 native_safe_halt
所占用的 50.22% 的时间。
其他部分没什么亮点了。
根据分析,我们的首要任务是把读写和计算并行化,使其可以同时执行。这样就可以将 try_repair_chunk
所耗费的时间塞到当前执行 native_safe_halt
的时间里。其次是要优化磁盘 IO 速率。最后才是想办法优化 try_repair_chunk
函数的计算速率。不过如果不能将磁盘 IO 的时间降低到比计算时间少的程度,优化 try_repair_chunk
是没有意义的。
优化记录
加大缓冲区
考虑到对所有文件的读写都是顺序执行的,缓冲区对性能影响非常显著。所以尝试加大缓冲区,好主意!
我们共有约 4GB 内存,程序运行时并不会分配很大的内存(因为设计时程序是逐数据块进行数据处理的,一个数据块不会太大),考虑到质数 p 最大仅为 100,所以即使给每个文件分配 16MB 缓冲区也没有问题。为了保险起见,分配 8MB。
查出下文档才知道,原来 setvbuf
函数的第二个参数设置成 NULL
的时候可以让它自动分配内存。真方便