Modify the way we account for CPU time spent (step 1)

Keep track of time spent by the cpu in various contexts in units of
"cputicks" and scale to real-world microsec^H^H^H^H^H^H^H^Hclock_t
only when somebody wants to inspect the numbers.

For now "cputicks" are still derived from the current timecounter
and therefore things should by definition remain sensible also on
SMP machines.  (The main reason for this first milestone commit is
to verify that hypothesis.)

On slower machines, the avoided multiplications to normalize timestams
at every context switch, comes out as a 5-7% better score on the
unixbench/context1 microbenchmark.  On more modern hardware no change
in performance is seen.
This commit is contained in:
Poul-Henning Kamp 2006-02-07 21:22:02 +00:00
parent 9bfe35c80d
commit 5b1a8eb397
16 changed files with 71 additions and 32 deletions

View File

@ -225,7 +225,7 @@ smp_init_secondary(void)
spinlock_exit(); spinlock_exit();
KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count")); KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count"));
binuptime(PCPU_PTR(switchtime)); PCPU_SET(switchtime, cpu_ticks());
PCPU_SET(switchticks, ticks); PCPU_SET(switchticks, ticks);
cpu_throw(NULL, choosethread()); /* doesn't return */ cpu_throw(NULL, choosethread()); /* doesn't return */

View File

@ -582,7 +582,7 @@ init_secondary(void)
spinlock_exit(); spinlock_exit();
KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count")); KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count"));
binuptime(PCPU_PTR(switchtime)); PCPU_SET(switchtime, cpu_ticks());
PCPU_SET(switchticks, ticks); PCPU_SET(switchticks, ticks);
cpu_throw(NULL, choosethread()); /* doesn't return */ cpu_throw(NULL, choosethread()); /* doesn't return */

View File

@ -631,7 +631,7 @@ init_secondary(void)
spinlock_exit(); spinlock_exit();
KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count")); KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count"));
binuptime(PCPU_PTR(switchtime)); PCPU_SET(switchtime, cpu_ticks());
PCPU_SET(switchticks, ticks); PCPU_SET(switchticks, ticks);
cpu_throw(NULL, choosethread()); /* doesn't return */ cpu_throw(NULL, choosethread()); /* doesn't return */

View File

@ -138,7 +138,7 @@ ia64_ap_startup(void)
spinlock_exit(); spinlock_exit();
KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count")); KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count"));
binuptime(PCPU_PTR(switchtime)); PCPU_SET(switchtime, cpu_ticks());
PCPU_SET(switchticks, ticks); PCPU_SET(switchticks, ticks);
ia64_set_tpr(0); ia64_set_tpr(0);

View File

@ -459,11 +459,10 @@ proc0_post(void *dummy __unused)
sx_slock(&allproc_lock); sx_slock(&allproc_lock);
LIST_FOREACH(p, &allproc, p_list) { LIST_FOREACH(p, &allproc, p_list) {
microuptime(&p->p_stats->p_start); microuptime(&p->p_stats->p_start);
p->p_rux.rux_runtime.sec = 0; p->p_rux.rux_runtime = 0;
p->p_rux.rux_runtime.frac = 0;
} }
sx_sunlock(&allproc_lock); sx_sunlock(&allproc_lock);
binuptime(PCPU_PTR(switchtime)); PCPU_SET(switchtime, cpu_ticks());
PCPU_SET(switchticks, ticks); PCPU_SET(switchticks, ticks);
/* /*

View File

@ -110,7 +110,7 @@ sys_exit(struct thread *td, struct sys_exit_args *uap)
void void
exit1(struct thread *td, int rv) exit1(struct thread *td, int rv)
{ {
struct bintime new_switchtime; uint64_t new_switchtime;
struct proc *p, *nq, *q; struct proc *p, *nq, *q;
struct tty *tp; struct tty *tp;
struct vnode *ttyvp; struct vnode *ttyvp;
@ -543,9 +543,8 @@ retry:
ruadd(p->p_ru, &p->p_rux, &p->p_stats->p_cru, &p->p_crux); ruadd(p->p_ru, &p->p_rux, &p->p_stats->p_cru, &p->p_crux);
/* Do the same timestamp bookkeeping that mi_switch() would do. */ /* Do the same timestamp bookkeeping that mi_switch() would do. */
binuptime(&new_switchtime); new_switchtime = cpu_ticks();
bintime_add(&p->p_rux.rux_runtime, &new_switchtime); p->p_rux.rux_runtime += (new_switchtime - PCPU_GET(switchtime));
bintime_sub(&p->p_rux.rux_runtime, PCPU_PTR(switchtime));
PCPU_SET(switchtime, new_switchtime); PCPU_SET(switchtime, new_switchtime);
PCPU_SET(switchticks, ticks); PCPU_SET(switchticks, ticks);
cnt.v_swtch++; cnt.v_swtch++;

