Fix find busiest queue 2.6.0-test9
PATCH 1/1 Fix find busiest queue 2.6.0-test9
Hi
I believe this is a simple typo / variable name mixup between rq_src and this_rq. So far all testing shows positive (if minor) improvements.
Con
kernel/sched.c +3 -3
@@ -1073,11 +1073,11 @@ static inline runqueue_t *find_busiest_qcontinue ; rq_src =cpu_rq (i); -if (idle || (rq_src->nr_running < this_rq->prev_cpu_load[i])) +if (idle || (rq_src->nr_running < rq_src->prev_cpu_load[i])) load = rq_src->nr_running;else - load = this_rq->prev_cpu_load[i]; - this_rq->prev_cpu_load[i] = rq_src->nr_running; + load = rq_src->prev_cpu_load[i]; + rq_src->prev_cpu_load[i] = rq_src->nr_running;if ((load > max_load) && (rq_src != this_rq)) { busiest = rq_src;
Quoted 4 lines · Con Kolivas
Hi
I believe this is a simple typo / variable name mixup between
rq_srcandthis_rq. So far all testing shows positive (if minor) improvements.
Hi Con,
It was correct (as Ingo intended) before the patch, I think.
Quoted 6 lines · Con Kolivas
Hi
I believe this is a simple typo / variable name mixup between
rq_srcandthis_rq. So far all testing shows positive (if minor) improvements.
Looks right to me. I'll queue this up for the 2.6.1 deluge.
kernel/sched.c +3 -3
@@ -1073,11 +1073,11 @@ static inline runqueue_t *find_busiest_qcontinue ; rq_src =cpu_rq (i); -if (idle || (rq_src->nr_running < this_rq->prev_cpu_load[i])) +if (idle || (rq_src->nr_running < rq_src->prev_cpu_load[i])) load = rq_src->nr_running;else - load = this_rq->prev_cpu_load[i]; - this_rq->prev_cpu_load[i] = rq_src->nr_running; + load = rq_src->prev_cpu_load[i]; + rq_src->prev_cpu_load[i] = rq_src->nr_running;if ((load > max_load) && (rq_src != this_rq)) { busiest = rq_src;
Quoted 10 lines · Andrew Morton
Quoted 5 lines · Con Kolivas
Hi
I believe this is a simple typo / variable name mixup between
rq_srcandthis_rq. So far all testing shows positive (if minor) improvements.Looks right to me. I'll queue this up for the 2.6.1 deluge.
prev_cpu_load[i] is nr_running of cpu i last time this operation was performed. Either it, or the current nr_running is taken, whichever is lower.
I guess its done this way for cache benefits, but it was correct as Ingo intended. For example, with Con's patch you can see rq_src->prev_cpu_load[i] will only ever use the ith position in the array.
I wonder what improvements Con is seeing? If they are significant, change prev_cpu_load to be an integer type and do a similar thing here.
Quoted 14 lines · Nick Piggin
Quoted 11 lines · Andrew Morton
Quoted 6 lines · Con Kolivas
Hi
I believe this is a simple typo / variable name mixup between
rq_srcandthis_rq. So far all testing shows positive (if minor) improvements.Looks right to me. I'll queue this up for the 2.6.1 deluge.
Quoted 7 lines
prev_cpu_load[i] is
nr_runningof cpu i last time this operation was performed. Either it, or the currentnr_runningis taken, whichever is lower.I guess its done this way for cache benefits, but it was correct as Ingo intended. For example, with Con's patch you can see rq_src->prev_cpu_load[i] will only ever use the ith position in the array.
Yes. The prev_cpu_load[] array takes a snapshot of the run queue lengths seen by the current rq (this_rq). The code is ok as is, and the reason is to avoid stealing tasks too fast from remote CPU (cache thing). Time ago I also tried to store an K-average (by varying K) rq length in prev_cpu_load[] instead of a simple min-of-two-values:
this_rq->prev_cpu_load[i] = (K * this_rq->prev_cpu_load[i] + rq_src->nr_running) / (K + 1);
I couldn't see any major improvements in my 2SMP (never tried on bigger SMP/NUMA).
- Davide
Quoted 14 lines
Quoted 7 lines
prev_cpu_load[i] is
nr_runningof cpu i last time this operation was performed. Either it, or the currentnr_runningis taken, whichever is lower.I guess its done this way for cache benefits, but it was correct as Ingo intended. For example, with Con's patch you can see rq_src->prev_cpu_load[i] will only ever use the ith position in the array.
Yes. The prev_cpu_load[] array takes a snapshot of the run queue lengths seen by the current rq (
this_rq). The code is ok as is, and the reason is to avoid stealing tasks too fast from remote CPU (cache thing). Time ago I also tried to store an K-average (by varying K) rq length in prev_cpu_load[] instead of a simple min-of-two-values:
Quoted 3 lines
this_rq->prev_cpu_load[i] = (K * this_rq->prev_cpu_load[i] +
rq_src->nr_running) / (K + 1);I couldn't see any major improvements in my 2SMP (never tried on bigger SMP/NUMA).
I ran it on the 16-way - no difference in performance. If the code is correct as was before (and I agree, it seems it was), perhaps it's just in need of a big fat comment to explain the confusion? ;-)
M.
Quoted 3 lines · Martin J. Bligh
I ran it on the 16-way - no difference in performance. If the code is correct as was before (and I agree, it seems it was), perhaps it's just in need of a big fat comment to explain the confusion? ;-)
Ingo already dropped a fat comment ;) This is the relevant part:
* We fend off statistical fluctuations in runqueue lengths by
* saving the runqueue length during the previous load-balancing
* operation and using the smaller one the current and saved lengths.
- Davide
Quoted 9 lines
Quoted 3 lines
I ran it on the 16-way - no difference in performance. If the code is correct as was before (and I agree, it seems it was), perhaps it's just in need of a big fat comment to explain the confusion? ;-)
Ingo already dropped a fat comment ;) This is the relevant part:
* We fend off statistical fluctuations in runqueue lengths by
* saving the runqueue length during the previous load-balancing
* operation and using the smaller one the current and saved lengths.
I think the confusing bit is this:
this_rq->prev_cpu_load[i] = rq_src->nr_running;
where "this_rq->prev_cpu_load[i]" doesn't intuitively look like what it means ;-) Even just 's/i/cpu/' would help a bit, or something (like wrapping it in macros). Seems it is correct as was though - thanks for explaining it.
M.
Quoted 8 lines · Martin J. Bligh
I think the confusing bit is this:
this_rq->prev_cpu_load[i] =
rq_src->nr_running;where "this_rq->prev_cpu_load[i]" doesn't intuitively look like what it means ;-) Even just 's/i/cpu/' would help a bit, or something (like wrapping it in macros). Seems it is correct as was though - thanks for explaining it.
Maybe something like:
* We fend off statistical fluctuations in runqueue lengths by
* saving the runqueue length (as seen by the balancing CPU) during the
* previous load-balancing operation and using the smaller one the current
* and saved lengths.
- Davide
Quoted 10 lines · Davide Libenzi
Quoted 8 lines · Martin J. Bligh
I think the confusing bit is this:
this_rq->prev_cpu_load[i] =
rq_src->nr_running;where "this_rq->prev_cpu_load[i]" doesn't intuitively look like what it means ;-) Even just 's/i/cpu/' would help a bit, or something (like wrapping it in macros). Seems it is correct as was though - thanks for explaining it.
Quoted 5 lines
Maybe something like:
* We fend off statistical fluctuations in runqueue lengths by
* saving the runqueue length (as seen by the balancing CPU) during the
* previous load-balancing operation and using the smaller one the current
"during the previous load-balancing operation" is what made me interpret it differently.
Con
On the same vein ... this looks odd:
* We fend off statistical fluctuations in runqueue lengths by
* saving the runqueue length during the previous load-balancing
* operation and using the smaller one the current and saved lengths.
* If a runqueue is long enough for a longer amount of time then
* we recognize it and pull tasks from it.
...
if (idle || (this_rq->nr_running > this_rq->prev_cpu_load[this_cpu]))
nr_running = this_rq->nr_running;
else
nr_running = this_rq->prev_cpu_load[this_cpu];It says we uses the smaller of the two in the comment, but then it seems to use the > of the two in the code? Unless I'm losing it, which is likely ;-)
Later, we do "*imbalance = (max_load - nr_running) / 2;" ... to "fend off statistical fluctuations", we want to reduce imbalance, which would mean a larger nr_running .... so to my mind, it's the comment that's wrong?
M.
Quoted 8 lines · Martin J. Bligh
On the same vein ... this looks odd:
* We fend off statistical fluctuations in runqueue lengths by
* saving the runqueue length during the previous load-balancing
* operation and using the smaller one the current and saved lengths.
* If a runqueue is long enough for a longer amount of time then
* we recognize it and pull tasks from it.
...
Quoted 8 lines
if (idle || (this_rq->nr_running > this_rq->prev_cpu_load[this_cpu])) nr_running = this_rq->nr_running; else nr_running = this_rq->prev_cpu_load[this_cpu];It says we uses the smaller of the two in the comment, but then it seems to use the > of the two in the code? Unless I'm losing it, which is likely ;-)
You want the larger of the two values to be taken for the destination queue, and the smaller to be taken for the source queue. This way you get a minimal imbalance.
Quoted 6 lines · Davide Libenzi
Maybe something like:
* We fend off statistical fluctuations in runqueue lengths by
* saving the runqueue length (as seen by the balancing CPU) during the
* previous load-balancing operation and using the smaller one the current
* and saved lengths.
yep, agreed - patch for 2.6.1 attached. (I also fixed a typo in the original comment and reformatted the lines) Anything else?
Ingo
sched.c renamed from sched.c.orig +5 -4
@@ -1061,10 +1061,11 @@* the lock held. * * We fend off statistical fluctuations in runqueue lengths by -* saving the runqueue length during the previous load-balancing -* operation and using the smaller one the current and saved lengths. -* If a runqueue is long enough for a longer amount of time then -* we recognize it and pull tasks from it. +* saving the runqueue length (as seen by the balancing CPU) during +* the previous load-balancing operation and using the smaller one +* of the current and saved lengths. If a runqueue is long enough +* for a longer amount of time then we recognize it and pull tasks +* from it. * * The 'current runqueue length' is a statistical maximum variable, * for that one we take the longer one - to avoid fluctuations in
Quoted 10 lines · Davide Libenzi
Quoted 3 lines · Martin J. Bligh
I ran it on the 16-way - no difference in performance. If the code is correct as was before (and I agree, it seems it was), perhaps it's just in need of a big fat comment to explain the confusion? ;-)
Ingo already dropped a fat comment ;) This is the relevant part:
* We fend off statistical fluctuations in runqueue lengths by
* saving the runqueue length during the previous load-balancing
* operation and using the smaller one the current and saved lengths.
Well that was the comment that led me to make that patch.
After discussion with mbligh it seems the confusion coming from me seeing ->prev_cpu_load
as the load for that runqueue the last time we balanced; whereas it's actually the load of the last runqueue checked during the balancing.
Anyway the performance differences are tiny on my testing and non existent on mblighs so forget it.
Con
Quoted 10 lines
Quoted 9 lines
Quoted 3 lines
I ran it on the 16-way - no difference in performance. If the code is correct as was before (and I agree, it seems it was), perhaps it's just in need of a big fat comment to explain the confusion? ;-)
Ingo already dropped a fat comment ;) This is the relevant part:
* We fend off statistical fluctuations in runqueue lengths by
* saving the runqueue length during the previous load-balancing
* operation and using the smaller one the current and saved lengths.
Quoted 6 lines
Well that was the comment that led me to make that patch.
After discussion with mbligh it seems the confusion coming from me seeing ->prev_cpu_load
as the load for that runqueue the last time we balanced; whereas it's actually the load of the last runqueue checked during the balancing.
Personally, I think the following makes the code more readable - if several of us can't easily see what the code is doing, I think it's a problem. I'm sure it makes me the very personification of Evil to suggest such a thing, but I don't care ;-)
kernel/sched.c +13 -5
@@ -902,6 +902,14 @@ static inline unsigned int double_lock_breturn nr_running; } +/* +* macro to make the code more readable - this_rq->prev_cpu_load[i] +* is our local cached value of i's prev cpu_load. However, putting +* this_rq->prev_cpu_load into the code makes it read like it's the +* prev_cpu_load of this_cpu, which makes it confusing to read +*/ +#define prev_cpu_load_cache (cpu) (this_rq->prev_cpu_load[cpu]) +/* * find_busiest_queue - find the busiest runqueue among the cpus in cpumask. */
@@ -932,10 +940,10 @@ static inline runqueue_t *find_busiest_q* that case we are less picky about moving a task across CPUs and * take what can be taken. */ -if (idle || (this_rq->nr_running > this_rq->prev_cpu_load[this_cpu])) +if (idle || (this_rq->nr_running >prev_cpu_load_cache (this_cpu))) nr_running = this_rq->nr_running;else - nr_running = this_rq->prev_cpu_load[this_cpu]; + nr_running =prev_cpu_load_cache (this_cpu); busiest =NULL ; max_load =1 ;
@@ -944,11 +952,11 @@ static inline runqueue_t *find_busiest_qcontinue ; rq_src =cpu_rq (i); -if (idle || (rq_src->nr_running < this_rq->prev_cpu_load[i])) +if (idle || (rq_src->nr_running <prev_cpu_load_cache (i))) load = rq_src->nr_running;else - load = this_rq->prev_cpu_load[i]; - this_rq->prev_cpu_load[i] = rq_src->nr_running; + load =prev_cpu_load_cache (i); +prev_cpu_load_cache (i) = rq_src->nr_running;if ((load > max_load) && (rq_src != this_rq)) { busiest = rq_src;
kernel/sched.c static inline unsigned int double_lock_b · line 911
Quoted 7 lines · Martin J. Bligh
kernel/sched.c +7
-0+/* +* macro to make the code more readable - this_rq->prev_cpu_load[i] +* is our local cached value of i's prev cpu_load. However, putting +* this_rq->prev_cpu_load into the code makes it read like it's the +* prev_cpu_load of this_cpu, which makes it confusing to read +*/ +#define prev_cpu_load_cache (cpu) (this_rq->prev_cpu_load[cpu])
Ouch, the implicit "this_rq" is really evil ;) Eventually:
#define prev_cpu_load_cache(rq, cpu) (rq->prev_cpu_load[cpu])
- Davide
Quoted 9 lines
kernel/sched.c static inline unsigned int double_lock_b · line 911
Quoted 7 lines
kernel/sched.c +7
-0+/* +* macro to make the code more readable - this_rq->prev_cpu_load[i] +* is our local cached value of i's prev cpu_load. However, putting +* this_rq->prev_cpu_load into the code makes it read like it's the +* prev_cpu_load of this_cpu, which makes it confusing to read +*/ +#define prev_cpu_load_cache (cpu) (this_rq->prev_cpu_load[cpu])Ouch, the implicit "
this_rq" is really evil ;) Eventually:
Yeah, I know, but ...
Quoted 1 line
# define prev_cpu_load_cache(rq, cpu) (rq->prev_cpu_load[cpu])
I think the implicit version is clearer to read. Oh well, maybe a good balance between readability and sheer evil ;-)
M.