- Change ULE to use dynamic slice sizes for the timeshare queue in order

to further reduce latency for threads in this queue.  This should help
   as threads transition from realtime to timeshare.  The latency is
   bound to a max of sched_slice until we have more than sched_slice / 6
   threads runnable.  Then the min slice is allotted to all threads and
   latency becomes (nthreads - 1) * min_slice.

Discussed with: mav
This commit is contained in:
Jeff Roberson 2012-11-08 01:46:47 +00:00
parent 40b43503c0
commit 5e5c387373
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=242736

View file

@ -189,6 +189,12 @@ static struct td_sched td_sched0;
#define SCHED_INTERACT_HALF (SCHED_INTERACT_MAX / 2)
#define SCHED_INTERACT_THRESH (30)
/*
* These parameters determine the slice behavior for batch work.
*/
#define SCHED_SLICE_DEFAULT_DIVISOR 10 /* ~94 ms, 12 stathz ticks. */
#define SCHED_SLICE_MIN_DIVISOR 6 /* DEFAULT/MIN = ~16 ms. */
/* Flags kept in td_flags. */
#define TDF_SLICEEND TDF_SCHED2 /* Thread time slice is over. */
@ -201,9 +207,10 @@ static struct td_sched td_sched0;
* preempt_thresh: Priority threshold for preemption and remote IPIs.
*/
static int sched_interact = SCHED_INTERACT_THRESH;
static int realstathz = 127;
static int tickincr = 8 << SCHED_TICK_SHIFT;
static int sched_slice = 12;
static int realstathz = 127; /* reset during boot. */
static int sched_slice = 10; /* reset during boot. */
static int sched_slice_min = 1; /* reset during boot. */
#ifdef PREEMPTION
#ifdef FULL_PREEMPTION
static int preempt_thresh = PRI_MAX_IDLE;
@ -558,6 +565,30 @@ tdq_load_rem(struct tdq *tdq, struct thread *td)
SDT_PROBE2(sched, , , load_change, (int)TDQ_ID(tdq), tdq->tdq_load);
}
/*
* Bound timeshare latency by decreasing slice size as load increases. We
* consider the maximum latency as the sum of the threads waiting to run
* aside from curthread and target no more than sched_slice latency but
* no less than sched_slice_min runtime.
*/
static inline int
tdq_slice(struct tdq *tdq)
{
int load;
/*
* It is safe to use sys_load here because this is called from
* contexts where timeshare threads are running and so there
* cannot be higher priority load in the system.
*/
load = tdq->tdq_sysload - 1;
if (load >= SCHED_SLICE_MIN_DIVISOR)
return (sched_slice_min);
if (load <= 1)
return (sched_slice);
return (sched_slice / load);
}
/*
* Set lowpri to its exact value by searching the run-queue and
* evaluating curthread. curthread may be passed as an optimization.
@ -1384,7 +1415,8 @@ sched_initticks(void *dummy)
int incr;
realstathz = stathz ? stathz : hz;
sched_slice = realstathz / 10; /* ~100ms */
sched_slice = realstathz / SCHED_SLICE_DEFAULT_DIVISOR;
sched_slice_min = sched_slice / SCHED_SLICE_MIN_DIVISOR;
hogticks = imax(1, (2 * hz * sched_slice + realstathz / 2) /
realstathz);
@ -1585,7 +1617,7 @@ schedinit(void)
thread0.td_sched = &td_sched0;
td_sched0.ts_ltick = ticks;
td_sched0.ts_ftick = ticks;
td_sched0.ts_slice = sched_slice;
td_sched0.ts_slice = 0;
}
/*
@ -2003,8 +2035,10 @@ sched_wakeup(struct thread *td)
sched_interact_update(td);
sched_pctcpu_update(ts, 0);
}
/* Reset the slice value after we sleep. */
ts->ts_slice = sched_slice;
/*
* Reset the slice value since we slept and advanced the round-robin.
*/
ts->ts_slice = 0;
sched_add(td, SRQ_BORING);
}
@ -2036,14 +2070,16 @@ sched_fork_thread(struct thread *td, struct thread *child)
{
struct td_sched *ts;
struct td_sched *ts2;
struct tdq *tdq;
tdq = TDQ_SELF();
THREAD_LOCK_ASSERT(td, MA_OWNED);
/*
* Initialize child.
*/
ts = td->td_sched;
ts2 = child->td_sched;
child->td_lock = TDQ_LOCKPTR(TDQ_SELF());
child->td_lock = TDQ_LOCKPTR(tdq);
child->td_cpuset = cpuset_ref(td->td_cpuset);
ts2->ts_cpu = ts->ts_cpu;
ts2->ts_flags = 0;
@ -2062,7 +2098,8 @@ sched_fork_thread(struct thread *td, struct thread *child)
*/
ts2->ts_slptime = ts->ts_slptime;
ts2->ts_runtime = ts->ts_runtime;
ts2->ts_slice = 1; /* Attempt to quickly learn interactivity. */
/* Attempt to quickly learn interactivity. */
ts2->ts_slice = tdq_slice(tdq) - sched_slice_min;
#ifdef KTR
bzero(ts2->ts_name, sizeof(ts2->ts_name));
#endif
@ -2227,8 +2264,8 @@ sched_clock(struct thread *td)
* Force a context switch if the current thread has used up a full
* time slice (default is 100ms).
*/
if (!TD_IS_IDLETHREAD(td) && --ts->ts_slice <= 0) {
ts->ts_slice = sched_slice;
if (!TD_IS_IDLETHREAD(td) && ++ts->ts_slice >= tdq_slice(tdq)) {
ts->ts_slice = 0;
td->td_flags |= TDF_NEEDRESCHED | TDF_SLICEEND;
}
}
@ -2799,6 +2836,7 @@ sysctl_kern_quantum(SYSCTL_HANDLER_ARGS)
if (new_val <= 0)
return (EINVAL);
sched_slice = imax(1, (new_val + period / 2) / period);
sched_slice_min = sched_slice / SCHED_SLICE_MIN_DIVISOR;
hogticks = imax(1, (2 * hz * sched_slice + realstathz / 2) /
realstathz);
return (0);