View File

@ -624,7 +624,6 @@ fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp)
struct thread *td0; struct thread *td0;
struct tty *tp; struct tty *tp;
struct session *sp; struct session *sp;
struct timeval tv;
struct ucred *cred; struct ucred *cred;
struct sigacts *ps; struct sigacts *ps;
@ -695,8 +694,7 @@ fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp)
kp->ki_swtime = p->p_swtime; kp->ki_swtime = p->p_swtime;
kp->ki_pid = p->p_pid; kp->ki_pid = p->p_pid;
kp->ki_nice = p->p_nice; kp->ki_nice = p->p_nice;
bintime2timeval(&p->p_rux.rux_runtime, &tv); kp->ki_runtime = p->p_rux.rux_runtime * 1000000 / cpu_tickrate();
kp->ki_runtime = tv.tv_sec * (u_int64_t)1000000 + tv.tv_usec;
mtx_unlock_spin(&sched_lock); mtx_unlock_spin(&sched_lock);
if ((p->p_sflag & PS_INMEM) && p->p_stats != NULL) { if ((p->p_sflag & PS_INMEM) && p->p_stats != NULL) {
kp->ki_start = p->p_stats->p_start; kp->ki_start = p->p_stats->p_start;

View File

@ -704,7 +704,7 @@ calcru(p, up, sp)
struct timeval *up; struct timeval *up;
struct timeval *sp; struct timeval *sp;
{ {
struct bintime bt; uint64_t bt;
struct rusage_ext rux; struct rusage_ext rux;
struct thread *td; struct thread *td;
int bt_valid; int bt_valid;
@ -712,6 +712,7 @@ calcru(p, up, sp)
PROC_LOCK_ASSERT(p, MA_OWNED); PROC_LOCK_ASSERT(p, MA_OWNED);
mtx_assert(&sched_lock, MA_NOTOWNED); mtx_assert(&sched_lock, MA_NOTOWNED);
bt_valid = 0; bt_valid = 0;
bt = 0;
mtx_lock_spin(&sched_lock); mtx_lock_spin(&sched_lock);
rux = p->p_rux; rux = p->p_rux;
FOREACH_THREAD_IN_PROC(p, td) { FOREACH_THREAD_IN_PROC(p, td) {
@ -725,12 +726,16 @@ calcru(p, up, sp)
KASSERT(td->td_oncpu != NOCPU, KASSERT(td->td_oncpu != NOCPU,
("%s: running thread has no CPU", __func__)); ("%s: running thread has no CPU", __func__));
if (!bt_valid) { if (!bt_valid) {
binuptime(&bt); bt = cpu_ticks();
bt_valid = 1; bt_valid = 1;
} }
bintime_add(&rux.rux_runtime, &bt); /*
bintime_sub(&rux.rux_runtime, * XXX: Doesn't this mean that this quantum will
&pcpu_find(td->td_oncpu)->pc_switchtime); * XXX: get counted twice if calcru() is called
* XXX: from SIGINFO ?
*/
rux.rux_runtime +=
(bt - pcpu_find(td->td_oncpu)->pc_switchtime);
} }
} }
mtx_unlock_spin(&sched_lock); mtx_unlock_spin(&sched_lock);
@ -758,7 +763,6 @@ calcru1(p, ruxp, up, sp)
struct timeval *up; struct timeval *up;
struct timeval *sp; struct timeval *sp;
{ {
struct timeval tv;
/* {user, system, interrupt, total} {ticks, usec}; previous tu: */ /* {user, system, interrupt, total} {ticks, usec}; previous tu: */
u_int64_t ut, uu, st, su, it, iu, tt, tu, ptu; u_int64_t ut, uu, st, su, it, iu, tt, tu, ptu;
@ -770,8 +774,7 @@ calcru1(p, ruxp, up, sp)
st = 1; st = 1;
tt = 1; tt = 1;
} }
bintime2timeval(&ruxp->rux_runtime, &tv); tu = (ruxp->rux_runtime * 1000000LL) / cpu_tickrate();
tu = (u_int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
ptu = ruxp->rux_uu + ruxp->rux_su + ruxp->rux_iu; ptu = ruxp->rux_uu + ruxp->rux_su + ruxp->rux_iu;
if (tu < ptu) { if (tu < ptu) {
printf( printf(
@ -884,7 +887,7 @@ ruadd(ru, rux, ru2, rux2)
register long *ip, *ip2; register long *ip, *ip2;
register int i; register int i;
bintime_add(&rux->rux_runtime, &rux2->rux_runtime); rux->rux_runtime += rux2->rux_runtime;
rux->rux_uticks += rux2->rux_uticks; rux->rux_uticks += rux2->rux_uticks;
rux->rux_sticks += rux2->rux_sticks; rux->rux_sticks += rux2->rux_sticks;
rux->rux_iticks += rux2->rux_iticks; rux->rux_iticks += rux2->rux_iticks;

View File

@ -355,7 +355,7 @@ wakeup_one(ident)
void void
mi_switch(int flags, struct thread *newtd) mi_switch(int flags, struct thread *newtd)
{ {
struct bintime new_switchtime; uint64_t new_switchtime;
struct thread *td; struct thread *td;
struct proc *p; struct proc *p;
@ -384,9 +384,8 @@ mi_switch(int flags, struct thread *newtd)
* Compute the amount of time during which the current * Compute the amount of time during which the current
* process was running, and add that to its total so far. * process was running, and add that to its total so far.
*/ */
binuptime(&new_switchtime); new_switchtime = cpu_ticks();
bintime_add(&p->p_rux.rux_runtime, &new_switchtime); p->p_rux.rux_runtime += (new_switchtime - PCPU_GET(switchtime));
bintime_sub(&p->p_rux.rux_runtime, PCPU_PTR(switchtime));
td->td_generation++; /* bump preempt-detect counter */ td->td_generation++; /* bump preempt-detect counter */
@ -405,7 +404,7 @@ mi_switch(int flags, struct thread *newtd)
* it reaches the max, arrange to kill the process in ast(). * it reaches the max, arrange to kill the process in ast().
*/ */
if (p->p_cpulimit != RLIM_INFINITY && if (p->p_cpulimit != RLIM_INFINITY &&
p->p_rux.rux_runtime.sec >= p->p_cpulimit) { p->p_rux.rux_runtime >= p->p_cpulimit * cpu_tickrate()) {
p->p_sflag |= PS_XCPU; p->p_sflag |= PS_XCPU;
td->td_flags |= TDF_ASTPENDING; td->td_flags |= TDF_ASTPENDING;
} }

View File

@ -131,6 +131,7 @@ sysctl_kern_boottime(SYSCTL_HANDLER_ARGS)
#endif #endif
return SYSCTL_OUT(req, &boottime, sizeof(boottime)); return SYSCTL_OUT(req, &boottime, sizeof(boottime));
} }
/* /*
* Return the difference between the timehands' counter value now and what * Return the difference between the timehands' counter value now and what
* was when we copied it to the timehands' offset_count. * was when we copied it to the timehands' offset_count.
@ -782,3 +783,23 @@ inittimecounter(void *dummy)
} }
SYSINIT(timecounter, SI_SUB_CLOCKS, SI_ORDER_SECOND, inittimecounter, NULL) SYSINIT(timecounter, SI_SUB_CLOCKS, SI_ORDER_SECOND, inittimecounter, NULL)
static
uint64_t
tc_cpu_ticks(void)
{
static uint64_t base;
static unsigned last;
uint64_t u;
struct timecounter *tc;
tc = timehands->th_counter;
u = tc->tc_get_timecount(tc) & tc->tc_counter_mask;
if (u < last)
base += tc->tc_counter_mask + 1;
last = u;
return (u + base);
}
uint64_t (*cpu_ticks)(void) = tc_cpu_ticks;
uint64_t (*cpu_tickrate)(void) = tc_getfrequency;

View File

@ -238,7 +238,7 @@ ast(struct trapframe *framep)
PROC_LOCK(p); PROC_LOCK(p);
lim_rlimit(p, RLIMIT_CPU, &rlim); lim_rlimit(p, RLIMIT_CPU, &rlim);
mtx_lock_spin(&sched_lock); mtx_lock_spin(&sched_lock);
if (p->p_rux.rux_runtime.sec >= rlim.rlim_max) { if (p->p_rux.rux_runtime >= rlim.rlim_max * cpu_tickrate()) {
mtx_unlock_spin(&sched_lock); mtx_unlock_spin(&sched_lock);
killproc(p, "exceeded maximum CPU limit"); killproc(p, "exceeded maximum CPU limit");
} else { } else {

View File

@ -361,7 +361,7 @@ cpu_mp_bootstrap(struct pcpu *pc)
/* ok, now grab sched_lock and enter the scheduler */ /* ok, now grab sched_lock and enter the scheduler */
mtx_lock_spin(&sched_lock); mtx_lock_spin(&sched_lock);
spinlock_exit(); spinlock_exit();
binuptime(PCPU_PTR(switchtime)); PCPU_SET(switchtime, cpu_ticks());
PCPU_SET(switchticks, ticks); PCPU_SET(switchticks, ticks);
cpu_throw(NULL, choosethread()); /* doesn't return */ cpu_throw(NULL, choosethread()); /* doesn't return */
} }

View File

@ -65,6 +65,20 @@ SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_ticks, CTLFLAG_RD, &adjust_ticks,
static void tick_hardclock(struct trapframe *); static void tick_hardclock(struct trapframe *);
static uint64_t
tick_cputicks(void)
{
return (rd(tick));
}
static uint64_t
tick_cputickrate(void)
{
return (tick_freq);
}
void void
cpu_initclocks(void) cpu_initclocks(void)
{ {
@ -156,6 +170,9 @@ tick_init(u_long clock)
* handled. * handled.
*/ */
tick_stop(); tick_stop();
cpu_ticks = tick_cputicks;
cpu_tickrate = tick_cputickrate;
} }
void void

View File

@ -60,7 +60,7 @@ struct pcpu {
struct thread *pc_fpcurthread; /* Fp state owner */ struct thread *pc_fpcurthread; /* Fp state owner */
struct thread *pc_deadthread; /* Zombie thread or NULL */ struct thread *pc_deadthread; /* Zombie thread or NULL */
struct pcb *pc_curpcb; /* Current pcb */ struct pcb *pc_curpcb; /* Current pcb */
struct bintime pc_switchtime; uint64_t pc_switchtime;
int pc_switchticks; int pc_switchticks;
u_int pc_cpuid; /* This cpu number */ u_int pc_cpuid; /* This cpu number */
cpumask_t pc_cpumask; /* This cpu mask */ cpumask_t pc_cpumask; /* This cpu mask */

View File

@ -505,7 +505,7 @@ struct ksegrp {
* Locking: (cj) means (j) for p_rux and (c) for p_crux. * Locking: (cj) means (j) for p_rux and (c) for p_crux.
*/ */
struct rusage_ext { struct rusage_ext {
struct bintime rux_runtime; /* (cj) Real time. */ u_int64_t rux_runtime; /* (cj) Real time. */
u_int64_t rux_uticks; /* (cj) Statclock hits in user mode. */ u_int64_t rux_uticks; /* (cj) Statclock hits in user mode. */
u_int64_t rux_sticks; /* (cj) Statclock hits in sys mode. */ u_int64_t rux_sticks; /* (cj) Statclock hits in sys mode. */
u_int64_t rux_iticks; /* (cj) Statclock hits in intr mode. */ u_int64_t rux_iticks; /* (cj) Statclock hits in intr mode. */

View File

@ -238,6 +238,9 @@ int setenv(const char *name, const char *value);
int unsetenv(const char *name); int unsetenv(const char *name);
int testenv(const char *name); int testenv(const char *name);
extern uint64_t (*cpu_ticks)(void);
extern uint64_t (*cpu_tickrate)(void);
#ifdef APM_FIXUP_CALLTODO #ifdef APM_FIXUP_CALLTODO
struct timeval; struct timeval;
void adjust_timeout_calltodo(struct timeval *time_change); void adjust_timeout_calltodo(struct timeval *time_change);