cfq-iosched preempt issues
queue preemption is good for some workloads and not for others. With commit f8ae6e3eb825, the impact is amplified. I currently have two issues with it:
1. In a multi-threaded workload, each thread runs a random read/write (for example, mmap write) with iodepth 1. I found the queue depth gets smaller with commit f8ae6e3eb825. The reason is write gets preempted, so more threads are waitting for write, and on the other hand, there are less threads doing read. This will make the queue depth small, so performance drops a little. So in this case, speed up write can speed up read too, but we can't detect it.
2. cfq_may_dispatch doesn't limit queue depth if the queue is the sole queue. What about if there are two queues, one sync and one async? If the sync queue's think time is small, we can treat it as the sole queue, because the sync queue will preempt async queue, so we don't need care about the async queue's latency. The issue exists before, but f8ae6e3eb825 amplifies it. Below is a patch for it.
Any idea?
Thanks,
Shaohua
-----------------------------------------------
Subject: cfq-iosched: don't limlit sync queue depth with only one such sync queue
If there are a sync and an async queue and the sync queue's think time is small, we can ignore the sync queue's dispatch quantum. Because the sync queue will always preempt the async queue, we don't need to care about async's latency. In the same way, we can optimize a RT queue too to improve performance. This can fix a performance regression of aiostress test, which is introduced by commit f8ae6e3eb825. The issue should exist even without the commit, but the commit amplifies the impact.
--- block/cfq-iosched.c | 91 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 18 deletions(-)
block/cfq-iosched.c +73 -18
@@ -1150,6 +1150,20 @@ void cfq_unlink_blkio_group(void *key, s spin_unlock_irqrestore(cfqd->queue->queue_lock, flags); } +static bool cfq_have_cfqgs (struct cfq_data *cfqd) +{ +struct hlist_node *pos; +struct cfq_group *cfqg; +int cnt =0 ; + +hlist_for_each_entry (cfqg, pos, &cfqd->cfqg_list, cfqd_node) { + cnt++; +if (cnt >1 ) +break ; + } +return cnt >1 ; +} +#else /* GROUP_IOSCHED */ static struct cfq_group *cfq_get_cfqg (struct cfq_data *cfqd,int create) {
@@ -1169,6 +1183,12 @@ cfq_link_cfqq_cfqg(struct cfq_queue *cfqstatic void cfq_release_cfq_groups (struct cfq_data *cfqd) {}static inline void cfq_put_cfqg (struct cfq_group *cfqg) {} +static inline bool cfq_have_cfqgs (struct cfq_data *cfqd) +{ +return false; +} + +#endif /* GROUP_IOSCHED */ /*
@@ -2381,6 +2401,57 @@ static inline bool cfq_slice_used_soon(sreturn false; } +static unsigned int cfq_queue_max_quantum (struct cfq_data *cfqd, +struct cfq_queue *cfqq) +{ +int sync =cfq_cfqq_sync (cfqq); +enum wl_prio_t prio =cfqq_prio (cfqq); +struct cfq_group *cfqg = cfqq->cfqg; +int sync_queues_cnt, async_queues_cnt; +struct cfq_io_context *cic =RQ_CIC (cfqq->next_rq); + +/* Sole queue user, no limit */ +if (cfqd->busy_queues ==1 ) +return -1 ; + +if (cfq_have_cfqgs (cfqd) || (!sync && prio !=RT_WORKLOAD )) + goto normal; + + sync_queues_cnt = cfqg->service_trees[prio][SYNC_NOIDLE_WORKLOAD ].count + + cfqg->service_trees[prio][SYNC_WORKLOAD ].count; + async_queues_cnt = cfqg->service_trees[prio][ASYNC_WORKLOAD ].count; +/* +* If a queue is a sole sync queue and think time is small, we can ignore +* async queue here and give the sync queue no dispatch limit, because a +* sync queue can preempt async queue. +* +* If the queue is RT, we don't need check BE, because even the +* queue is expired, the dispatcher will select RT queue again next time. +* +* If the queue is BE, we don't check RT here, because dispatcher will +* switch to RT next time, so we at most dispatch one extra request. +*/ +if (((!sync && prio ==RT_WORKLOAD && sync_queues_cnt ==0 && + async_queues_cnt ==1 ) || sync_queues_cnt ==1 ) && +sample_valid (cic->ttime_samples) && + cic->ttime_mean < cfqd->cfq_slice_idle) +return -1 ; +normal: +/* +* We have other queues, don't allow more IO from this one +*/ +if (cfq_slice_used_soon (cfqd, cfqq)) +return 0 ; +else +/* +* Normally we start throttling cfqq when cfq_quantum/2 +* requests have been dispatched. But we can drive +* deeper queue depths at the beginning of slice +* subjected to upper limit of cfq_quantum. +* */ +return cfqd->cfq_quantum; +} +static bool cfq_may_dispatch (struct cfq_data *cfqd,struct cfq_queue *cfqq) {unsigned int max_dispatch;
@@ -2411,25 +2482,9 @@ static bool cfq_may_dispatch(struct cfq_if (cfq_class_idle (cfqq))return false; -/* -* We have other queues, don't allow more IO from this one -*/ -if (cfqd->busy_queues >1 &&cfq_slice_used_soon (cfqd, cfqq)) + max_dispatch =cfq_queue_max_quantum (cfqd, cfqq); +if (max_dispatch ==0 )return false; - -/* -* Sole queue user, no limit -*/ -if (cfqd->busy_queues ==1 ) - max_dispatch =-1 ; -else -/* -* Normally we start throttling cfqq when cfq_quantum/2 -* requests have been dispatched. But we can drive -* deeper queue depths at the beginning of slice -* subjected to upper limit of cfq_quantum. -* */ - max_dispatch = cfqd->cfq_quantum; }/*
Quoted 9 lines · Shaohua Li
queue preemption is good for some workloads and not for others. With commit
f8ae6e3eb825, the impact is amplified. I currently have two issues with it:
1. In a multi-threaded workload, each thread runs a random read/write (for example, mmap write) with iodepth 1. I found the queue depth gets smaller with commitf8ae6e3eb825. The reason is write gets preempted, so more threads are waitting for write, and on the other hand, there are less threads doing read. This will make the queue depth small, so performance drops a little. So in this case, speed up write can speed up read too, but we can't detect it.
I don't fully understand your workload. What is the aio-stress or fio command line/config file?
Quoted 5 lines
2.
cfq_may_dispatchdoesn't limit queue depth if the queue is the sole queue. What about if there are two queues, one sync and one async? If the sync queue's think time is small, we can treat it as the sole queue, because the sync queue will preempt async queue, so we don't need care about the async queue's latency. The issue exists before, butf8ae6e3eb825amplifies it. Below is a patch for it.
I'm not sure I entirely agree with that reasoning. Do you have real workloads that are regressing due to this commit, or is it just these cooked up benchmarks?
Cheers,
Jeff
Quoted 12 lines · Jeff Moyer
Quoted 9 lines · Shaohua Li
queue preemption is good for some workloads and not for others. With commit
f8ae6e3eb825, the impact is amplified. I currently have two issues with it:
1. In a multi-threaded workload, each thread runs a random read/write (for example, mmap write) with iodepth 1. I found the queue depth gets smaller with commitf8ae6e3eb825. The reason is write gets preempted, so more threads are waitting for write, and on the other hand, there are less threads doing read. This will make the queue depth small, so performance drops a little. So in this case, speed up write can speed up read too, but we can't detect it.
Quoted 2 lines
I don't fully understand your workload. What is the aio-stress or fio command line/config file?
ha, sure, this is a fio workload. attached is the fio script. it's basically a simple mmap rand write workload.
Quoted 9 lines
Quoted 5 lines
2.
cfq_may_dispatchdoesn't limit queue depth if the queue is the sole queue. What about if there are two queues, one sync and one async? If the sync queue's think time is small, we can treat it as the sole queue, because the sync queue will preempt async queue, so we don't need care about the async queue's latency. The issue exists before, butf8ae6e3eb825amplifies it. Below is a patch for it.I'm not sure I entirely agree with that reasoning. Do you have real workloads that are regressing due to this commit, or is it just these cooked up benchmarks?
this is an aiostress with option: '-O -l -L -s 1200m -i 1 -r 16k' the random read throughput has impact with preemption.
Thanks,
Shaohua
[global]
direct=0
ioengine=mmap
size=8G
bs=4k
numjobs=1
loops=5
overwrite=1
runtime=600group_reporting
invalidate=0
directory=/mnt/stp/fiodata
file_service_type=random:36
[job_sdb1_sub0]
startdelay=0
rw=randwrite filename=data0/f1:data0/f2:data0/f3:data0/f4:data0/f5:data0/f6:data0/f7:data0/f8
[job_sdb1_sub1]
startdelay=0
rw=randwrite filename=data0/f2:data0/f3:data0/f4:data0/f5:data0/f6:data0/f7:data0/f8:data0/f1
[job_sdb1_sub2]
startdelay=0
rw=randwrite filename=data0/f3:data0/f4:data0/f5:data0/f6:data0/f7:data0/f8:data0/f1:data0/f2
[job_sdb1_sub3]
startdelay=0
rw=randwrite filename=data0/f4:data0/f5:data0/f6:data0/f7:data0/f8:data0/f1:data0/f2:data0/f3
[job_sdb1_sub4]
startdelay=0
rw=randwrite filename=data0/f5:data0/f6:data0/f7:data0/f8:data0/f1:data0/f2:data0/f3:data0/f4
[job_sdb1_sub5]
startdelay=0
rw=randwrite filename=data0/f6:data0/f7:data0/f8:data0/f1:data0/f2:data0/f3:data0/f4:data0/f5
[job_sdb1_sub6]
startdelay=0
rw=randwrite filename=data0/f7:data0/f8:data0/f1:data0/f2:data0/f3:data0/f4:data0/f5:data0/f6
[job_sdb1_sub7]
startdelay=0
rw=randwrite filename=data0/f8:data0/f1:data0/f2:data0/f3:data0/f4:data0/f5:data0/f6:data0/f7
[job_sdc1_sub0]
startdelay=0
rw=randwrite filename=data1/f1:data1/f2:data1/f3:data1/f4:data1/f5:data1/f6:data1/f7:data1/f8
[job_sdc1_sub1]
startdelay=0
rw=randwrite filename=data1/f2:data1/f3:data1/f4:data1/f5:data1/f6:data1/f7:data1/f8:data1/f1
[job_sdc1_sub2]
startdelay=0
rw=randwrite filename=data1/f3:data1/f4:data1/f5:data1/f6:data1/f7:data1/f8:data1/f1:data1/f2
[job_sdc1_sub3]
startdelay=0
rw=randwrite filename=data1/f4:data1/f5:data1/f6:data1/f7:data1/f8:data1/f1:data1/f2:data1/f3
[job_sdc1_sub4]
startdelay=0
rw=randwrite filename=data1/f5:data1/f6:data1/f7:data1/f8:data1/f1:data1/f2:data1/f3:data1/f4
[job_sdc1_sub5]
startdelay=0
rw=randwrite filename=data1/f6:data1/f7:data1/f8:data1/f1:data1/f2:data1/f3:data1/f4:data1/f5
[job_sdc1_sub6]
startdelay=0
rw=randwrite filename=data1/f7:data1/f8:data1/f1:data1/f2:data1/f3:data1/f4:data1/f5:data1/f6
[job_sdc1_sub7]
startdelay=0
rw=randwrite filename=data1/f8:data1/f1:data1/f2:data1/f3:data1/f4:data1/f5:data1/f6:data1/f7
[job_sdd1_sub0]
startdelay=0
rw=randwrite filename=data2/f1:data2/f2:data2/f3:data2/f4:data2/f5:data2/f6:data2/f7:data2/f8
[job_sdd1_sub1]
startdelay=0
rw=randwrite filename=data2/f2:data2/f3:data2/f4:data2/f5:data2/f6:data2/f7:data2/f8:data2/f1
[job_sdd1_sub2]
startdelay=0
rw=randwrite filename=data2/f3:data2/f4:data2/f5:data2/f6:data2/f7:data2/f8:data2/f1:data2/f2
[job_sdd1_sub3]
startdelay=0
rw=randwrite filename=data2/f4:data2/f5:data2/f6:data2/f7:data2/f8:data2/f1:data2/f2:data2/f3
[job_sdd1_sub4]
startdelay=0
rw=randwrite filename=data2/f5:data2/f6:data2/f7:data2/f8:data2/f1:data2/f2:data2/f3:data2/f4
[job_sdd1_sub5]
startdelay=0
rw=randwrite filename=data2/f6:data2/f7:data2/f8:data2/f1:data2/f2:data2/f3:data2/f4:data2/f5
[job_sdd1_sub6]
startdelay=0
rw=randwrite filename=data2/f7:data2/f8:data2/f1:data2/f2:data2/f3:data2/f4:data2/f5:data2/f6
[job_sdd1_sub7]
startdelay=0
rw=randwrite filename=data2/f8:data2/f1:data2/f2:data2/f3:data2/f4:data2/f5:data2/f6:data2/f7
[job_sde1_sub0]
startdelay=0
rw=randwrite filename=data3/f1:data3/f2:data3/f3:data3/f4:data3/f5:data3/f6:data3/f7:data3/f8
[job_sde1_sub1]
startdelay=0
rw=randwrite filename=data3/f2:data3/f3:data3/f4:data3/f5:data3/f6:data3/f7:data3/f8:data3/f1
[job_sde1_sub2]
startdelay=0
rw=randwrite filename=data3/f3:data3/f4:data3/f5:data3/f6:data3/f7:data3/f8:data3/f1:data3/f2
[job_sde1_sub3]
startdelay=0
rw=randwrite filename=data3/f4:data3/f5:data3/f6:data3/f7:data3/f8:data3/f1:data3/f2:data3/f3
[job_sde1_sub4]
startdelay=0
rw=randwrite filename=data3/f5:data3/f6:data3/f7:data3/f8:data3/f1:data3/f2:data3/f3:data3/f4
[job_sde1_sub5]
startdelay=0
rw=randwrite filename=data3/f6:data3/f7:data3/f8:data3/f1:data3/f2:data3/f3:data3/f4:data3/f5
[job_sde1_sub6]
startdelay=0
rw=randwrite filename=data3/f7:data3/f8:data3/f1:data3/f2:data3/f3:data3/f4:data3/f5:data3/f6
[job_sde1_sub7]
startdelay=0
rw=randwrite filename=data3/f8:data3/f1:data3/f2:data3/f3:data3/f4:data3/f5:data3/f6:data3/f7
[job_sdf1_sub0]
startdelay=0
rw=randwrite filename=data4/f1:data4/f2:data4/f3:data4/f4:data4/f5:data4/f6:data4/f7:data4/f8
[job_sdf1_sub1]
startdelay=0
rw=randwrite filename=data4/f2:data4/f3:data4/f4:data4/f5:data4/f6:data4/f7:data4/f8:data4/f1
[job_sdf1_sub2]
startdelay=0
rw=randwrite filename=data4/f3:data4/f4:data4/f5:data4/f6:data4/f7:data4/f8:data4/f1:data4/f2
[job_sdf1_sub3]
startdelay=0
rw=randwrite filename=data4/f4:data4/f5:data4/f6:data4/f7:data4/f8:data4/f1:data4/f2:data4/f3
[job_sdf1_sub4]
startdelay=0
rw=randwrite filename=data4/f5:data4/f6:data4/f7:data4/f8:data4/f1:data4/f2:data4/f3:data4/f4
[job_sdf1_sub5]
startdelay=0
rw=randwrite filename=data4/f6:data4/f7:data4/f8:data4/f1:data4/f2:data4/f3:data4/f4:data4/f5
[job_sdf1_sub6]
startdelay=0
rw=randwrite filename=data4/f7:data4/f8:data4/f1:data4/f2:data4/f3:data4/f4:data4/f5:data4/f6
[job_sdf1_sub7]
startdelay=0
rw=randwrite filename=data4/f8:data4/f1:data4/f2:data4/f3:data4/f4:data4/f5:data4/f6:data4/f7
[job_sdg1_sub0]
startdelay=0
rw=randwrite filename=data5/f1:data5/f2:data5/f3:data5/f4:data5/f5:data5/f6:data5/f7:data5/f8
[job_sdg1_sub1]
startdelay=0
rw=randwrite filename=data5/f2:data5/f3:data5/f4:data5/f5:data5/f6:data5/f7:data5/f8:data5/f1
[job_sdg1_sub2]
startdelay=0
rw=randwrite filename=data5/f3:data5/f4:data5/f5:data5/f6:data5/f7:data5/f8:data5/f1:data5/f2
[job_sdg1_sub3]
startdelay=0
rw=randwrite filename=data5/f4:data5/f5:data5/f6:data5/f7:data5/f8:data5/f1:data5/f2:data5/f3
[job_sdg1_sub4]
startdelay=0
rw=randwrite filename=data5/f5:data5/f6:data5/f7:data5/f8:data5/f1:data5/f2:data5/f3:data5/f4
[job_sdg1_sub5]
startdelay=0
rw=randwrite filename=data5/f6:data5/f7:data5/f8:data5/f1:data5/f2:data5/f3:data5/f4:data5/f5
[job_sdg1_sub6]
startdelay=0
rw=randwrite filename=data5/f7:data5/f8:data5/f1:data5/f2:data5/f3:data5/f4:data5/f5:data5/f6
[job_sdg1_sub7]
startdelay=0
rw=randwrite filename=data5/f8:data5/f1:data5/f2:data5/f3:data5/f4:data5/f5:data5/f6:data5/f7
[job_sdh1_sub0]
startdelay=0
rw=randwrite filename=data6/f1:data6/f2:data6/f3:data6/f4:data6/f5:data6/f6:data6/f7:data6/f8
[job_sdh1_sub1]
startdelay=0
rw=randwrite filename=data6/f2:data6/f3:data6/f4:data6/f5:data6/f6:data6/f7:data6/f8:data6/f1
[job_sdh1_sub2]
startdelay=0
rw=randwrite filename=data6/f3:data6/f4:data6/f5:data6/f6:data6/f7:data6/f8:data6/f1:data6/f2
[job_sdh1_sub3]
startdelay=0
rw=randwrite filename=data6/f4:data6/f5:data6/f6:data6/f7:data6/f8:data6/f1:data6/f2:data6/f3
[job_sdh1_sub4]
startdelay=0
rw=randwrite filename=data6/f5:data6/f6:data6/f7:data6/f8:data6/f1:data6/f2:data6/f3:data6/f4
[job_sdh1_sub5]
startdelay=0
rw=randwrite filename=data6/f6:data6/f7:data6/f8:data6/f1:data6/f2:data6/f3:data6/f4:data6/f5
[job_sdh1_sub6]
startdelay=0
rw=randwrite filename=data6/f7:data6/f8:data6/f1:data6/f2:data6/f3:data6/f4:data6/f5:data6/f6
[job_sdh1_sub7]
startdelay=0
rw=randwrite filename=data6/f8:data6/f1:data6/f2:data6/f3:data6/f4:data6/f5:data6/f6:data6/f7
[job_sdi1_sub0]
startdelay=0
rw=randwrite filename=data7/f1:data7/f2:data7/f3:data7/f4:data7/f5:data7/f6:data7/f7:data7/f8
[job_sdi1_sub1]
startdelay=0
rw=randwrite filename=data7/f2:data7/f3:data7/f4:data7/f5:data7/f6:data7/f7:data7/f8:data7/f1
[job_sdi1_sub2]
startdelay=0
rw=randwrite filename=data7/f3:data7/f4:data7/f5:data7/f6:data7/f7:data7/f8:data7/f1:data7/f2
[job_sdi1_sub3]
startdelay=0
rw=randwrite filename=data7/f4:data7/f5:data7/f6:data7/f7:data7/f8:data7/f1:data7/f2:data7/f3
[job_sdi1_sub4]
startdelay=0
rw=randwrite filename=data7/f5:data7/f6:data7/f7:data7/f8:data7/f1:data7/f2:data7/f3:data7/f4
[job_sdi1_sub5]
startdelay=0
rw=randwrite filename=data7/f6:data7/f7:data7/f8:data7/f1:data7/f2:data7/f3:data7/f4:data7/f5
[job_sdi1_sub6]
startdelay=0
rw=randwrite filename=data7/f7:data7/f8:data7/f1:data7/f2:data7/f3:data7/f4:data7/f5:data7/f6
[job_sdi1_sub7]
startdelay=0
rw=randwrite filename=data7/f8:data7/f1:data7/f2:data7/f3:data7/f4:data7/f5:data7/f6:data7/f7
[job_sdj1_sub0]
startdelay=0
rw=randwrite filename=data8/f1:data8/f2:data8/f3:data8/f4:data8/f5:data8/f6:data8/f7:data8/f8
[job_sdj1_sub1]
startdelay=0
rw=randwrite filename=data8/f2:data8/f3:data8/f4:data8/f5:data8/f6:data8/f7:data8/f8:data8/f1
[job_sdj1_sub2]
startdelay=0
rw=randwrite filename=data8/f3:data8/f4:data8/f5:data8/f6:data8/f7:data8/f8:data8/f1:data8/f2
[job_sdj1_sub3]
startdelay=0
rw=randwrite filename=data8/f4:data8/f5:data8/f6:data8/f7:data8/f8:data8/f1:data8/f2:data8/f3
[job_sdj1_sub4]
startdelay=0
rw=randwrite filename=data8/f5:data8/f6:data8/f7:data8/f8:data8/f1:data8/f2:data8/f3:data8/f4
[job_sdj1_sub5]
startdelay=0
rw=randwrite filename=data8/f6:data8/f7:data8/f8:data8/f1:data8/f2:data8/f3:data8/f4:data8/f5
[job_sdj1_sub6]
startdelay=0
rw=randwrite filename=data8/f7:data8/f8:data8/f1:data8/f2:data8/f3:data8/f4:data8/f5:data8/f6
[job_sdj1_sub7]
startdelay=0
rw=randwrite filename=data8/f8:data8/f1:data8/f2:data8/f3:data8/f4:data8/f5:data8/f6:data8/f7
[job_sdk1_sub0]
startdelay=0
rw=randwrite filename=data9/f1:data9/f2:data9/f3:data9/f4:data9/f5:data9/f6:data9/f7:data9/f8
[job_sdk1_sub1]
startdelay=0
rw=randwrite filename=data9/f2:data9/f3:data9/f4:data9/f5:data9/f6:data9/f7:data9/f8:data9/f1
[job_sdk1_sub2]
startdelay=0
rw=randwrite filename=data9/f3:data9/f4:data9/f5:data9/f6:data9/f7:data9/f8:data9/f1:data9/f2
[job_sdk1_sub3]
startdelay=0
rw=randwrite filename=data9/f4:data9/f5:data9/f6:data9/f7:data9/f8:data9/f1:data9/f2:data9/f3
[job_sdk1_sub4]
startdelay=0
rw=randwrite filename=data9/f5:data9/f6:data9/f7:data9/f8:data9/f1:data9/f2:data9/f3:data9/f4
[job_sdk1_sub5]
startdelay=0
rw=randwrite filename=data9/f6:data9/f7:data9/f8:data9/f1:data9/f2:data9/f3:data9/f4:data9/f5
[job_sdk1_sub6]
startdelay=0
rw=randwrite filename=data9/f7:data9/f8:data9/f1:data9/f2:data9/f3:data9/f4:data9/f5:data9/f6
[job_sdk1_sub7]
startdelay=0
rw=randwrite filename=data9/f8:data9/f1:data9/f2:data9/f3:data9/f4:data9/f5:data9/f6:data9/f7
[job_sdl1_sub0]
startdelay=0
rw=randwrite filename=data10/f1:data10/f2:data10/f3:data10/f4:data10/f5:data10/f6:data10/f7:data10/f8
[job_sdl1_sub1]
startdelay=0
rw=randwrite filename=data10/f2:data10/f3:data10/f4:data10/f5:data10/f6:data10/f7:data10/f8:data10/f1
[job_sdl1_sub2]
startdelay=0
rw=randwrite filename=data10/f3:data10/f4:data10/f5:data10/f6:data10/f7:data10/f8:data10/f1:data10/f2
[job_sdl1_sub3]
startdelay=0
rw=randwrite filename=data10/f4:data10/f5:data10/f6:data10/f7:data10/f8:data10/f1:data10/f2:data10/f3
[job_sdl1_sub4]
startdelay=0
rw=randwrite filename=data10/f5:data10/f6:data10/f7:data10/f8:data10/f1:data10/f2:data10/f3:data10/f4
[job_sdl1_sub5]
startdelay=0
rw=randwrite filename=data10/f6:data10/f7:data10/f8:data10/f1:data10/f2:data10/f3:data10/f4:data10/f5
[job_sdl1_sub6]
startdelay=0
rw=randwrite filename=data10/f7:data10/f8:data10/f1:data10/f2:data10/f3:data10/f4:data10/f5:data10/f6
[job_sdl1_sub7]
startdelay=0
rw=randwrite filename=data10/f8:data10/f1:data10/f2:data10/f3:data10/f4:data10/f5:data10/f6:data10/f7
[job_sdm1_sub0]
startdelay=0
rw=randwrite filename=data11/f1:data11/f2:data11/f3:data11/f4:data11/f5:data11/f6:data11/f7:data11/f8
[job_sdm1_sub1]
startdelay=0
rw=randwrite filename=data11/f2:data11/f3:data11/f4:data11/f5:data11/f6:data11/f7:data11/f8:data11/f1
[job_sdm1_sub2]
startdelay=0
rw=randwrite filename=data11/f3:data11/f4:data11/f5:data11/f6:data11/f7:data11/f8:data11/f1:data11/f2
[job_sdm1_sub3]
startdelay=0
rw=randwrite filename=data11/f4:data11/f5:data11/f6:data11/f7:data11/f8:data11/f1:data11/f2:data11/f3
[job_sdm1_sub4]
startdelay=0
rw=randwrite filename=data11/f5:data11/f6:data11/f7:data11/f8:data11/f1:data11/f2:data11/f3:data11/f4
[job_sdm1_sub5]
startdelay=0
rw=randwrite filename=data11/f6:data11/f7:data11/f8:data11/f1:data11/f2:data11/f3:data11/f4:data11/f5
[job_sdm1_sub6]
startdelay=0
rw=randwrite filename=data11/f7:data11/f8:data11/f1:data11/f2:data11/f3:data11/f4:data11/f5:data11/f6
[job_sdm1_sub7]
startdelay=0
rw=randwrite filename=data11/f8:data11/f1:data11/f2:data11/f3:data11/f4:data11/f5:data11/f6:data11/f7
Quoted 15 lines · Shaohua Li
queue preemption is good for some workloads and not for others. With commit
f8ae6e3eb825, the impact is amplified. I currently have two issues with it:
1. In a multi-threaded workload, each thread runs a random read/write (for example, mmap write) with iodepth 1. I found the queue depth gets smaller with commitf8ae6e3eb825. The reason is write gets preempted, so more threads are waitting for write, and on the other hand, there are less threads doing read. This will make the queue depth small, so performance drops a little. So in this case, speed up write can speed up read too, but we can't detect it.
2.cfq_may_dispatchdoesn't limit queue depth if the queue is the sole queue. What about if there are two queues, one sync and one async? If the sync queue's think time is small, we can treat it as the sole queue, because the sync queue will preempt async queue, so we don't need care about the async queue's latency. The issue exists before, butf8ae6e3eb825amplifies it. Below is a patch for it.
Quoted 1 line
Any idea?
CFQ is already very complicated, lets try to keep it simple. Because it is complicated, making it hierarchical for cgroup becomes even harder.
IIUC, you are saying that cfqd->busy_queues check is not sufficient as it takes async queues also in account.
So we can keep another count say, cfqd->busy_sync_queues and if there are no busy_sync_queues, allow unlimited depth and that should be a really simple few lines change.
But lets do it only if you have a real life workload. Similiarly we can worry about RT case when there is a real workload behind it.
Thanks
Vivek
Quoted 143 lines
Thanks,
Shaohua
-----------------------------------------------
Subject: cfq-iosched: don't limlit sync queue depth with only one such sync queueIf there are a sync and an async queue and the sync queue's think time is small, we can ignore the sync queue's dispatch quantum. Because the sync queue will always preempt the async queue, we don't need to care about async's latency. In the same way, we can optimize a RT queue too to improve performance. This can fix a performance regression of aiostress test, which is introduced by commit
f8ae6e3eb825. The issue should exist even without the commit, but the commit amplifies the impact.Signed-off-by Shaohua Li <[email protected]>--- block/cfq-iosched.c | 91 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 18 deletions(-)Index:
linux/block/cfq-iosched.c
===================================================================block/cfq-iosched.c +73
-18@@ -1150,6 +1150,20 @@ void cfq_unlink_blkio_group(void *key, s spin_unlock_irqrestore(cfqd->queue->queue_lock, flags); } +static bool cfq_have_cfqgs (struct cfq_data *cfqd) +{ +struct hlist_node *pos; +struct cfq_group *cfqg; +int cnt =0 ; + +hlist_for_each_entry (cfqg, pos, &cfqd->cfqg_list, cfqd_node) { + cnt++; +if (cnt >1 ) +break ; + } +return cnt >1 ; +} +#else /* GROUP_IOSCHED */ static struct cfq_group *cfq_get_cfqg (struct cfq_data *cfqd,int create) {@@ -1169,6 +1183,12 @@ cfq_link_cfqq_cfqg(struct cfq_queue *cfqstatic void cfq_release_cfq_groups (struct cfq_data *cfqd) {}static inline void cfq_put_cfqg (struct cfq_group *cfqg) {} +static inline bool cfq_have_cfqgs (struct cfq_data *cfqd) +{ +return false; +} + +#endif /* GROUP_IOSCHED */ /* @@ -2381,6 +2401,57 @@ static inline bool cfq_slice_used_soon(sreturn false; } +static unsigned int cfq_queue_max_quantum (struct cfq_data *cfqd, +struct cfq_queue *cfqq) +{ +int sync =cfq_cfqq_sync (cfqq); +enum wl_prio_t prio =cfqq_prio (cfqq); +struct cfq_group *cfqg = cfqq->cfqg; +int sync_queues_cnt, async_queues_cnt; +struct cfq_io_context *cic =RQ_CIC (cfqq->next_rq); + +/* Sole queue user, no limit */ +if (cfqd->busy_queues ==1 ) +return -1 ; + +if (cfq_have_cfqgs (cfqd) || (!sync && prio !=RT_WORKLOAD )) + goto normal; + + sync_queues_cnt = cfqg->service_trees[prio][SYNC_NOIDLE_WORKLOAD ].count + + cfqg->service_trees[prio][SYNC_WORKLOAD ].count; + async_queues_cnt = cfqg->service_trees[prio][ASYNC_WORKLOAD ].count; +/* +* If a queue is a sole sync queue and think time is small, we can ignore +* async queue here and give the sync queue no dispatch limit, because a +* sync queue can preempt async queue. +* +* If the queue is RT, we don't need check BE, because even the +* queue is expired, the dispatcher will select RT queue again next time. +* +* If the queue is BE, we don't check RT here, because dispatcher will +* switch to RT next time, so we at most dispatch one extra request. +*/ +if (((!sync && prio ==RT_WORKLOAD && sync_queues_cnt ==0 && + async_queues_cnt ==1 ) || sync_queues_cnt ==1 ) && +sample_valid (cic->ttime_samples) && + cic->ttime_mean < cfqd->cfq_slice_idle) +return -1 ; +normal: +/* +* We have other queues, don't allow more IO from this one +*/ +if (cfq_slice_used_soon (cfqd, cfqq)) +return 0 ; +else +/* +* Normally we start throttling cfqq when cfq_quantum/2 +* requests have been dispatched. But we can drive +* deeper queue depths at the beginning of slice +* subjected to upper limit of cfq_quantum. +* */ +return cfqd->cfq_quantum; +} +static bool cfq_may_dispatch (struct cfq_data *cfqd,struct cfq_queue *cfqq) {unsigned int max_dispatch;@@ -2411,25 +2482,9 @@ static bool cfq_may_dispatch(struct cfq_if (cfq_class_idle (cfqq))return false; -/* -* We have other queues, don't allow more IO from this one -*/ -if (cfqd->busy_queues >1 &&cfq_slice_used_soon (cfqd, cfqq)) + max_dispatch =cfq_queue_max_quantum (cfqd, cfqq); +if (max_dispatch ==0 )return false; - -/* -* Sole queue user, no limit -*/ -if (cfqd->busy_queues ==1 ) - max_dispatch =-1 ; -else -/* -* Normally we start throttling cfqq when cfq_quantum/2 -* requests have been dispatched. But we can drive -* deeper queue depths at the beginning of slice -* subjected to upper limit of cfq_quantum. -* */ - max_dispatch = cfqd->cfq_quantum; }/*
Quoted 21 lines · Vivek Goyal
Quoted 15 lines · Shaohua Li
queue preemption is good for some workloads and not for others. With commit
f8ae6e3eb825, the impact is amplified. I currently have two issues with it:
1. In a multi-threaded workload, each thread runs a random read/write (for example, mmap write) with iodepth 1. I found the queue depth gets smaller with commitf8ae6e3eb825. The reason is write gets preempted, so more threads are waitting for write, and on the other hand, there are less threads doing read. This will make the queue depth small, so performance drops a little. So in this case, speed up write can speed up read too, but we can't detect it.
2.cfq_may_dispatchdoesn't limit queue depth if the queue is the sole queue. What about if there are two queues, one sync and one async? If the sync queue's think time is small, we can treat it as the sole queue, because the sync queue will preempt async queue, so we don't need care about the async queue's latency. The issue exists before, butf8ae6e3eb825amplifies it. Below is a patch for it.Quoted 1 line
Any idea?
CFQ is already very complicated, lets try to keep it simple. Because it is complicated, making it hierarchical for cgroup becomes even harder.
Quoted 6 lines
IIUC, you are saying that
cfqd->busy_queuescheck is not sufficient as it takes async queues also in account.So we can keep another count say,
cfqd->busy_sync_queuesand if there are nobusy_sync_queues, allow unlimited depth and that should be a really simple few lines change.
That covers workload 2, but what about 1? I'm really not sure what the workload there is.
Cheers,
Jeff
Quoted 26 lines · Jeff Moyer
Quoted 24 lines · Vivek Goyal
Quoted 15 lines · Shaohua Li
queue preemption is good for some workloads and not for others. With commit
f8ae6e3eb825, the impact is amplified. I currently have two issues with it:
1. In a multi-threaded workload, each thread runs a random read/write (for example, mmap write) with iodepth 1. I found the queue depth gets smaller with commitf8ae6e3eb825. The reason is write gets preempted, so more threads are waitting for write, and on the other hand, there are less threads doing read. This will make the queue depth small, so performance drops a little. So in this case, speed up write can speed up read too, but we can't detect it.
2.cfq_may_dispatchdoesn't limit queue depth if the queue is the sole queue. What about if there are two queues, one sync and one async? If the sync queue's think time is small, we can treat it as the sole queue, because the sync queue will preempt async queue, so we don't need care about the async queue's latency. The issue exists before, butf8ae6e3eb825amplifies it. Below is a patch for it.Quoted 1 line
Any idea?
CFQ is already very complicated, lets try to keep it simple. Because it is complicated, making it hierarchical for cgroup becomes even harder.
IIUC, you are saying that
cfqd->busy_queuescheck is not sufficient as it takes async queues also in account.
Quoted 6 lines
Quoted 3 lines
So we can keep another count say,
cfqd->busy_sync_queuesand if there are nobusy_sync_queues, allow unlimited depth and that should be a really simple few lines change.That covers workload 2, but what about 1? I'm really not sure what the workload there is.
But CFQ can't track that if reads are stuck behind peding writes. And the whole philosophy is that give READS the importance and not WRITES. So I am not sure what we can do about first case.
If we are really worried about performance and willing to loose isolation in the process (read vs write isolation, or isolation across groups), then may be we can think of implementing another tunables say min_queue_depth. That tells CFQ that don't idle if you are not driving min_queue_depth.
But again, this should be backed by some real workloads.
Thanks
Vivek
Quoted 31 lines · Vivek Goyal
Quoted 30 lines · Jeff Moyer
Quoted 27 lines · Vivek Goyal
Quoted 15 lines · Shaohua Li
queue preemption is good for some workloads and not for others. With commit
f8ae6e3eb825, the impact is amplified. I currently have two issues with it:
1. In a multi-threaded workload, each thread runs a random read/write (for example, mmap write) with iodepth 1. I found the queue depth gets smaller with commitf8ae6e3eb825. The reason is write gets preempted, so more threads are waitting for write, and on the other hand, there are less threads doing read. This will make the queue depth small, so performance drops a little. So in this case, speed up write can speed up read too, but we can't detect it.
2.cfq_may_dispatchdoesn't limit queue depth if the queue is the sole queue. What about if there are two queues, one sync and one async? If the sync queue's think time is small, we can treat it as the sole queue, because the sync queue will preempt async queue, so we don't need care about the async queue's latency. The issue exists before, butf8ae6e3eb825amplifies it. Below is a patch for it.Quoted 1 line
Any idea?
CFQ is already very complicated, lets try to keep it simple. Because it is complicated, making it hierarchical for cgroup becomes even harder.
IIUC, you are saying that
cfqd->busy_queuescheck is not sufficient as it takes async queues also in account.So we can keep another count say,
cfqd->busy_sync_queuesand if there are nobusy_sync_queues, allow unlimited depth and that should be a really simple few lines change.
Quoted 6 lines
Quoted 2 lines
That covers workload 2, but what about 1? I'm really not sure what the workload there is.
But CFQ can't track that if reads are stuck behind peding writes. And the whole philosophy is that give READS the importance and not WRITES. So I am not sure what we can do about first case.
OK, I suspected it might be reads backed up behind writes, but wasn't sure. I agree that we can't tell that's happening, and it's less clear whether we'd even want to do anything about it.
Quoted 4 lines
If we are really worried about performance and willing to loose isolation in the process (read vs write isolation, or isolation across groups), then may be we can think of implementing another tunables say
min_queue_depth. That tells CFQ that don't idle if you are not drivingmin_queue_depth.
Hm, I think that would break a lot of things. ;-)
Quoted 1 line
But again, this should be backed by some real workloads.
I agree, and said as much in my initial response to Shaohua.
Cheers,
Jeff
Quoted 31 lines · Vivek Goyal
Quoted 30 lines · Jeff Moyer
Quoted 27 lines · Vivek Goyal
Quoted 15 lines · Shaohua Li
queue preemption is good for some workloads and not for others. With commit
f8ae6e3eb825, the impact is amplified. I currently have two issues with it:
1. In a multi-threaded workload, each thread runs a random read/write (for example, mmap write) with iodepth 1. I found the queue depth gets smaller with commitf8ae6e3eb825. The reason is write gets preempted, so more threads are waitting for write, and on the other hand, there are less threads doing read. This will make the queue depth small, so performance drops a little. So in this case, speed up write can speed up read too, but we can't detect it.
2.cfq_may_dispatchdoesn't limit queue depth if the queue is the sole queue. What about if there are two queues, one sync and one async? If the sync queue's think time is small, we can treat it as the sole queue, because the sync queue will preempt async queue, so we don't need care about the async queue's latency. The issue exists before, butf8ae6e3eb825amplifies it. Below is a patch for it.Quoted 1 line
Any idea?
CFQ is already very complicated, lets try to keep it simple. Because it is complicated, making it hierarchical for cgroup becomes even harder.
IIUC, you are saying that
cfqd->busy_queuescheck is not sufficient as it takes async queues also in account.So we can keep another count say,
cfqd->busy_sync_queuesand if there are nobusy_sync_queues, allow unlimited depth and that should be a really simple few lines change.
Quoted 6 lines
Quoted 2 lines
That covers workload 2, but what about 1? I'm really not sure what the workload there is.
But CFQ can't track that if reads are stuck behind peding writes. And the whole philosophy is that give READS the importance and not WRITES. So I am not sure what we can do about first case.
I'm also not sure if we should take care about the case, since we should give READ priority.
Quoted 4 lines
If we are really worried about performance and willing to loose isolation in the process (read vs write isolation, or isolation across groups), then may be we can think of implementing another tunables say
min_queue_depth. That tells CFQ that don't idle if you are not drivingmin_queue_depth.
The NCQ disk gives a lot of challenges to CFQ. It is hard to utilize the full disk queue depth without loosing isolation. A tunable seems the best option for people who don't so care about latency.
Quoted 21 lines · Vivek Goyal
Quoted 15 lines · Shaohua Li
queue preemption is good for some workloads and not for others. With commit
f8ae6e3eb825, the impact is amplified. I currently have two issues with it:
1. In a multi-threaded workload, each thread runs a random read/write (for example, mmap write) with iodepth 1. I found the queue depth gets smaller with commitf8ae6e3eb825. The reason is write gets preempted, so more threads are waitting for write, and on the other hand, there are less threads doing read. This will make the queue depth small, so performance drops a little. So in this case, speed up write can speed up read too, but we can't detect it.
2.cfq_may_dispatchdoesn't limit queue depth if the queue is the sole queue. What about if there are two queues, one sync and one async? If the sync queue's think time is small, we can treat it as the sole queue, because the sync queue will preempt async queue, so we don't need care about the async queue's latency. The issue exists before, butf8ae6e3eb825amplifies it. Below is a patch for it.Quoted 1 line
Any idea?
CFQ is already very complicated, lets try to keep it simple. Because it is complicated, making it hierarchical for cgroup becomes even harder.
Quoted 6 lines
IIUC, you are saying that
cfqd->busy_queuescheck is not sufficient as it takes async queues also in account.So we can keep another count say,
cfqd->busy_sync_queuesand if there are nobusy_sync_queues, allow unlimited depth and that should be a really simple few lines change.
sure, this is ok too.
Quoted 2 lines
But lets do it only if you have a real life workload. Similiarly we can worry about RT case when there is a real workload behind it.
aiostress is the workload. but I haven't real workload for the RT case.
Thanks,
Shaohua