397 Star 1.4K Fork 1.3K

GVPopenEuler / kernel

 / 详情

在centos7 4.18与openeuler 5.10.0-136.14.2.90.oe2203sp1.x86_64测试nvme fio write时候,使用ext4文件系统存在性能差异

已完成
缺陷
创建于  
2024-02-04 14:43

问题描述:

使用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

ext4单线程下

4.18:
输入图片说明

5.10:
输入图片说明

ext4并发下

5.10
输入图片说明

4.18
输入图片说明

使用xfs单线程

5.10
输入图片说明
4.18
输入图片说明

评论 (37)

mazhenxian 创建了缺陷

Hi mazhenxian, welcome to the openEuler Community.
I'm the Bot here serving you. You can find the instructions on how to interact with me at Here.
If you have any questions, please contact the SIG: Kernel, and any of the maintainers.

openeuler-ci-bot 添加了
 
sig/Kernel
标签
mazhenxian 修改了描述
mazhenxian 修改了描述
mazhenxian 修改了描述
zhangjialin 添加了
 
help-wanted
标签
zhangjialin 关联分支设置为openEuler-22.03-LTS-SP1

抓一下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没有复现出对应现象,性能看起来是正常的

  1. 后台dio kworker的CPU和fio不在同一个CPU上(你们的是在同一个CPU上)
  2. 后台dio kworker CPU占用率没有达到90%,只有20~30%,iops也很快
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 !4461netfilter: 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
perf指定cpu10
nice:10
perf指定cpu10
ps: perf 抓的指定cpu 10

nice:0
offcpu
nice:10
offcpu

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;

但是在测试中发现下面几种情况,性能可以提高,但是不稳定,具体的:

  • 在开numa机器中性能可以提高,且较为稳定,iops大概可以从220k提高到250k,但与4.18仍存在月10K的差距;
  • 在关numa机器中,性能也可以提高,但是不稳定。iops在200-270k中间浮动,且浮动范围较大。
    原因怀疑在关numa中,dio与fio存在跨numa访问,存在性能损失。且kworker漂移到其他cpu,与cpu affinity也有关系。
    请问是否有其他较好解决方案?

问题原因: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

登录 后才可以发表评论

状态
负责人
项目
里程碑
Pull Requests
关联的 Pull Requests 被合并后可能会关闭此 issue
分支
开始日期   -   截止日期
-
置顶选项
优先级
预计工期 (小时)
参与者(4)
5329419 openeuler ci bot 1632792936
C
1
https://gitee.com/openeuler/kernel.git
git@gitee.com:openeuler/kernel.git
openeuler
kernel
kernel

搜索帮助