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:
parent
9bfe35c80d
commit
5b1a8eb397
@ -225,7 +225,7 @@ smp_init_secondary(void)
|
||||
spinlock_exit();
|
||||
KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count"));
|
||||
|
||||
binuptime(PCPU_PTR(switchtime));
|
||||
PCPU_SET(switchtime, cpu_ticks());
|
||||
PCPU_SET(switchticks, ticks);
|
||||
|
||||
cpu_throw(NULL, choosethread()); /* doesn't return */
|
||||
|
@ -582,7 +582,7 @@ init_secondary(void)
|
||||
spinlock_exit();
|
||||
KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count"));
|
||||
|
||||
binuptime(PCPU_PTR(switchtime));
|
||||
PCPU_SET(switchtime, cpu_ticks());
|
||||
PCPU_SET(switchticks, ticks);
|
||||
|
||||
cpu_throw(NULL, choosethread()); /* doesn't return */
|
||||
|
@ -631,7 +631,7 @@ init_secondary(void)
|
||||
spinlock_exit();
|
||||
KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count"));
|
||||
|
||||
binuptime(PCPU_PTR(switchtime));
|
||||
PCPU_SET(switchtime, cpu_ticks());
|
||||
PCPU_SET(switchticks, ticks);
|
||||
|
||||
cpu_throw(NULL, choosethread()); /* doesn't return */
|
||||
|
@ -138,7 +138,7 @@ ia64_ap_startup(void)
|
||||
spinlock_exit();
|
||||
KASSERT(curthread->td_md.md_spinlock_count == 1, ("invalid count"));
|
||||
|
||||
binuptime(PCPU_PTR(switchtime));
|
||||
PCPU_SET(switchtime, cpu_ticks());
|
||||
PCPU_SET(switchticks, ticks);
|
||||
|
||||
ia64_set_tpr(0);
|
||||
|
@ -459,11 +459,10 @@ proc0_post(void *dummy __unused)
|
||||
sx_slock(&allproc_lock);
|
||||
LIST_FOREACH(p, &allproc, p_list) {
|
||||
microuptime(&p->p_stats->p_start);
|
||||
p->p_rux.rux_runtime.sec = 0;
|
||||
p->p_rux.rux_runtime.frac = 0;
|
||||
p->p_rux.rux_runtime = 0;
|
||||
}
|
||||
sx_sunlock(&allproc_lock);
|
||||
binuptime(PCPU_PTR(switchtime));
|
||||
PCPU_SET(switchtime, cpu_ticks());
|
||||
PCPU_SET(switchticks, ticks);
|
||||
|
||||
/*
|
||||
|
@ -110,7 +110,7 @@ sys_exit(struct thread *td, struct sys_exit_args *uap)
|
||||
void
|
||||
exit1(struct thread *td, int rv)
|
||||
{
|
||||
struct bintime new_switchtime;
|
||||
uint64_t new_switchtime;
|
||||
struct proc *p, *nq, *q;
|
||||
struct tty *tp;
|
||||
struct vnode *ttyvp;
|
||||
@ -543,9 +543,8 @@ exit1(struct thread *td, int rv)
|
||||
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. */
|
||||
binuptime(&new_switchtime);
|
||||
bintime_add(&p->p_rux.rux_runtime, &new_switchtime);
|
||||
bintime_sub(&p->p_rux.rux_runtime, PCPU_PTR(switchtime));
|
||||
new_switchtime = cpu_ticks();
|
||||
p->p_rux.rux_runtime += (new_switchtime - PCPU_GET(switchtime));
|
||||
PCPU_SET(switchtime, new_switchtime);
|
||||
PCPU_SET(switchticks, ticks);
|
||||
cnt.v_swtch++;
|
||||
|
@ -624,7 +624,6 @@ fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp)
|
||||
struct thread *td0;
|
||||
struct tty *tp;
|
||||
struct session *sp;
|
||||
struct timeval tv;
|
||||
struct ucred *cred;
|
||||
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_pid = p->p_pid;
|
||||
kp->ki_nice = p->p_nice;
|
||||
bintime2timeval(&p->p_rux.rux_runtime, &tv);
|
||||
kp->ki_runtime = tv.tv_sec * (u_int64_t)1000000 + tv.tv_usec;
|
||||
kp->ki_runtime = p->p_rux.rux_runtime * 1000000 / cpu_tickrate();
|
||||
mtx_unlock_spin(&sched_lock);
|
||||
if ((p->p_sflag & PS_INMEM) && p->p_stats != NULL) {
|
||||
kp->ki_start = p->p_stats->p_start;
|
||||
|
@ -704,7 +704,7 @@ calcru(p, up, sp)
|
||||
struct timeval *up;
|
||||
struct timeval *sp;
|
||||
{
|
||||
struct bintime bt;
|
||||
uint64_t bt;
|
||||
struct rusage_ext rux;
|
||||
struct thread *td;
|
||||
int bt_valid;
|
||||
@ -712,6 +712,7 @@ calcru(p, up, sp)
|
||||
PROC_LOCK_ASSERT(p, MA_OWNED);
|
||||
mtx_assert(&sched_lock, MA_NOTOWNED);
|
||||
bt_valid = 0;
|
||||
bt = 0;
|
||||
mtx_lock_spin(&sched_lock);
|
||||
rux = p->p_rux;
|
||||
FOREACH_THREAD_IN_PROC(p, td) {
|
||||
@ -725,12 +726,16 @@ calcru(p, up, sp)
|
||||
KASSERT(td->td_oncpu != NOCPU,
|
||||
("%s: running thread has no CPU", __func__));
|
||||
if (!bt_valid) {
|
||||
binuptime(&bt);
|
||||
bt = cpu_ticks();
|
||||
bt_valid = 1;
|
||||
}
|
||||
bintime_add(&rux.rux_runtime, &bt);
|
||||
bintime_sub(&rux.rux_runtime,
|
||||
&pcpu_find(td->td_oncpu)->pc_switchtime);
|
||||
/*
|
||||
* XXX: Doesn't this mean that this quantum will
|
||||
* 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);
|
||||
@ -758,7 +763,6 @@ calcru1(p, ruxp, up, sp)
|
||||
struct timeval *up;
|
||||
struct timeval *sp;
|
||||
{
|
||||
struct timeval tv;
|
||||
/* {user, system, interrupt, total} {ticks, usec}; previous tu: */
|
||||
u_int64_t ut, uu, st, su, it, iu, tt, tu, ptu;
|
||||
|
||||
@ -770,8 +774,7 @@ calcru1(p, ruxp, up, sp)
|
||||
st = 1;
|
||||
tt = 1;
|
||||
}
|
||||
bintime2timeval(&ruxp->rux_runtime, &tv);
|
||||
tu = (u_int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
|
||||
tu = (ruxp->rux_runtime * 1000000LL) / cpu_tickrate();
|
||||
ptu = ruxp->rux_uu + ruxp->rux_su + ruxp->rux_iu;
|
||||
if (tu < ptu) {
|
||||
printf(
|
||||
@ -884,7 +887,7 @@ ruadd(ru, rux, ru2, rux2)
|
||||
register long *ip, *ip2;
|
||||
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_sticks += rux2->rux_sticks;
|
||||
rux->rux_iticks += rux2->rux_iticks;
|
||||
|
@ -355,7 +355,7 @@ wakeup_one(ident)
|
||||
void
|
||||
mi_switch(int flags, struct thread *newtd)
|
||||
{
|
||||
struct bintime new_switchtime;
|
||||
uint64_t new_switchtime;
|
||||
struct thread *td;
|
||||
struct proc *p;
|
||||
|
||||
@ -384,9 +384,8 @@ mi_switch(int flags, struct thread *newtd)
|
||||
* Compute the amount of time during which the current
|
||||
* process was running, and add that to its total so far.
|
||||
*/
|
||||
binuptime(&new_switchtime);
|
||||
bintime_add(&p->p_rux.rux_runtime, &new_switchtime);
|
||||
bintime_sub(&p->p_rux.rux_runtime, PCPU_PTR(switchtime));
|
||||
new_switchtime = cpu_ticks();
|
||||
p->p_rux.rux_runtime += (new_switchtime - PCPU_GET(switchtime));
|
||||
|
||||
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().
|
||||
*/
|
||||
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;
|
||||
td->td_flags |= TDF_ASTPENDING;
|
||||
}
|
||||
|
@ -131,6 +131,7 @@ sysctl_kern_boottime(SYSCTL_HANDLER_ARGS)
|
||||
#endif
|
||||
return SYSCTL_OUT(req, &boottime, sizeof(boottime));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the difference between the timehands' counter value now and what
|
||||
* 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)
|
||||
|
||||
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;
|
||||
|
@ -238,7 +238,7 @@ ast(struct trapframe *framep)
|
||||
PROC_LOCK(p);
|
||||
lim_rlimit(p, RLIMIT_CPU, &rlim);
|
||||
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);
|
||||
killproc(p, "exceeded maximum CPU limit");
|
||||
} else {
|
||||
|
@ -361,7 +361,7 @@ cpu_mp_bootstrap(struct pcpu *pc)
|
||||
/* ok, now grab sched_lock and enter the scheduler */
|
||||
mtx_lock_spin(&sched_lock);
|
||||
spinlock_exit();
|
||||
binuptime(PCPU_PTR(switchtime));
|
||||
PCPU_SET(switchtime, cpu_ticks());
|
||||
PCPU_SET(switchticks, ticks);
|
||||
cpu_throw(NULL, choosethread()); /* doesn't return */
|
||||
}
|
||||
|
@ -65,6 +65,20 @@ SYSCTL_INT(_machdep_tick, OID_AUTO, adjust_ticks, CTLFLAG_RD, &adjust_ticks,
|
||||
|
||||
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
|
||||
cpu_initclocks(void)
|
||||
{
|
||||
@ -156,6 +170,9 @@ tick_init(u_long clock)
|
||||
* handled.
|
||||
*/
|
||||
tick_stop();
|
||||
|
||||
cpu_ticks = tick_cputicks;
|
||||
cpu_tickrate = tick_cputickrate;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -60,7 +60,7 @@ struct pcpu {
|
||||
struct thread *pc_fpcurthread; /* Fp state owner */
|
||||
struct thread *pc_deadthread; /* Zombie thread or NULL */
|
||||
struct pcb *pc_curpcb; /* Current pcb */
|
||||
struct bintime pc_switchtime;
|
||||
uint64_t pc_switchtime;
|
||||
int pc_switchticks;
|
||||
u_int pc_cpuid; /* This cpu number */
|
||||
cpumask_t pc_cpumask; /* This cpu mask */
|
||||
|
@ -505,7 +505,7 @@ struct ksegrp {
|
||||
* Locking: (cj) means (j) for p_rux and (c) for p_crux.
|
||||
*/
|
||||
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_sticks; /* (cj) Statclock hits in sys mode. */
|
||||
u_int64_t rux_iticks; /* (cj) Statclock hits in intr mode. */
|
||||
|
@ -238,6 +238,9 @@ int setenv(const char *name, const char *value);
|
||||
int unsetenv(const char *name);
|
||||
int testenv(const char *name);
|
||||
|
||||
extern uint64_t (*cpu_ticks)(void);
|
||||
extern uint64_t (*cpu_tickrate)(void);
|
||||
|
||||
#ifdef APM_FIXUP_CALLTODO
|
||||
struct timeval;
|
||||
void adjust_timeout_calltodo(struct timeval *time_change);
|
||||
|
Loading…
Reference in New Issue
Block a user