Increase resolution of load weights
v2 cover Increase resolution of load weights
Hi All,
Please find attached v2 of the patchset series to increase load resolution. Based on discussions with Ingo and Peter, this version drops all the unsigned long -> u64 conversions which greatly simplifies the patchset. We also only scale load when BITS_PER_LONG > 32 bits. Based on experiments with the previous patchset, the benefits for 32-bit systems were limited and did not justify the increased performance penalties.
Major changes from v1:
- Dropped unsigned long -> u64 conversions
- Cleaned up set_load_weight()
- Scale load only when BITS_PER_LONG > 32
- Rebased patchset to -tip instead of v2.6.39-rcX
Previous versions:
- v1: http://thread.gmane.org/gmane.linux.kernel/1133978
- v0: http://thread.gmane.org/gmane.linux.kernel/1129232
The patchset applies cleanly to -tip. Please note that this does not apply cleanly to v2.6.39-rc8. I can post a patchset against v2.6.39-rc8 if needed.
Below is some analysis of the performance costs/improvements of this patchset.
1. Micro-arch performance costs:
Experiment was to run Ingo's pipe_test_100k about 200 times and measure instructions, cycles, stalled cycles, branches, branch-misses and other micro-arch costs as needed.
-tip (baseline):
# taskset 4 perf stat -e instructions -e cycles -e stalled-cycles-backend -e stalled-cycles-frontend --repeat=200 ./pipe-test-100k
Performance counter stats for '/root/load-scale/pipe-test-100k' (200 runs):
964,991,769 instructions # 0.82 insns per cycle
# 0.33 stalled cycles per insn
# ( +- 0.05% )
1,171,186,635 cycles # 0.000 GHz ( +- 0.08% )
306,373,664 stalled-cycles-backend # 26.16% backend cycles idle ( +- 0.28% )
314,933,621 stalled-cycles-frontend # 26.89% frontend cycles idle ( +- 0.34% )1.122405684 seconds time elapsed ( +- 0.05% )
-tip+patches:
# taskset 4 perf stat -e instructions -e cycles -e stalled-cycles-backend -e stalled-cycles-frontend --repeat=200 ./pipe-test-100k
Performance counter stats for './load-scale/pipe-test-100k' (200 runs):
963,624,821 instructions # 0.82 insns per cycle
# 0.33 stalled cycles per insn
# ( +- 0.04% )
1,175,215,649 cycles # 0.000 GHz ( +- 0.08% )
315,321,126 stalled-cycles-backend # 26.83% backend cycles idle ( +- 0.28% )
316,835,873 stalled-cycles-frontend # 26.96% frontend cycles idle ( +- 0.29% )1.122238659 seconds time elapsed ( +- 0.06% )
With this version of the patchset, looks like instructions decreases by ~0.10% and cycles increases by 0.27%. This doesn't look statistically significant. As expected, number of stalled cycles in the backend increased from 26.16% to 26.83%. This can be attributed to the shifts we do in c_d_m() and other places. The fraction of stalled cycles in the frontend remains about the same, at 26.96% compared to 26.89% in -tip.
For comparison sake, I modified the patch to remove the shifts in c_d_m() and scale down the inverse weight for tasks.
Performance counter stats for './data/pipe-test-100k' (200 runs):
956,993,064 instructions # 0.81 insns per cycle
# 0.34 stalled cycles per insn
# ( +- 0.05% )
1,181,506,294 cycles # 0.000 GHz ( +- 0.10% )
304,271,160 stalled-cycles-backend # 25.75% backend cycles idle ( +- 0.36% )
325,099,601 stalled-cycles-frontend # 27.52% frontend cycles idle ( +- 0.37% )1.129208596 seconds time elapsed ( +- 0.06% )
The number of instructions decreases by about 0.8% and cycles increase about 0.81%. The fraction of cycles stalled in the backend drops to 25.72% and fraction of cycles in the frontend increases to 27.52% (increase of 2.61%). This is probably because we take the path marked unlikely in c_d_m() more often because we overflow the 64-bit mult.
I tried a few variations of this alternative; i.e. do not scale weights in c_d_m() and replace the unlikely() compiler hint with (i). no compiler hint, and (ii). a likley() compiler hint. Neither of these variations resulted in better performance compared to scaling down weights (as currently done).
(i). -tip+patches(alt) + no compiler hint in c_d_m()
# taskset 4 perf stat -e instructions -e cycles -e stalled-cycles-backend -e stalled-cycles-frontend --repeat=200 ./pipe-test-100k
Performance counter stats for './load-scale/pipe-test-100k' (200 runs):
958,280,690 instructions # 0.80 insns per cycle
# 0.34 stalled cycles per insn
# ( +- 0.07% )
1,191,992,203 cycles # 0.000 GHz ( +- 0.10% )
314,905,306 stalled-cycles-backend # 26.42% backend cycles idle ( +- 0.36% )
324,940,653 stalled-cycles-frontend # 27.26% frontend cycles idle ( +- 0.38% )1.136591328 seconds time elapsed ( +- 0.08% )
(ii). -tip+patches(alt) + likely() compiler hint
# taskset 4 perf stat -e instructions -e cycles -e stalled-cycles-backend -e stalled-cycles-frontend --repeat=200 ./pipe-test-100k
Performance counter stats for './load-scale/pipe-test-100k' (200 runs):
956,711,525 instructions # 0.81 insns per cycle
# 0.34 stalled cycles per insn
# ( +- 0.07% )
1,184,777,377 cycles # 0.000 GHz ( +- 0.09% )
308,259,625 stalled-cycles-backend # 26.02% backend cycles idle ( +- 0.32% )
325,105,968 stalled-cycles-frontend # 27.44% frontend cycles idle ( +- 0.38% )1.131130981 seconds time elapsed ( +- 0.06% )
2. Balancing low-weight task groups
Test setup: run 50 tasks with random sleep/busy times (biased around 100ms) in a low weight container (with cpu.shares = 2). Measure %idle as reported by mpstat over a 10s window.
-tip (baseline):
06:47:48 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle intr/s 06:47:49 PM all 94.32 0.00 0.06 0.00 0.00 0.00 0.00 0.00 5.62 15888.00 06:47:50 PM all 94.57 0.00 0.62 0.00 0.00 0.00 0.00 0.00 4.81 16180.00 06:47:51 PM all 94.69 0.00 0.06 0.00 0.00 0.00 0.00 0.00 5.25 15966.00 06:47:52 PM all 95.81 0.00 0.00 0.00 0.00 0.00 0.00 0.00 4.19 16053.00 06:47:53 PM all 94.88 0.06 0.00 0.00 0.00 0.00 0.00 0.00 5.06 15984.00 06:47:54 PM all 93.31 0.00 0.00 0.00 0.00 0.00 0.00 0.00 6.69 15806.00 06:47:55 PM all 94.19 0.00 0.06 0.00 0.00 0.00 0.00 0.00 5.75 15896.00 06:47:56 PM all 92.87 0.00 0.00 0.00 0.00 0.00 0.00 0.00 7.13 15716.00 06:47:57 PM all 94.88 0.00 0.00 0.00 0.00 0.00 0.00 0.00 5.12 15982.00 06:47:58 PM all 95.44 0.00 0.00 0.00 0.00 0.00 0.00 0.00 4.56 16075.00 Average: all 94.49 0.01 0.08 0.00 0.00 0.00 0.00 0.00 5.42 15954.60
-tip+patches:
06:47:03 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle intr/s 06:47:04 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16630.00 06:47:05 PM all 99.69 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.31 16580.20 06:47:06 PM all 99.69 0.00 0.06 0.00 0.00 0.00 0.00 0.00 0.25 16596.00 06:47:07 PM all 99.20 0.00 0.74 0.00 0.00 0.06 0.00 0.00 0.00 17838.61 06:47:08 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16540.00 06:47:09 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16575.00 06:47:10 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16614.00 06:47:11 PM all 99.94 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.06 16588.00 06:47:12 PM all 99.94 0.00 0.06 0.00 0.00 0.00 0.00 0.00 0.00 16593.00 06:47:13 PM all 99.94 0.00 0.06 0.00 0.00 0.00 0.00 0.00 0.00 16551.00 Average: all 99.84 0.00 0.09 0.00 0.00 0.01 0.00 0.00 0.06 16711.58
On a related note, compared to v2.6.39-rc7, -tip seems to have regressed in this test. A bisection points to this merge commit:
$ git show 493bd3180190511770876ed251d75dab8595d322 commit 493bd3180190511770876ed251d75dab8595d322 Merge: 0cc5bc3 d906f0e Author: Ingo Molnar <[email protected]> Date: Fri Jan 7 14:09:55 2011 +0100
Merge branch 'x86/numa'
Is there a way to bisect into this merge commit? I don't have much experience with git bisect and I've been manually inspecting commits :-( Full bisection log below.
git bisect start
# good: [693d92a1bbc9e42681c42ed190bd42b636ca876f] Linux 2.6.39-rc7 git bisect good 693d92a1bbc9e42681c42ed190bd42b636ca876f
# bad: [6639653af57eb8f48971cc8662318dfacd192929] Merge branch 'perf/urgent' git bisect bad 6639653af57eb8f48971cc8662318dfacd192929
# bad: [1ebfeec0895bf5afc59d135deab2664ad4d71e82] Merge branch 'x86/urgent' git bisect bad 1ebfeec0895bf5afc59d135deab2664ad4d71e82
# good: [0d51ef74badd470c0f84ee4eff502e06bdb7e06b] Merge branch 'perf/core' git bisect good 0d51ef74badd470c0f84ee4eff502e06bdb7e06b
# good: [4c637fc4944b153dc98735cc79dcd776c1a47303] Merge branch 'perf/core' git bisect good 4c637fc4944b153dc98735cc79dcd776c1a47303
# good: [959620395dcef68a93288ac6055f6010fbefa243] Merge branch 'perf/core' git bisect good 959620395dcef68a93288ac6055f6010fbefa243
# good: [6a8aeb83d3d7e5682df4542e6080cd4037a1f6d7] Merge branch 'out-of-tree' git bisect good 6a8aeb83d3d7e5682df4542e6080cd4037a1f6d7
# good: [106f3c7499db2d4094e2d3a669c2f2c6f08ab093] Merge branch 'perf/core' git bisect good 106f3c7499db2d4094e2d3a669c2f2c6f08ab093
# good: [0de7032fa49c88b06a492a88da7ea0c5118cedad] Merge branch 'linus' git bisect good 0de7032fa49c88b06a492a88da7ea0c5118cedad
# good: [5e02063f418c58fb756b1ac972a7d1805127f299] Merge branch 'perf/core' git bisect good 5e02063f418c58fb756b1ac972a7d1805127f299
# bad: [f9a3e42e48b14d6cb698cf8a5380ea32c316c949] Merge branch 'sched/urgent' git bisect bad f9a3e42e48b14d6cb698cf8a5380ea32c316c949
# good: [0cc5bc39098229cf4192c3639f0f6108afe4932b] Merge branch 'x86/urgent' git bisect good 0cc5bc39098229cf4192c3639f0f6108afe4932b
# bad: [1f01b669d560f33ba388360aa6070fbdef9f8f44] Merge branch 'perf/core' git bisect bad 1f01b669d560f33ba388360aa6070fbdef9f8f44
# bad: [493bd3180190511770876ed251d75dab8595d322] Merge branch 'x86/numa' git bisect bad 493bd3180190511770876ed251d75dab8595d322
# bad: [493bd3180190511770876ed251d75dab8595d322] Merge branch 'x86/numa' git bisect bad 493bd3180190511770876ed251d75dab8595d322
-Thanks,
Nikhil
Nikhil Rao (3):
sched: cleanup set_load_weight()
sched: introduce SCHED_POWER_SCALE to scale cpu_power calculations
sched: increase SCHED_LOAD_SCALE resolution
include/linux/sched.h | 25 +++++++++++++++++----- kernel/sched.c | 38 ++++++++++++++++++++++++----------- kernel/sched_fair.c | 52 +++++++++++++++++++++++++----------------------- 3 files changed, 72 insertions(+), 43 deletions(-)
Signature · 1 line
1.7.3.1
PATCH v2 1/3 sched: cleanup set_load_weight()
Avoid using long repetitious names; make this simpler and nicer to read. No functional change introduced in this patch.
--- kernel/sched.c | 11 +++++++---- 1 files changed, 7 insertions(+), 4 deletions(-)
kernel/sched.c +7 -4
@@ -1778,17 +1778,20 @@ static void dec_nr_running(struct rq *rq)static void set_load_weight (struct task_struct *p) { +int prio = p->static_prio -MAX_RT_PRIO ; +struct load_weight *load = &p->se.load; +/* * SCHED_IDLE tasks get minimal weight: */ if (p->policy ==SCHED_IDLE ) { - p->se.load.weight =WEIGHT_IDLEPRIO ; - p->se.load.inv_weight =WMULT_IDLEPRIO ; + load->weight =WEIGHT_IDLEPRIO ; + load->inv_weight =WMULT_IDLEPRIO ;return ; } - p->se.load.weight = prio_to_weight[p->static_prio -MAX_RT_PRIO ]; - p->se.load.inv_weight = prio_to_wmult[p->static_prio -MAX_RT_PRIO ]; + load->weight = prio_to_weight[prio]; + load->inv_weight = prio_to_wmult[prio]; }static void enqueue_task (struct rq *rq,struct task_struct *p,int flags)
Signature · 1 line
1.7.3.1
[tip:sched/core] sched: Cleanup set_load_weight()
Commit-ID: f05998d4b80632f2cc00f108da503066ef5d38d5 Gitweb: http://git.kernel.org/tip/f05998d4b80632f2cc00f108da503066ef5d38d5 Author: Nikhil Rao <[email protected]> AuthorDate: Wed, 18 May 2011 10:09:38 -0700 Committer: Ingo Molnar <[email protected]> CommitDate: Fri, 20 May 2011 14:16:49 +0200
sched: Cleanup set_load_weight()
Avoid using long repetitious names; make this simpler and nicer to read. No functional change introduced in this patch.
--- kernel/sched.c | 11 +++++++---- 1 files changed, 7 insertions(+), 4 deletions(-)
kernel/sched.c +7 -4
@@ -1778,17 +1778,20 @@ static void dec_nr_running(struct rq *rq)static void set_load_weight (struct task_struct *p) { +int prio = p->static_prio -MAX_RT_PRIO ; +struct load_weight *load = &p->se.load; +/* * SCHED_IDLE tasks get minimal weight: */ if (p->policy ==SCHED_IDLE ) { - p->se.load.weight =WEIGHT_IDLEPRIO ; - p->se.load.inv_weight =WMULT_IDLEPRIO ; + load->weight =WEIGHT_IDLEPRIO ; + load->inv_weight =WMULT_IDLEPRIO ;return ; } - p->se.load.weight = prio_to_weight[p->static_prio -MAX_RT_PRIO ]; - p->se.load.inv_weight = prio_to_wmult[p->static_prio -MAX_RT_PRIO ]; + load->weight = prio_to_weight[prio]; + load->inv_weight = prio_to_wmult[prio]; }static void enqueue_task (struct rq *rq,struct task_struct *p,int flags)
PATCH v2 2/3 sched: introduce SCHED_POWER_SCALE to scale cpu_power calculations
In reply to Nikhil RaoSCHED_LOAD_SCALE is used to increase nice resolution and to scale cpu_power calculations in the scheduler. This patch introduces SCHED_POWER_SCALE and converts all uses of SCHED_LOAD_SCALE for scaling cpu_power to use SCHED_POWER_SCALE instead.
This is a preparatory patch for increasing the resolution of SCHED_LOAD_SCALE, and there is no need to increase resolution for cpu_power calculations.
--- include/linux/sched.h | 13 +++++++---- kernel/sched.c | 4 +- kernel/sched_fair.c | 52 +++++++++++++++++++++++++----------------------- 3 files changed, 37 insertions(+), 32 deletions(-)
include/linux/sched.h +8 -5
@@ -788,17 +788,20 @@ enum cpu_idle_type { };/* -* sched-domains (multiprocessor balancing) declarations: -*/ - -/* * Increase resolution of nice-level calculations: */ #define SCHED_LOAD_SHIFT 10#define SCHED_LOAD_SCALE (1L << SCHED_LOAD_SHIFT) -#define SCHED_LOAD_SCALE_FUZZ SCHED_LOAD_SCALE +/* +* Increase resolution of cpu_power calculations +*/ +#define SCHED_POWER_SHIFT 10 +#define SCHED_POWER_SCALE (1L << SCHED_POWER_SHIFT) +/* +* sched-domains (multiprocessor balancing) declarations: +*/ #ifdef CONFIG_SMP #define SD_LOAD_BALANCE 0x0001/* Do load balancing on this domain. */ #define SD_BALANCE_NEWIDLE 0x0002/* Balance when about to become idle */
kernel/sched.c +2 -2
@@ -6530,7 +6530,7 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level,cpulist_scnprintf (str,sizeof (str),sched_group_cpus (group));printk (KERN_CONT " %s" , str); -if (group->cpu_power !=SCHED_LOAD_SCALE ) { +if (group->cpu_power !=SCHED_POWER_SCALE ) {printk (KERN_CONT " (cpu_power = %d)" , group->cpu_power); }
@@ -7905,7 +7905,7 @@ void __init sched_init(void)#ifdef CONFIG_SMP rq->sd =NULL ; rq->rd =NULL ; - rq->cpu_power =SCHED_LOAD_SCALE ; + rq->cpu_power =SCHED_POWER_SCALE ; rq->post_schedule =0 ; rq->active_balance =0 ; rq->next_balance = jiffies;
kernel/sched_fair.c +27 -25
177 lines not on this page · raw message
Signature · 1 line
1.7.3.1
[tip:sched/core] sched: Introduce SCHED_POWER_SCALE to scale cpu_power calculations
Commit-ID: 1399fa7807a1a5998bbf147e80668e9950661dfa Gitweb: http://git.kernel.org/tip/1399fa7807a1a5998bbf147e80668e9950661dfa Author: Nikhil Rao <[email protected]> AuthorDate: Wed, 18 May 2011 10:09:39 -0700 Committer: Ingo Molnar <[email protected]> CommitDate: Fri, 20 May 2011 14:16:50 +0200
sched: Introduce SCHED_POWER_SCALE to scale cpu_power calculations
SCHED_LOAD_SCALE is used to increase nice resolution and to scale cpu_power calculations in the scheduler. This patch introduces SCHED_POWER_SCALE and converts all uses of SCHED_LOAD_SCALE for scaling cpu_power to use SCHED_POWER_SCALE instead.
This is a preparatory patch for increasing the resolution of SCHED_LOAD_SCALE, and there is no need to increase resolution for cpu_power calculations.
--- include/linux/sched.h | 13 +++++++---- kernel/sched.c | 4 +- kernel/sched_fair.c | 52 +++++++++++++++++++++++++----------------------- 3 files changed, 37 insertions(+), 32 deletions(-)
include/linux/sched.h +8 -5
@@ -788,17 +788,20 @@ enum cpu_idle_type { };/* -* sched-domains (multiprocessor balancing) declarations: -*/ - -/* * Increase resolution of nice-level calculations: */ #define SCHED_LOAD_SHIFT 10#define SCHED_LOAD_SCALE (1L << SCHED_LOAD_SHIFT) -#define SCHED_LOAD_SCALE_FUZZ SCHED_LOAD_SCALE +/* +* Increase resolution of cpu_power calculations +*/ +#define SCHED_POWER_SHIFT 10 +#define SCHED_POWER_SCALE (1L << SCHED_POWER_SHIFT) +/* +* sched-domains (multiprocessor balancing) declarations: +*/ #ifdef CONFIG_SMP #define SD_LOAD_BALANCE 0x0001/* Do load balancing on this domain. */ #define SD_BALANCE_NEWIDLE 0x0002/* Balance when about to become idle */
kernel/sched.c +2 -2
@@ -6530,7 +6530,7 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level,cpulist_scnprintf (str,sizeof (str),sched_group_cpus (group));printk (KERN_CONT " %s" , str); -if (group->cpu_power !=SCHED_LOAD_SCALE ) { +if (group->cpu_power !=SCHED_POWER_SCALE ) {printk (KERN_CONT " (cpu_power = %d)" , group->cpu_power); }
@@ -7905,7 +7905,7 @@ void __init sched_init(void)#ifdef CONFIG_SMP rq->sd =NULL ; rq->rd =NULL ; - rq->cpu_power =SCHED_LOAD_SCALE ; + rq->cpu_power =SCHED_POWER_SCALE ; rq->post_schedule =0 ; rq->active_balance =0 ; rq->next_balance = jiffies;
kernel/sched_fair.c +27 -25
177 lines not on this page · raw message
PATCH v2 3/3 sched: increase SCHED_LOAD_SCALE resolution
In reply to Nikhil RaoIntroduce SCHED_LOAD_RESOLUTION, which scales is added to SCHED_LOAD_SHIFT and increases the resolution of SCHED_LOAD_SCALE. This patch sets the value of SCHED_LOAD_RESOLUTION to 10, scaling up the weights for all sched entities by a factor of 1024. With this extra resolution, we can handle deeper cgroup hiearchies and the scheduler can do better shares distribution and load load balancing on larger systems (especially for low weight task groups).
This does not change the existing user interface, the scaled weights are only used internally. We do not modify prio_to_weight values or inverses, but use the original weights when calculating the inverse which is used to scale execution time delta in calc_delta_mine(). This ensures we do not lose accuracy when accounting time to the sched entities. Thanks to Nikunj Dadhania for fixing an bug in c_d_m() that broken fairness.
--- include/linux/sched.h | 14 ++++++++++++-- kernel/sched.c | 27 +++++++++++++++++++-------- 2 files changed, 31 insertions(+), 10 deletions(-)
include/linux/sched.h +12 -2
@@ -788,9 +788,19 @@ enum cpu_idle_type { };/* -* Increase resolution of nice-level calculations: +* Increase resolution of nice-level calculations for 64-bit architectures. */ -#define SCHED_LOAD_SHIFT 10 +#if BITS_PER_LONG >32 +#define SCHED_LOAD_RESOLUTION 10 +#define scale_load (w) (w << SCHED_LOAD_RESOLUTION) +#define scale_load_down (w) (w >> SCHED_LOAD_RESOLUTION) +#else +#define SCHED_LOAD_RESOLUTION 0 +#define scale_load (w) w +#define scale_load_down (w) w +#endif + +#define SCHED_LOAD_SHIFT (10 + (SCHED_LOAD_RESOLUTION))#define SCHED_LOAD_SCALE (1L << SCHED_LOAD_SHIFT)/*
kernel/sched.c +19 -8
@@ -293,7 +293,7 @@ static DEFINE_SPINLOCK(task_group_lock);* limitation from this.) */ #define MIN_SHARES 2 -#define MAX_SHARES (1UL << 18) +#define MAX_SHARES (1UL << (18 + SCHED_LOAD_RESOLUTION))static int root_task_group_load =ROOT_TASK_GROUP_LOAD ;#endif
@@ -1330,13 +1330,24 @@ calc_delta_mine(unsigned long delta_exec, unsigned long weight, {u64 tmp; - tmp = (u64 )delta_exec * weight; +/* +* weight can be less than 2^SCHED_LOAD_RESOLUTION for task group sched +* entities since MIN_SHARES = 2. Treat weight as 1 if less than +* 2^SCHED_LOAD_RESOLUTION. +*/ +if (likely (weight > (1UL <<SCHED_LOAD_RESOLUTION ))) + tmp = (u64 )delta_exec *scale_load_down (weight); +else + tmp = (u64 )delta_exec;if (!lw->inv_weight) { -if (BITS_PER_LONG >32 &&unlikely (lw->weight >=WMULT_CONST )) +unsigned long w =scale_load_down (lw->weight); +if (BITS_PER_LONG >32 &&unlikely (w >=WMULT_CONST )) lw->inv_weight =1 ; +else if (unlikely (!w)) + lw->inv_weight =WMULT_CONST ;else - lw->inv_weight =WMULT_CONST / lw->weight; + lw->inv_weight =WMULT_CONST / w; }/*
@@ -1785,12 +1796,12 @@ static void set_load_weight(struct task_struct *p)* SCHED_IDLE tasks get minimal weight: */ if (p->policy ==SCHED_IDLE ) { - load->weight =WEIGHT_IDLEPRIO ; + load->weight =scale_load (WEIGHT_IDLEPRIO ); load->inv_weight =WMULT_IDLEPRIO ;return ; } - load->weight = prio_to_weight[prio]; + load->weight =scale_load (prio_to_weight[prio]); load->inv_weight = prio_to_wmult[prio]; }
@@ -8809,14 +8820,14 @@ cpu_cgroup_exit(struct cgroup_subsys *ss, struct cgroup *cgrp,static int cpu_shares_write_u64 (struct cgroup *cgrp,struct cftype *cftype,u64 shareval) { -return sched_group_set_shares (cgroup_tg (cgrp), shareval); +return sched_group_set_shares (cgroup_tg (cgrp),scale_load (shareval)); }static u64 cpu_shares_read_u64 (struct cgroup *cgrp,struct cftype *cft) {struct task_group *tg =cgroup_tg (cgrp); -return (u64 ) tg->shares; +return (u64 )scale_load_down (tg->shares); }#endif /* CONFIG_FAIR_GROUP_SCHED */
Signature · 1 line
1.7.3.1
Re: [PATCH v2 3/3] sched: increase SCHED_LOAD_SCALE resolution
include/linux/sched.h enum cpu_idle_type { · line 801
Quoted 10 lines · Nikhil Rao
include/linux/sched.h +9
-1-#define SCHED_LOAD_SHIFT 10 +#if BITS_PER_LONG >32 +#define SCHED_LOAD_RESOLUTION 10 +#define scale_load (w) (w << SCHED_LOAD_RESOLUTION) +#define scale_load_down (w) (w >> SCHED_LOAD_RESOLUTION) +#else +#define SCHED_LOAD_RESOLUTION 0 +#define scale_load (w) w +#define scale_load_down (w) w +#endif
Please use the:
#if X
# define Y A #else # define Y B #endif
type of nested CPP branching style.
Also, the 'w' probably wants to be '(w)', just in case.
kernel/sched.c calc_delta_mine(unsigned long delta_exec, unsigned long weight, · line 1346
Quoted 5 lines
kernel/sched.c +2
-1if (!lw->inv_weight) { -if (BITS_PER_LONG >32 &&unlikely (lw->weight >=WMULT_CONST )) +unsigned long w =scale_load_down (lw->weight); +if (BITS_PER_LONG >32 &&unlikely (w >=WMULT_CONST )) lw->inv_weight =1 ;
Please separate local variable declarations and the first statement following it by an extra empty line.
Could you also put all the performance measurement description into the changelog of this third commit - so that people can see (and enjoy) the extensive testing you have done on this topic?
Thanks,
Ingo
Re: [PATCH v2 3/3] sched: increase SCHED_LOAD_SCALE resolution
Quoted 17 lines · Ingo Molnar
include/linux/sched.h enum cpu_idle_type { · line 801
Quoted 10 lines · Nikhil Rao
include/linux/sched.h +9
-1-#define SCHED_LOAD_SHIFT 10 +#if BITS_PER_LONG >32 +#define SCHED_LOAD_RESOLUTION 10 +#define scale_load (w) (w << SCHED_LOAD_RESOLUTION) +#define scale_load_down (w) (w >> SCHED_LOAD_RESOLUTION) +#else +#define SCHED_LOAD_RESOLUTION 0 +#define scale_load (w) w +#define scale_load_down (w) w +#endif Please use the:
#if X
Quoted 8 lines
# define Y A #else # define Y B #endiftype of nested CPP branching style.
Also, the 'w' probably wants to be '(w)', just in case.
Thanks for the review. Will fix the macros.
Quoted 10 lines
Quoted 6 lines
if (!lw->inv_weight) {
- if (BITS_PER_LONG> 32 &&unlikely(lw->weight >= WMULT_CONST))
+ unsigned long w =scale_load_down(lw->weight);
+ if (BITS_PER_LONG> 32 &&unlikely(w >= WMULT_CONST))lw->inv_weight= 1;Please separate local variable declarations and the first statement following it by an extra empty line.
Yes, will do.
Quoted 4 lines
Could you also put all the performance measurement description into the changelog of this third commit - so that people can see (and enjoy) the extensive testing you have done on this topic?
Yes, will add the performance changes to the commit description.
Quoted 9 lines
Quoted 9 lines
/*
- * Increase resolution of nice-level calculations:
+ * Increase resolution of nice-level calculations for 64-bit architectures. */include/linux/sched.h +4
-1-#define SCHED_LOAD_SHIFT 10 +#if BITS_PER_LONG >32 +#define SCHED_LOAD_RESOLUTION 10 +#define scale_load (w) (w << SCHED_LOAD_RESOLUTION) +#define scale_load_down (w) (w >> SCHED_LOAD_RESOLUTION)
Quoted 8 lines
include/linux/sched.h enum cpu_idle_type { · line 801
Quoted 5 lines
include/linux/sched.h +5
-0+#else +#define SCHED_LOAD_RESOLUTION 0 +#define scale_load (w) w +#define scale_load_down (w) w +#endif Please also be a bit more verbose in the comment above why we treat 64-bit architectures differently - it's not obvious.
Added a more descriptive comment.
Thanks for the review. I'll refresh the patch and send it with the fixes.
-Thanks,
Nikhil
PATCH v2 3/3 sched: increase SCHED_LOAD_SCALE resolution
Introduce SCHED_LOAD_RESOLUTION, which scales is added to SCHED_LOAD_SHIFT and increases the resolution of SCHED_LOAD_SCALE. This patch sets the value of SCHED_LOAD_RESOLUTION to 10, scaling up the weights for all sched entities by a factor of 1024. With this extra resolution, we can handle deeper cgroup hiearchies and the scheduler can do better shares distribution and load load balancing on larger systems (especially for low weight task groups).
This does not change the existing user interface, the scaled weights are only used internally. We do not modify prio_to_weight values or inverses, but use the original weights when calculating the inverse which is used to scale execution time delta in calc_delta_mine(). This ensures we do not lose accuracy when accounting time to the sched entities. Thanks to Nikunj Dadhania for fixing an bug in c_d_m() that broken fairness.
Below is some analysis of the performance costs/improvements of this patch.
1. Micro-arch performance costs:
Experiment was to run Ingo's pipe_test_100k 200 times with the task pinned to one cpu. I measured instruction, cycles and stalled-cycles for the runs. See
http://thread.gmane.org/gmane.linux.kernel/1129232/focus=1129389 for more info.
-tip (baseline):
Performance counter stats for '/root/load-scale/pipe-test-100k' (200 runs):
964,991,769 instructions # 0.82 insns per cycle
# 0.33 stalled cycles per insn
# ( +- 0.05% )
1,171,186,635 cycles # 0.000 GHz ( +- 0.08% )
306,373,664 stalled-cycles-backend # 26.16% backend cycles idle ( +- 0.28% )
314,933,621 stalled-cycles-frontend # 26.89% frontend cycles idle ( +- 0.34% )1.122405684 seconds time elapsed ( +- 0.05% )
-tip+patches:
Performance counter stats for './load-scale/pipe-test-100k' (200 runs):
963,624,821 instructions # 0.82 insns per cycle
# 0.33 stalled cycles per insn
# ( +- 0.04% )
1,175,215,649 cycles # 0.000 GHz ( +- 0.08% )
315,321,126 stalled-cycles-backend # 26.83% backend cycles idle ( +- 0.28% )
316,835,873 stalled-cycles-frontend # 26.96% frontend cycles idle ( +- 0.29% )1.122238659 seconds time elapsed ( +- 0.06% )
With this patch, instructions decrease by ~0.10% and cycles increase by 0.27%. This doesn't look statistically significant. The number of stalled cycles in the backend increased from 26.16% to 26.83%. This can be attributed to the shifts we do in c_d_m() and other places. The fraction of stalled cycles in the frontend remains about the same, at 26.96% compared to 26.89% in -tip.
2. Balancing low-weight task groups
Test setup: run 50 tasks with random sleep/busy times (biased around 100ms) in a low weight container (with cpu.shares = 2). Measure %idle as reported by mpstat over a 10s window.
-tip (baseline):
06:47:48 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle intr/s 06:47:49 PM all 94.32 0.00 0.06 0.00 0.00 0.00 0.00 0.00 5.62 15888.00 06:47:50 PM all 94.57 0.00 0.62 0.00 0.00 0.00 0.00 0.00 4.81 16180.00 06:47:51 PM all 94.69 0.00 0.06 0.00 0.00 0.00 0.00 0.00 5.25 15966.00 06:47:52 PM all 95.81 0.00 0.00 0.00 0.00 0.00 0.00 0.00 4.19 16053.00 06:47:53 PM all 94.88 0.06 0.00 0.00 0.00 0.00 0.00 0.00 5.06 15984.00 06:47:54 PM all 93.31 0.00 0.00 0.00 0.00 0.00 0.00 0.00 6.69 15806.00 06:47:55 PM all 94.19 0.00 0.06 0.00 0.00 0.00 0.00 0.00 5.75 15896.00 06:47:56 PM all 92.87 0.00 0.00 0.00 0.00 0.00 0.00 0.00 7.13 15716.00 06:47:57 PM all 94.88 0.00 0.00 0.00 0.00 0.00 0.00 0.00 5.12 15982.00 06:47:58 PM all 95.44 0.00 0.00 0.00 0.00 0.00 0.00 0.00 4.56 16075.00 Average: all 94.49 0.01 0.08 0.00 0.00 0.00 0.00 0.00 5.42 15954.60
-tip+patches:
06:47:03 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle intr/s 06:47:04 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16630.00 06:47:05 PM all 99.69 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.31 16580.20 06:47:06 PM all 99.69 0.00 0.06 0.00 0.00 0.00 0.00 0.00 0.25 16596.00 06:47:07 PM all 99.20 0.00 0.74 0.00 0.00 0.06 0.00 0.00 0.00 17838.61 06:47:08 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16540.00 06:47:09 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16575.00 06:47:10 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16614.00 06:47:11 PM all 99.94 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.06 16588.00 06:47:12 PM all 99.94 0.00 0.06 0.00 0.00 0.00 0.00 0.00 0.00 16593.00 06:47:13 PM all 99.94 0.00 0.06 0.00 0.00 0.00 0.00 0.00 0.00 16551.00 Average: all 99.84 0.00 0.09 0.00 0.00 0.01 0.00 0.00 0.06 16711.58
We see an improvement in idle% on the system (drops from 5.42% on -tip to 0.06% with the patches).
--- include/linux/sched.h | 23 +++++++++++++++++++++-- kernel/sched.c | 28 ++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 10 deletions(-)
include/linux/sched.h +21 -2
@@ -788,9 +788,28 @@ enum cpu_idle_type { };/* -* Increase resolution of nice-level calculations: +* Increase resolution of nice-level calculations for 64-bit architectures. +* The extra resolution improves shares distribution and load balancing of +* low-weight task groups (eg. nice +19 on an autogroup), deeper taskgroup +* hierarchies, especially on larger systems. This is not a user-visible change +* and does not change the user-interface for setting shares/weights. +* +* We increase resolution only if we have enough bits to allow this increased +* resolution (i.e. BITS_PER_LONG > 32). The costs for increasing resolution +* when BITS_PER_LONG <= 32 are pretty high and the returns do not justify the +* increased costs. */ -#define SCHED_LOAD_SHIFT 10 +#if BITS_PER_LONG >32 +# define SCHED_LOAD_RESOLUTION 10 +# define scale_load (w) (w << SCHED_LOAD_RESOLUTION) +# define scale_load_down (w) (w >> SCHED_LOAD_RESOLUTION) +#else +# define SCHED_LOAD_RESOLUTION 0 +# define scale_load (w) (w) +# define scale_load_down (w) (w) +#endif + +#define SCHED_LOAD_SHIFT (10 + (SCHED_LOAD_RESOLUTION))#define SCHED_LOAD_SCALE (1L << SCHED_LOAD_SHIFT)/*
kernel/sched.c +20 -8
@@ -293,7 +293,7 @@ static DEFINE_SPINLOCK(task_group_lock);* limitation from this.) */ #define MIN_SHARES 2 -#define MAX_SHARES (1UL << 18) +#define MAX_SHARES (1UL << (18 + SCHED_LOAD_RESOLUTION))static int root_task_group_load =ROOT_TASK_GROUP_LOAD ;#endif
@@ -1330,13 +1330,25 @@ calc_delta_mine(unsigned long delta_exec, unsigned long weight, {u64 tmp; - tmp = (u64 )delta_exec * weight; +/* +* weight can be less than 2^SCHED_LOAD_RESOLUTION for task group sched +* entities since MIN_SHARES = 2. Treat weight as 1 if less than +* 2^SCHED_LOAD_RESOLUTION. +*/ +if (likely (weight > (1UL <<SCHED_LOAD_RESOLUTION ))) + tmp = (u64 )delta_exec *scale_load_down (weight); +else + tmp = (u64 )delta_exec;if (!lw->inv_weight) { -if (BITS_PER_LONG >32 &&unlikely (lw->weight >=WMULT_CONST )) +unsigned long w =scale_load_down (lw->weight); + +if (BITS_PER_LONG >32 &&unlikely (w >=WMULT_CONST )) lw->inv_weight =1 ; +else if (unlikely (!w)) + lw->inv_weight =WMULT_CONST ;else - lw->inv_weight =WMULT_CONST / lw->weight; + lw->inv_weight =WMULT_CONST / w; }/*
@@ -1785,12 +1797,12 @@ static void set_load_weight(struct task_struct *p)* SCHED_IDLE tasks get minimal weight: */ if (p->policy ==SCHED_IDLE ) { - load->weight =WEIGHT_IDLEPRIO ; + load->weight =scale_load (WEIGHT_IDLEPRIO ); load->inv_weight =WMULT_IDLEPRIO ;return ; } - load->weight = prio_to_weight[prio]; + load->weight =scale_load (prio_to_weight[prio]); load->inv_weight = prio_to_wmult[prio]; }
@@ -8809,14 +8821,14 @@ cpu_cgroup_exit(struct cgroup_subsys *ss, struct cgroup *cgrp,static int cpu_shares_write_u64 (struct cgroup *cgrp,struct cftype *cftype,u64 shareval) { -return sched_group_set_shares (cgroup_tg (cgrp), shareval); +return sched_group_set_shares (cgroup_tg (cgrp),scale_load (shareval)); }static u64 cpu_shares_read_u64 (struct cgroup *cgrp,struct cftype *cft) {struct task_group *tg =cgroup_tg (cgrp); -return (u64 ) tg->shares; +return (u64 )scale_load_down (tg->shares); }#endif /* CONFIG_FAIR_GROUP_SCHED */
Signature · 1 line
1.7.3.1
Re: [PATCH v2 3/3] sched: increase SCHED_LOAD_SCALE resolution
include/linux/sched.h enum cpu_idle_type { · line 810
Quoted 9 lines · Nikhil Rao
include/linux/sched.h +9
-0+#if BITS_PER_LONG >32 +# define SCHED_LOAD_RESOLUTION 10 +# define scale_load (w) (w << SCHED_LOAD_RESOLUTION) +# define scale_load_down (w) (w >> SCHED_LOAD_RESOLUTION) +#else +# define SCHED_LOAD_RESOLUTION 0 +# define scale_load (w) (w) +# define scale_load_down (w) (w) +#endif
We want (w) in the other definitions as well, to protect potential operators with lower precedence than <<. (Roughly half of the C operators are such so it's a real issue, should anyone use these macros with operators.)
Quoted 1 line
+#define
SCHED_LOAD_SHIFT(10 + (SCHED_LOAD_RESOLUTION))
that () is not needed actually, if you look at the definition of SCHED_LOAD_RESOLUTION.
So you could move the superfluous () from here up to the two definitions above and thus no parentheses would be hurt during the making of this patch.
kernel/sched.c calc_delta_mine(unsigned long delta_exec, unsigned long weight, · line 1351
Quoted 6 lines
kernel/sched.c +4
-0+if (BITS_PER_LONG >32 &&unlikely (w >=WMULT_CONST )) lw->inv_weight =1 ; +else if (unlikely (!w)) + lw->inv_weight =WMULT_CONST ;else + lw->inv_weight =WMULT_CONST / w;
Ok, i just noticed that you made use of BITS_PER_LONG here too.
It's better to put that into a helper define, something like SCHED_LOAD_HIGHRES, which could thus be used like this:
if (SCHED_LOAD_HIGHRES && unlikely(w >= WMULT_CONST))
then, should anyone want to tweak the condition for SCHED_LOAD_HIGHRES, it could be done in a single place. It would also self-document.
Thanks,
Ingo
Re: [PATCH v2 3/3] sched: increase SCHED_LOAD_SCALE resolution
Quoted 17 lines · Ingo Molnar
Review
Quoted 9 lines · Nikhil Rao
(quoted diff) +9
-0+#if BITS_PER_LONG > 32 +# define SCHED_LOAD_RESOLUTION 10 +# define scale_load(w) (w << SCHED_LOAD_RESOLUTION) +# define scale_load_down(w) (w >> SCHED_LOAD_RESOLUTION) +#else +# define SCHED_LOAD_RESOLUTION 0 +# define scale_load(w) (w) +# define scale_load_down(w) (w) +#endifWe want (w) in the other definitions as well, to protect potential operators with lower precedence than <<. (Roughly half of the C operators are such so it's a real issue, should anyone use these macros with operators.)
Quoted 8 lines
Quoted 1 line
+#define
SCHED_LOAD_SHIFT(10 + (SCHED_LOAD_RESOLUTION))that () is not needed actually, if you look at the definition of
SCHED_LOAD_RESOLUTION.So you could move the superfluous () from here up to the two definitions above and thus no parentheses would be hurt during the making of this patch.
Ah, thanks for the explanation. Does this look OK?
include/linux/sched.h +21 -2
@@ -788,9 +788,28 @@ enum cpu_idle_type { };/* -* Increase resolution of nice-level calculations: +* Increase resolution of nice-level calculations for 64-bit architectures. +* The extra resolution improves shares distribution and load balancing of +* low-weight task groups (eg. nice +19 on an autogroup), deeper taskgroup +* hierarchies, especially on larger systems. This is not a user-visible change +* and does not change the user-interface for setting shares/weights. +* +* We increase resolution only if we have enough bits to allow this increased +* resolution (i.e. BITS_PER_LONG > 32). The costs for increasing resolution +* when BITS_PER_LONG <= 32 are pretty high and the returns do not justify the +* increased costs. */ -#define SCHED_LOAD_SHIFT 10 +#if BITS_PER_LONG >32 +# define SCHED_LOAD_RESOLUTION 10 +# define scale_load (w) ((w) << SCHED_LOAD_RESOLUTION) +# define scale_load_down (w) ((w) >> SCHED_LOAD_RESOLUTION) +#else +# define SCHED_LOAD_RESOLUTION 0 +# define scale_load (w) (w) +# define scale_load_down (w) (w) +#endif + +#define SCHED_LOAD_SHIFT (10 + SCHED_LOAD_RESOLUTION)#define SCHED_LOAD_SCALE (1L << SCHED_LOAD_SHIFT)/*
Quoted 9 lines
Quoted 6 lines
+ if (
BITS_PER_LONG> 32 &&unlikely(w >= WMULT_CONST))lw->inv_weight= 1;
+ else if (unlikely(!w))
+lw->inv_weight=WMULT_CONST;
else
+lw->inv_weight=WMULT_CONST/ w;Ok, i just noticed that you made use of
BITS_PER_LONGhere too.
Quoted 8 lines
It's better to put that into a helper define, something like
SCHED_LOAD_HIGHRES, which could thus be used like this:if (
SCHED_LOAD_HIGHRES&&unlikely(w >= WMULT_CONST))then, should anyone want to tweak the condition for
SCHED_LOAD_HIGHRES, it could be done in a single place. It would also self-document.
Hmm, that particular use of BITS_PER_LONG was not touched by this patch. This patch only changes lw->weight to use the local variable w. The (BITS_PER_LONG & > WMULT_CONST) check is required on 64-bit systems irrespective of the load-resolution changes.
Re: [PATCH v2 3/3] sched: increase SCHED_LOAD_SCALE resolution
Quoted 80 lines · Nikhil Rao
Quoted 17 lines · Ingo Molnar
include/linux/sched.h enum cpu_idle_type { · line 810
Quoted 9 lines · Nikhil Rao
include/linux/sched.h +9
-0+#if BITS_PER_LONG >32 +# define SCHED_LOAD_RESOLUTION 10 +# define scale_load (w) (w << SCHED_LOAD_RESOLUTION) +# define scale_load_down (w) (w >> SCHED_LOAD_RESOLUTION) +#else +# define SCHED_LOAD_RESOLUTION 0 +# define scale_load (w) (w) +# define scale_load_down (w) (w) +#endif We want (w) in the other definitions as well, to protect potential operators with lower precedence than <<. (Roughly half of the C operators are such so it's a real issue, should anyone use these macros with operators.)
Quoted 8 lines
Quoted 1 line
+#define
SCHED_LOAD_SHIFT(10 + (SCHED_LOAD_RESOLUTION))that () is not needed actually, if you look at the definition of
SCHED_LOAD_RESOLUTION.So you could move the superfluous () from here up to the two definitions above and thus no parentheses would be hurt during the making of this patch.
Ah, thanks for the explanation. Does this look OK?
include/linux/sched.h +21
-2@@ -788,9 +788,28 @@ enum cpu_idle_type { };/* -* Increase resolution of nice-level calculations: +* Increase resolution of nice-level calculations for 64-bit architectures. +* The extra resolution improves shares distribution and load balancing of +* low-weight task groups (eg. nice +19 on an autogroup), deeper taskgroup +* hierarchies, especially on larger systems. This is not a user-visible change +* and does not change the user-interface for setting shares/weights. +* +* We increase resolution only if we have enough bits to allow this increased +* resolution (i.e. BITS_PER_LONG > 32). The costs for increasing resolution +* when BITS_PER_LONG <= 32 are pretty high and the returns do not justify the +* increased costs. */ -#define SCHED_LOAD_SHIFT 10 +#if BITS_PER_LONG >32 +# define SCHED_LOAD_RESOLUTION 10 +# define scale_load (w) ((w) << SCHED_LOAD_RESOLUTION) +# define scale_load_down (w) ((w) >> SCHED_LOAD_RESOLUTION) +#else +# define SCHED_LOAD_RESOLUTION 0 +# define scale_load (w) (w) +# define scale_load_down (w) (w) +#endif + +#define SCHED_LOAD_SHIFT (10 + SCHED_LOAD_RESOLUTION)#define SCHED_LOAD_SCALE (1L << SCHED_LOAD_SHIFT)/* Quoted 16 lines
Quoted 8 lines
+ if (
BITS_PER_LONG> 32 &&unlikely(w >= WMULT_CONST))lw->inv_weight= 1;
+ else if (unlikely(!w))
+lw->inv_weight=WMULT_CONST;
else
+lw->inv_weight=WMULT_CONST/ w;Ok, i just noticed that you made use of
BITS_PER_LONGhere too.It's better to put that into a helper define, something like
SCHED_LOAD_HIGHRES, which could thus be used like this:if (
SCHED_LOAD_HIGHRES&&unlikely(w >= WMULT_CONST))
Quoted 8 lines
Quoted 3 lines
then, should anyone want to tweak the condition for
SCHED_LOAD_HIGHRES, it could be done in a single place. It would also self-document.Hmm, that particular use of
BITS_PER_LONGwas not touched by this patch. This patch only changeslw->weightto use the local variable w. The (BITS_PER_LONG& >WMULT_CONST) check is required on 64-bit systems irrespective of the load-resolution changes.
my bad, i confused it with the resolution changes.
It's fine as-is then i guess. Mind reposting the full patch again, with all updates included and the subject line changed to make it easy to find this patch in the discussion?
Thanks,
Ingo
PATCH v3 3/3 sched: increase SCHED_LOAD_SCALE resolution
Introduce SCHED_LOAD_RESOLUTION, which scales is added to SCHED_LOAD_SHIFT and increases the resolution of SCHED_LOAD_SCALE. This patch sets the value of SCHED_LOAD_RESOLUTION to 10, scaling up the weights for all sched entities by a factor of 1024. With this extra resolution, we can handle deeper cgroup hiearchies and the scheduler can do better shares distribution and load load balancing on larger systems (especially for low weight task groups).
This does not change the existing user interface, the scaled weights are only used internally. We do not modify prio_to_weight values or inverses, but use the original weights when calculating the inverse which is used to scale execution time delta in calc_delta_mine(). This ensures we do not lose accuracy when accounting time to the sched entities. Thanks to Nikunj Dadhania for fixing an bug in c_d_m() that broken fairness.
Below is some analysis of the performance costs/improvements of this patch.
1. Micro-arch performance costs:
Experiment was to run Ingo's pipe_test_100k 200 times with the task pinned to one cpu. I measured instruction, cycles and stalled-cycles for the runs. See
http://thread.gmane.org/gmane.linux.kernel/1129232/focus=1129389 for more info.
-tip (baseline):
Performance counter stats for '/root/load-scale/pipe-test-100k' (200 runs):
964,991,769 instructions # 0.82 insns per cycle
# 0.33 stalled cycles per insn
# ( +- 0.05% )
1,171,186,635 cycles # 0.000 GHz ( +- 0.08% )
306,373,664 stalled-cycles-backend # 26.16% backend cycles idle ( +- 0.28% )
314,933,621 stalled-cycles-frontend # 26.89% frontend cycles idle ( +- 0.34% )1.122405684 seconds time elapsed ( +- 0.05% )
-tip+patches:
Performance counter stats for './load-scale/pipe-test-100k' (200 runs):
963,624,821 instructions # 0.82 insns per cycle
# 0.33 stalled cycles per insn
# ( +- 0.04% )
1,175,215,649 cycles # 0.000 GHz ( +- 0.08% )
315,321,126 stalled-cycles-backend # 26.83% backend cycles idle ( +- 0.28% )
316,835,873 stalled-cycles-frontend # 26.96% frontend cycles idle ( +- 0.29% )1.122238659 seconds time elapsed ( +- 0.06% )
With this patch, instructions decrease by ~0.10% and cycles increase by 0.27%. This doesn't look statistically significant. The number of stalled cycles in the backend increased from 26.16% to 26.83%. This can be attributed to the shifts we do in c_d_m() and other places. The fraction of stalled cycles in the frontend remains about the same, at 26.96% compared to 26.89% in -tip.
2. Balancing low-weight task groups
Test setup: run 50 tasks with random sleep/busy times (biased around 100ms) in a low weight container (with cpu.shares = 2). Measure %idle as reported by mpstat over a 10s window.
-tip (baseline):
06:47:48 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle intr/s 06:47:49 PM all 94.32 0.00 0.06 0.00 0.00 0.00 0.00 0.00 5.62 15888.00 06:47:50 PM all 94.57 0.00 0.62 0.00 0.00 0.00 0.00 0.00 4.81 16180.00 06:47:51 PM all 94.69 0.00 0.06 0.00 0.00 0.00 0.00 0.00 5.25 15966.00 06:47:52 PM all 95.81 0.00 0.00 0.00 0.00 0.00 0.00 0.00 4.19 16053.00 06:47:53 PM all 94.88 0.06 0.00 0.00 0.00 0.00 0.00 0.00 5.06 15984.00 06:47:54 PM all 93.31 0.00 0.00 0.00 0.00 0.00 0.00 0.00 6.69 15806.00 06:47:55 PM all 94.19 0.00 0.06 0.00 0.00 0.00 0.00 0.00 5.75 15896.00 06:47:56 PM all 92.87 0.00 0.00 0.00 0.00 0.00 0.00 0.00 7.13 15716.00 06:47:57 PM all 94.88 0.00 0.00 0.00 0.00 0.00 0.00 0.00 5.12 15982.00 06:47:58 PM all 95.44 0.00 0.00 0.00 0.00 0.00 0.00 0.00 4.56 16075.00 Average: all 94.49 0.01 0.08 0.00 0.00 0.00 0.00 0.00 5.42 15954.60
-tip+patches:
06:47:03 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle intr/s 06:47:04 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16630.00 06:47:05 PM all 99.69 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.31 16580.20 06:47:06 PM all 99.69 0.00 0.06 0.00 0.00 0.00 0.00 0.00 0.25 16596.00 06:47:07 PM all 99.20 0.00 0.74 0.00 0.00 0.06 0.00 0.00 0.00 17838.61 06:47:08 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16540.00 06:47:09 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16575.00 06:47:10 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16614.00 06:47:11 PM all 99.94 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.06 16588.00 06:47:12 PM all 99.94 0.00 0.06 0.00 0.00 0.00 0.00 0.00 0.00 16593.00 06:47:13 PM all 99.94 0.00 0.06 0.00 0.00 0.00 0.00 0.00 0.00 16551.00 Average: all 99.84 0.00 0.09 0.00 0.00 0.01 0.00 0.00 0.06 16711.58
We see an improvement in idle% on the system (drops from 5.42% on -tip to 0.06% with the patches).
--- include/linux/sched.h | 23 +++++++++++++++++++++-- kernel/sched.c | 28 ++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 10 deletions(-)
include/linux/sched.h +21 -2
@@ -788,9 +788,28 @@ enum cpu_idle_type { };/* -* Increase resolution of nice-level calculations: +* Increase resolution of nice-level calculations for 64-bit architectures. +* The extra resolution improves shares distribution and load balancing of +* low-weight task groups (eg. nice +19 on an autogroup), deeper taskgroup +* hierarchies, especially on larger systems. This is not a user-visible change +* and does not change the user-interface for setting shares/weights. +* +* We increase resolution only if we have enough bits to allow this increased +* resolution (i.e. BITS_PER_LONG > 32). The costs for increasing resolution +* when BITS_PER_LONG <= 32 are pretty high and the returns do not justify the +* increased costs. */ -#define SCHED_LOAD_SHIFT 10 +#if BITS_PER_LONG >32 +# define SCHED_LOAD_RESOLUTION 10 +# define scale_load (w) ((w) << SCHED_LOAD_RESOLUTION) +# define scale_load_down (w) ((w) >> SCHED_LOAD_RESOLUTION) +#else +# define SCHED_LOAD_RESOLUTION 0 +# define scale_load (w) (w) +# define scale_load_down (w) (w) +#endif + +#define SCHED_LOAD_SHIFT (10 + SCHED_LOAD_RESOLUTION)#define SCHED_LOAD_SCALE (1L << SCHED_LOAD_SHIFT)/*
kernel/sched.c +20 -8
@@ -293,7 +293,7 @@ static DEFINE_SPINLOCK(task_group_lock);* limitation from this.) */ #define MIN_SHARES 2 -#define MAX_SHARES (1UL << 18) +#define MAX_SHARES (1UL << (18 + SCHED_LOAD_RESOLUTION))static int root_task_group_load =ROOT_TASK_GROUP_LOAD ;#endif
@@ -1330,13 +1330,25 @@ calc_delta_mine(unsigned long delta_exec, unsigned long weight, {u64 tmp; - tmp = (u64 )delta_exec * weight; +/* +* weight can be less than 2^SCHED_LOAD_RESOLUTION for task group sched +* entities since MIN_SHARES = 2. Treat weight as 1 if less than +* 2^SCHED_LOAD_RESOLUTION. +*/ +if (likely (weight > (1UL <<SCHED_LOAD_RESOLUTION ))) + tmp = (u64 )delta_exec *scale_load_down (weight); +else + tmp = (u64 )delta_exec;if (!lw->inv_weight) { -if (BITS_PER_LONG >32 &&unlikely (lw->weight >=WMULT_CONST )) +unsigned long w =scale_load_down (lw->weight); + +if (BITS_PER_LONG >32 &&unlikely (w >=WMULT_CONST )) lw->inv_weight =1 ; +else if (unlikely (!w)) + lw->inv_weight =WMULT_CONST ;else - lw->inv_weight =WMULT_CONST / lw->weight; + lw->inv_weight =WMULT_CONST / w; }/*
@@ -1785,12 +1797,12 @@ static void set_load_weight(struct task_struct *p)* SCHED_IDLE tasks get minimal weight: */ if (p->policy ==SCHED_IDLE ) { - load->weight =WEIGHT_IDLEPRIO ; + load->weight =scale_load (WEIGHT_IDLEPRIO ); load->inv_weight =WMULT_IDLEPRIO ;return ; } - load->weight = prio_to_weight[prio]; + load->weight =scale_load (prio_to_weight[prio]); load->inv_weight = prio_to_wmult[prio]; }
@@ -8809,14 +8821,14 @@ cpu_cgroup_exit(struct cgroup_subsys *ss, struct cgroup *cgrp,static int cpu_shares_write_u64 (struct cgroup *cgrp,struct cftype *cftype,u64 shareval) { -return sched_group_set_shares (cgroup_tg (cgrp), shareval); +return sched_group_set_shares (cgroup_tg (cgrp),scale_load (shareval)); }static u64 cpu_shares_read_u64 (struct cgroup *cgrp,struct cftype *cft) {struct task_group *tg =cgroup_tg (cgrp); -return (u64 ) tg->shares; +return (u64 )scale_load_down (tg->shares); }#endif /* CONFIG_FAIR_GROUP_SCHED */
Signature · 1 line
1.7.3.1
[tip:sched/core] sched: Increase SCHED_LOAD_SCALE resolution
Commit-ID: c8b281161dfa4bb5d5be63fb036ce19347b88c63 Gitweb: http://git.kernel.org/tip/c8b281161dfa4bb5d5be63fb036ce19347b88c63 Author: Nikhil Rao <[email protected]> AuthorDate: Wed, 18 May 2011 14:37:48 -0700 Committer: Ingo Molnar <[email protected]> CommitDate: Fri, 20 May 2011 14:16:50 +0200
sched: Increase SCHED_LOAD_SCALE resolution
Introduce SCHED_LOAD_RESOLUTION, which scales is added to SCHED_LOAD_SHIFT and increases the resolution of SCHED_LOAD_SCALE. This patch sets the value of SCHED_LOAD_RESOLUTION to 10, scaling up the weights for all sched entities by a factor of 1024. With this extra resolution, we can handle deeper cgroup hiearchies and the scheduler can do better shares distribution and load load balancing on larger systems (especially for low weight task groups).
This does not change the existing user interface, the scaled weights are only used internally. We do not modify prio_to_weight values or inverses, but use the original weights when calculating the inverse which is used to scale execution time delta in calc_delta_mine(). This ensures we do not lose accuracy when accounting time to the sched entities. Thanks to Nikunj Dadhania for fixing an bug in c_d_m() that broken fairness.
Below is some analysis of the performance costs/improvements of this patch.
1. Micro-arch performance costs:
Experiment was to run Ingo's pipe_test_100k 200 times with the task pinned to one cpu. I measured instruction, cycles and stalled-cycles for the runs. See:
http://thread.gmane.org/gmane.linux.kernel/1129232/focus=1129389
for more info.
-tip (baseline):
Performance counter stats for '/root/load-scale/pipe-test-100k' (200 runs):
964,991,769 instructions # 0.82 insns per cycle
# 0.33 stalled cycles per insn
# ( +- 0.05% )
1,171,186,635 cycles # 0.000 GHz ( +- 0.08% )
306,373,664 stalled-cycles-backend # 26.16% backend cycles idle ( +- 0.28% )
314,933,621 stalled-cycles-frontend # 26.89% frontend cycles idle ( +- 0.34% )1.122405684 seconds time elapsed ( +- 0.05% )
-tip+patches:
Performance counter stats for './load-scale/pipe-test-100k' (200 runs):
963,624,821 instructions # 0.82 insns per cycle
# 0.33 stalled cycles per insn
# ( +- 0.04% )
1,175,215,649 cycles # 0.000 GHz ( +- 0.08% )
315,321,126 stalled-cycles-backend # 26.83% backend cycles idle ( +- 0.28% )
316,835,873 stalled-cycles-frontend # 26.96% frontend cycles idle ( +- 0.29% )1.122238659 seconds time elapsed ( +- 0.06% )
With this patch, instructions decrease by ~0.10% and cycles increase by 0.27%. This doesn't look statistically significant. The number of stalled cycles in the backend increased from 26.16% to 26.83%. This can be attributed to the shifts we do in c_d_m() and other places. The fraction of stalled cycles in the frontend remains about the same, at 26.96% compared to 26.89% in -tip.
2. Balancing low-weight task groups
Test setup: run 50 tasks with random sleep/busy times (biased around 100ms) in a low weight container (with cpu.shares = 2). Measure %idle as reported by mpstat over a 10s window.
-tip (baseline):
06:47:48 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle intr/s 06:47:49 PM all 94.32 0.00 0.06 0.00 0.00 0.00 0.00 0.00 5.62 15888.00 06:47:50 PM all 94.57 0.00 0.62 0.00 0.00 0.00 0.00 0.00 4.81 16180.00 06:47:51 PM all 94.69 0.00 0.06 0.00 0.00 0.00 0.00 0.00 5.25 15966.00 06:47:52 PM all 95.81 0.00 0.00 0.00 0.00 0.00 0.00 0.00 4.19 16053.00 06:47:53 PM all 94.88 0.06 0.00 0.00 0.00 0.00 0.00 0.00 5.06 15984.00 06:47:54 PM all 93.31 0.00 0.00 0.00 0.00 0.00 0.00 0.00 6.69 15806.00 06:47:55 PM all 94.19 0.00 0.06 0.00 0.00 0.00 0.00 0.00 5.75 15896.00 06:47:56 PM all 92.87 0.00 0.00 0.00 0.00 0.00 0.00 0.00 7.13 15716.00 06:47:57 PM all 94.88 0.00 0.00 0.00 0.00 0.00 0.00 0.00 5.12 15982.00 06:47:58 PM all 95.44 0.00 0.00 0.00 0.00 0.00 0.00 0.00 4.56 16075.00 Average: all 94.49 0.01 0.08 0.00 0.00 0.00 0.00 0.00 5.42 15954.60
-tip+patches:
06:47:03 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle intr/s 06:47:04 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16630.00 06:47:05 PM all 99.69 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.31 16580.20 06:47:06 PM all 99.69 0.00 0.06 0.00 0.00 0.00 0.00 0.00 0.25 16596.00 06:47:07 PM all 99.20 0.00 0.74 0.00 0.00 0.06 0.00 0.00 0.00 17838.61 06:47:08 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16540.00 06:47:09 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16575.00 06:47:10 PM all 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 16614.00 06:47:11 PM all 99.94 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.06 16588.00 06:47:12 PM all 99.94 0.00 0.06 0.00 0.00 0.00 0.00 0.00 0.00 16593.00 06:47:13 PM all 99.94 0.00 0.06 0.00 0.00 0.00 0.00 0.00 0.00 16551.00 Average: all 99.84 0.00 0.09 0.00 0.00 0.01 0.00 0.00 0.06 16711.58
We see an improvement in idle% on the system (drops from 5.42% on -tip to 0.06% with the patches).
We see an improvement in idle% on the system (drops from 5.42% on -tip to 0.06% with the patches).
--- include/linux/sched.h | 23 +++++++++++++++++++++-- kernel/sched.c | 28 ++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 10 deletions(-)
include/linux/sched.h +21 -2
@@ -788,9 +788,28 @@ enum cpu_idle_type { };/* -* Increase resolution of nice-level calculations: +* Increase resolution of nice-level calculations for 64-bit architectures. +* The extra resolution improves shares distribution and load balancing of +* low-weight task groups (eg. nice +19 on an autogroup), deeper taskgroup +* hierarchies, especially on larger systems. This is not a user-visible change +* and does not change the user-interface for setting shares/weights. +* +* We increase resolution only if we have enough bits to allow this increased +* resolution (i.e. BITS_PER_LONG > 32). The costs for increasing resolution +* when BITS_PER_LONG <= 32 are pretty high and the returns do not justify the +* increased costs. */ -#define SCHED_LOAD_SHIFT 10 +#if BITS_PER_LONG >32 +# define SCHED_LOAD_RESOLUTION 10 +# define scale_load (w) ((w) << SCHED_LOAD_RESOLUTION) +# define scale_load_down (w) ((w) >> SCHED_LOAD_RESOLUTION) +#else +# define SCHED_LOAD_RESOLUTION 0 +# define scale_load (w) (w) +# define scale_load_down (w) (w) +#endif + +#define SCHED_LOAD_SHIFT (10 + SCHED_LOAD_RESOLUTION)#define SCHED_LOAD_SCALE (1L << SCHED_LOAD_SHIFT)/*
kernel/sched.c +20 -8
@@ -293,7 +293,7 @@ static DEFINE_SPINLOCK(task_group_lock);* limitation from this.) */ #define MIN_SHARES 2 -#define MAX_SHARES (1UL << 18) +#define MAX_SHARES (1UL << (18 + SCHED_LOAD_RESOLUTION))static int root_task_group_load =ROOT_TASK_GROUP_LOAD ;#endif
@@ -1330,13 +1330,25 @@ calc_delta_mine(unsigned long delta_exec, unsigned long weight, {u64 tmp; - tmp = (u64 )delta_exec * weight; +/* +* weight can be less than 2^SCHED_LOAD_RESOLUTION for task group sched +* entities since MIN_SHARES = 2. Treat weight as 1 if less than +* 2^SCHED_LOAD_RESOLUTION. +*/ +if (likely (weight > (1UL <<SCHED_LOAD_RESOLUTION ))) + tmp = (u64 )delta_exec *scale_load_down (weight); +else + tmp = (u64 )delta_exec;if (!lw->inv_weight) { -if (BITS_PER_LONG >32 &&unlikely (lw->weight >=WMULT_CONST )) +unsigned long w =scale_load_down (lw->weight); + +if (BITS_PER_LONG >32 &&unlikely (w >=WMULT_CONST )) lw->inv_weight =1 ; +else if (unlikely (!w)) + lw->inv_weight =WMULT_CONST ;else - lw->inv_weight =WMULT_CONST / lw->weight; + lw->inv_weight =WMULT_CONST / w; }/*
@@ -1785,12 +1797,12 @@ static void set_load_weight(struct task_struct *p)* SCHED_IDLE tasks get minimal weight: */ if (p->policy ==SCHED_IDLE ) { - load->weight =WEIGHT_IDLEPRIO ; + load->weight =scale_load (WEIGHT_IDLEPRIO ); load->inv_weight =WMULT_IDLEPRIO ;return ; } - load->weight = prio_to_weight[prio]; + load->weight =scale_load (prio_to_weight[prio]); load->inv_weight = prio_to_wmult[prio]; }
@@ -8809,14 +8821,14 @@ cpu_cgroup_exit(struct cgroup_subsys *ss, struct cgroup *cgrp,static int cpu_shares_write_u64 (struct cgroup *cgrp,struct cftype *cftype,u64 shareval) { -return sched_group_set_shares (cgroup_tg (cgrp), shareval); +return sched_group_set_shares (cgroup_tg (cgrp),scale_load (shareval)); }static u64 cpu_shares_read_u64 (struct cgroup *cgrp,struct cftype *cft) {struct task_group *tg =cgroup_tg (cgrp); -return (u64 ) tg->shares; +return (u64 )scale_load_down (tg->shares); }#endif /* CONFIG_FAIR_GROUP_SCHED */
Re: [PATCH v2 3/3] sched: increase SCHED_LOAD_SCALE resolution
In reply to Nikhil RaoQuoted 4 lines · Nikhil Rao
/*
- * Increase resolution of nice-level calculations:
+ * Increase resolution of nice-level calculations for 64-bit architectures. */
include/linux/sched.h enum cpu_idle_type { · line 801
Quoted 10 lines
include/linux/sched.h +9
-1-#define SCHED_LOAD_SHIFT 10 +#if BITS_PER_LONG >32 +#define SCHED_LOAD_RESOLUTION 10 +#define scale_load (w) (w << SCHED_LOAD_RESOLUTION) +#define scale_load_down (w) (w >> SCHED_LOAD_RESOLUTION) +#else +#define SCHED_LOAD_RESOLUTION 0 +#define scale_load (w) w +#define scale_load_down (w) w +#endif
Please also be a bit more verbose in the comment above why we treat 64-bit architectures differently - it's not obvious.
Thanks,
Ingo