问题描述:
使用4.18与5.10分别测试fio randwrite,有下面几种差异:
在单线程时候,5.10表现比4.18差:4.18 IOPS为289k;5.10 IOPS为234k。
并发时候,5.10表现更好:4.18 IOPS为562k;5.10 IOPS为651k。
上面两种均使用ext4文件系统。但在对裸盘与xfs进行测试时候,发现性能相差不大。
需求 :为什么单线程在ext4文件系统下,5.10性能表现比4.18差?有没有什么解决方案?希望帮忙排查一下。
测试方法
fio -direct=1 -iodepth=128 -rw=randwrite -ioengine=libaio -bs=4k -size=2G -numjobs=1 -time_based -runtime=100 -group_reporting -filename=./test -name=Rand_write_Testing
4.18:
5.10:
5.10
4.18
5.10
4.18
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
抓一下4.18和5.10 的火焰图,另外5.10使用mount -odioread_lock后再对比一下写性能是否有提升。这是4.18->5.10 ext4 dio性能的一个已知差异
你好,不应该是mount dioread_nolock 吗?
5.10不加挂载选项,默认是dioread_nolock,你们测试的时候显示指定了dioread_lock吗
现在测试的mount参数(火焰图测试均基于该参数):
5.10的 mount:/dev/nvme0n1 on /root/test type ext4 (rw,noatime,nodiratime,nodioread_nolock,data=writeback)
5.10的 mount:/dev/nvme0n1 on /root/test type ext4 (rw,noatime,nodiratime,nodioread_nolock,data=writeback)
抓一下all cpu
首先,使用下面命令将fio固定在cpu1上。
fio -direct=1 -iodepth=128 -rw=randwrite -ioengine=libaio -bs=4k -size=2G -numjobs=1 -time_based -runtime=100 -group_reporting -filename=/test/test -name=Rand_write_Testing --cpus_allowed=1
这时的IOPS
Jobs: 1 (f=1): [w(1)][51.0%][w=881MiB/s][w=226k IOPS][eta 00m:49s]
更改kworker/1:1-dio/nvme0n1线程的优先级,默认是0
renice -n 10 -p $(pid)
IOPS会提高
Jobs: 1 (f=1): [w(1)][61.0%][w=1073MiB/s][w=275k IOPS][eta 00m:39s]
同时,将文章中patch编译进5.10中,没有效果
fio -direct=1 -iodepth=128 -rw=randwrite -ioengine=libaio -bs=4k -size=2G -numjobs=1 -time_based -runtime=100 -group_reporting -filename=/test/test -name=Rand_write_Testing --cpus_allowed=1
我用最新的OLK 5.10没有复现出对应现象,性能看起来是正常的
Jobs: 1 (f=1): [w(1)][8.0%][w=1277MiB/s][w=327k IOPS][eta 01m:32s]
4188 root 20 0 394116 2944 1320 R 99.3 0.0 0:14.63 fio
3819 root 20 0 0 0 0 R 21.3 0.0 0:09.50 kworker/36:2-dio/nvme0n1
要不你们用最新的OLK 510试一下,如果性能提上去了,二分找一下是哪个补丁引入的
5.10.0-136.14.2.90.oe2203sp1.x86_64 我们用的是社区的这个版本,请问你这里用的具体是哪个版本呢,我们这里对齐版本后看一下。
我用的162d8dcc29a0a3e083d45339f1ba89cdc2129030 !4461: netfilter: nf_tables: reject QUEUE/DROP verdict parametersnetfilter: nf_tables: reject QUEUE/DROP verdict parameters
5.10.0-136.14.2.90.oe2203sp1.x86_64 在这个版本上能够复现出来吗
afe6e074c110 (HEAD -> OLK-5.10, origin/OLK-5.10) !4431:v2 patchset for CVE-2023-52340 v2 patchset for CVE-2023-52340
用了OLK-5.10最新后还是可以复现出来
dio kworker和fio还是在同一CPU上吗
是的
15194 root 30 10 0 0 0 R 5.0 0.0 0:10.04 kworker/1:0-dio/nvme0n1
fio -direct=1 -iodepth=128 -rw=randwrite -ioengine=libaio -bs=4k -size=2G -numjobs=1 -time_based -runtime=100 -group_reporting -filename=/test/test -name=Rand_write_Testing --cpus_allowed=1
Jobs: 1 (f=1): [w(1)][13.0%][w=888MiB/s][w=227k IOPS][eta 01m:27s
你提供一份4.18的代码链接和config,我本地试试
https://git.centos.org/rpms/kernel/tree/3c62944e5fa3aca5deea2e1aaedca7ef49c1057c
config就是默认的 kernel-x86_64.config.
我们4.18就是根据这个做的
这个clone下来该怎么用,没有看到SOURCE下有内核源码
我本地搭建了centos版本,但是不开KASAN内核起不来。
我测了一份KASAN版本的数据和Openeuler(KASAN)做对比,没有发现劣化
centos(KASAN) 206MiB/s
OLK(KASAN) 330MiB/s
nice:0
nice:10
ps: perf 抓的指定cpu 10
nice:0
nice:10
ps : perf 抓的fio进程
初步看,5.x的ext4 DIO框架改成iomap后,kworker-dio的任务唤醒次数任务调度频繁,导致cpu上下文切换消耗增加。
cat /sys/block/nvme0n1/queue/rq_affinity 这个值是多少
可以试试 echo 0 > /sys/block/nvme0n1/queue/rq_affinity 后看看有没有性能提升
你好没有用
对于下面这段workqueue的实现代码
static int worker_thread(void *__worker)
{
do {
...
process_one_work
...
} while (keep_working(pool));
...
sleep:
schedule();
goto woke_up;
}
能否抓一下dio下发相同size(不是time_based,保证完成次数相同,也即process_work的调用次数相同场景下)后,5.10和4.18 的后台dio kworker 分别schedule执行了多少次。
根据火焰图,我猜5.10会执行的更多一些。导致后台worker抢占更多CPU使用率。
对比4.18和5.10 ext4 dio 完成流程,5.10的end_io实现更少(只有一条isize判断语句),理论执行速度更快。由于fio和kworker的优先级一样,基于CFS调度两个任务执行的时间片应该是相同的,因此5.10 worker在做完process_one_work(ext4_dio_write_end_io)后有更多机会做schedule,导致了schedule带来的CPU使用率(因此增加worker的nice值可以减少worker的schedule频率,让前台fio有更多CPU)
4.18
ext4_end_io_dio
ext4_put_io_end
ext4_release_io_end
ext4_finish_bio
5.10
ext4_dio_write_end_io
if (pos > i_size_read(inode))
ext4 dio end io的流程变更是由引入
378f32bab3714f04c4e0c3aee4129f6703805550 (ext4 dio切换iomap)
5899593f51e63dde2f07c67358bd65a641585abb (Fix补丁)
4.18比较难抓到kworker,所以抓了fio进程调用schedule()次数
工具bpftrace,脚本为count_schedule_by_pid.bt:
kprobe:schedule /pid == $1/ {
@count = count();
}
fio测试命令:
fio -direct=1 -iodepth=128 -rw=randwrite -ioengine=libaio -bs=4k -size=2G -numjobs=1 -time_based -runtime=50 -group_reporting -filename=/test/test -name=Rand_write_Testing --cpus_allowed=100
执行脚本
bpftrace count_schedule_by_pid.bt -v $fio_pid
在4.18中,count为@count: 32
在5.10中,count为@count: 8970360
5.10中调低fio nice值后,count为:@count: 1779565
测试方法不知是否准确,如果有更好的测试方法,可以重新测试
我们尝试了使用unbound模式使用kworker,可以实现打散kworker来避免与fio的cpu争抢。改动如下:
diff --git a/fs/direct-io.c b/fs/direct-io.c
index 9dafbb07dd6a..cb056cd21d30 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -577,7 +577,7 @@ int sb_init_dio_done_wq(struct super_block *sb)
{
struct workqueue_struct *old;
struct workqueue_struct *wq = alloc_workqueue("dio/%s",
- WQ_MEM_RECLAIM, 0,
+ WQ_MEM_RECLAIM | WQ_UNBOUND, 0,
sb->s_id);
if (!wq)
return -ENOMEM;
但是在测试中发现下面几种情况,性能可以提高,但是不稳定,具体的:
5.10,unbound模式,设置cpumask
https://gitee.com/mazhenxian/test/blob/master/perf.svg
问题原因:ext4切换iomap后(378f32bab3714f04c4e0c3aee4129f6703805550),aio dio覆盖写场景的完成由 中断上下文处理 变成了 基于workqueue启worker处理,因为多了唤醒kworker的流程并且kworker和fio在同一CPU会竞争计算资源,处理路径相比4.18效率低,表现为IO完成速度慢:
4.18
dio_bio_end_aio
defer_completion = dio->defer_completion || (dio->op == REQ_OP_WRITE && dio->inode->i_mapping->nrpages); // 只有fio -sync或者ext4写入unwritten ext区域才会走kworker路径(详见ext4_dio_get_block_unwritten_async中设置set_buffer_defer_completion 和 get_more_blocks中基于buffer_defer_completion设置dio->defer_completion)
if (defer_completion) {
queue_work(dio->inode->i_sb->s_dio_done_wq, &dio->complete_work);
} else {
dio_complete(dio, 0, DIO_COMPLETE_ASYNC); // 中断上下文完成DIO
}
5.10
iomap_dio_bio_end_io
if (dio->wait_for_completion) { // 非aio场景
} else if (dio->flags & IOMAP_DIO_WRITE) {
queue_work(inode->i_sb->s_dio_done_wq, &dio->aio.work); // aio场景默认都是kworker完成DIO
} else {
iomap_dio_complete_work(&dio->aio.work);
}
对于fio测试,2G文件在nvme可以很快(<2s)完成一次全覆盖写(unwritten -> written),之后的测试都是overwrite。对于4.18都是在nvme irq中完成DIO,对于5.10则是使用kworker完成DIO。由于外部厂商使用的nvme硬件队列数量和CPU数量相同,因此每当DIO完成都会在下发的CPU上处理nvme中断,意味着fio、nvme irq、kworker(5.10)在同一个CPU上执行。5.10使用单独kworker处理DIO完成,nvme irq完成会先唤醒worker,然后worker处理DIO,并且fio和kworker会在同一CPU竞争资源,相比4.18直接在nvme irq完成DIO 5.10效率会降低,因此性能会差。
可以解释之前观测到的现象:
4.18很难抓到dio kworker:只有第一次写才会用kworker处理,后续相同地址的写都是nvme irq完成,因此kworker很难看到。
火焰图中5.10 kworker的schedule占比很高:由于5.10 DIO完成只有一条语句,执行很快,导致kworker大部分时间都在做schedule
修改5.10 kworker nice值可以提升性能:fio和kworker会抢CPU,kworker实际执行时间很短,调低kworker优先级一定程度可以提升fio下发速度
当前linux主线代码和5.10逻辑一样,也存在类似问题。
linux主线修复方法已发社区(不一定被社区接受,kworker完成DIO还有一个原因是要invalidate pages,我把invalidate操作删除了):https://lore.kernel.org/linux-ext4/20240229113849.2222577-1-chengzhihao1@huawei.com/T/#t
OLK 适配补丁(本地物理机测试 效果960MiB/s -> 1372MiB/s)
!4820: ext4: dio: Put endio under irq context for overwrite
5.10与4.18在mysql压测场景下,sysbench 32线程时,perf采集的火焰图链接与diff文件,在文件夹20240311中
https://gitee.com/mazhenxian/test/tree/master/20240311
登录 后才可以发表评论