Do some major performance tuning of the userland scheduler.
authorMatthew Dillon <dillon@dragonflybsd.org>
Sun, 28 Mar 2004 08:03:05 +0000 (08:03 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Sun, 28 Mar 2004 08:03:05 +0000 (08:03 +0000)
When determining whether to reschedule, use a relative priority comparison
against PPQ rather then a queue index comparison to avoid the edge case
where two processes are only a p_priority of 1 apart, but fall into
different queues.  This reduces unnecessary preemptive context switches.
Also change the sense of test_resched() and document it.

Properly incriement p_ru.ru_nivcsw (involuntary context switches stat counter).

Fix uio_yield().  We have to call lwkt_setpri_self() to cycle our thread
to the end of its runq, and we do not need to call acquire_curproc() and
release_curproc() after switching.

When returning to userland, lower our priority and call lwkt_maybe_switch()
BEFORE acquiring P_CURPROC.  Before we called lwkt_maybe_switch() after we
acquired P_CURPROC which could result in us holding P_CURPROC, switching to
another thread which itself returns to usermode at a higher priority, and
that thread having to switch back to us to release P_CURPROC and then us back
to the other thread again.  This reduces the number of unnecessary context
switches that occur in certain situations.  In particular, this cuts the
number of context switches in PIPE situations by 50-75% (1/2 to 2/3).

sys/i386/i386/swtch.s
sys/i386/i386/trap.c
sys/kern/kern_switch.c
sys/kern/lwkt_thread.c
sys/platform/pc32/i386/swtch.s
sys/platform/pc32/i386/trap.c

index 23330a9..02482a3 100644 (file)
@@ -35,7 +35,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/i386/i386/swtch.s,v 1.89.2.10 2003/01/23 03:36:24 ps Exp $
- * $DragonFly: src/sys/i386/i386/Attic/swtch.s,v 1.30 2004/02/21 06:37:07 dillon Exp $
+ * $DragonFly: src/sys/i386/i386/Attic/swtch.s,v 1.31 2004/03/28 08:03:05 dillon Exp $
  */
 
 #include "use_npx.h"
@@ -532,7 +532,7 @@ ENTRY(cpu_kthread_restore)
  */
 ENTRY(cpu_lwkt_switch)
        movl    4(%esp),%eax
-       pushl   %ebp
+       pushl   %ebp    /* note: GDB hacked to locate ebp relative to td_sp */
        pushl   %ebx
        pushl   %esi
        pushl   %edi
index 4b71263..db58fba 100644 (file)
@@ -36,7 +36,7 @@
  *
  *     from: @(#)trap.c        7.4 (Berkeley) 5/13/91
  * $FreeBSD: src/sys/i386/i386/trap.c,v 1.147.2.11 2003/02/27 19:09:59 luoqi Exp $
- * $DragonFly: src/sys/i386/i386/Attic/trap.c,v 1.46 2004/03/06 19:40:23 dillon Exp $
+ * $DragonFly: src/sys/i386/i386/Attic/trap.c,v 1.47 2004/03/28 08:03:05 dillon Exp $
  */
 
 /*
@@ -236,7 +236,11 @@ userexit(struct proc *p)
         *
         * Lowering our priority may make other higher priority threads
         * runnable. lwkt_setpri_self() does not switch away, so call
-        * lwkt_maybe_switch() to deal with it.  
+        * lwkt_maybe_switch() to deal with it.  We do this *before* we
+        * acquire P_CURPROC because another thread may also be intending
+        * to return to userland and if it has a higher user priority then
+        * us it will have to block and force us to reschedule, resulting in
+        * unnecessary extra context switches.
         *
         * WARNING!  Once our priority is lowered to a user level priority
         * it is possible, once we return to user mode (or if we were to
@@ -246,10 +250,14 @@ userexit(struct proc *p)
        td->td_release = NULL;
        if ((p->p_flag & (P_CP_RELEASED|P_CURPROC)) == P_CURPROC) {
                ++fast_release;
+               lwkt_maybe_switch();
        } else {
                ++slow_release;
+               lwkt_setpri_self(TDPRI_USER_NORM);
+               lwkt_maybe_switch();
                acquire_curproc(p);
-
+#if 0
+               /* POSSIBLE FUTURE */
                switch(p->p_rtprio.type) {
                case RTP_PRIO_IDLE:
                        lwkt_setpri_self(TDPRI_USER_IDLE);
@@ -262,8 +270,8 @@ userexit(struct proc *p)
                        lwkt_setpri_self(TDPRI_USER_NORM);
                        break;
                }
+#endif
        }
-       lwkt_maybe_switch();
 }
 
 
index 3ad34cb..5cb4b75 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/kern/kern_switch.c,v 1.3.2.1 2000/05/16 06:58:12 dillon Exp $
- * $DragonFly: src/sys/kern/Attic/kern_switch.c,v 1.18 2004/03/01 06:33:17 dillon Exp $
+ * $DragonFly: src/sys/kern/Attic/kern_switch.c,v 1.19 2004/03/28 08:03:02 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -37,6 +37,7 @@
 #include <sys/thread2.h>
 #include <sys/uio.h>
 #include <sys/sysctl.h>
+#include <sys/resourcevar.h>
 #include <machine/ipl.h>
 #include <machine/cpu.h>
 #include <machine/smp.h>
@@ -94,8 +95,6 @@ SYSCTL_INT(_debug, OID_AUTO, choose_affinity, CTLFLAG_RD,
         &choose_affinity, 0, "chooseproc() was smart");
 #endif
 
-static void sched_thread_cpu_init(void);
-
 #define USCHED_COUNTER(td)     ((td->td_gd == mycpu) ? ++usched_optimal : ++usched_steal)
 
 /*
@@ -115,18 +114,27 @@ rqinit(void *dummy)
 }
 SYSINIT(runqueue, SI_SUB_RUN_QUEUE, SI_ORDER_FIRST, rqinit, NULL)
 
+/*
+ * Returns 1 if curp is equal to or better then newp.  Note that
+ * lower p_priority values == higher process priorities.
+ *
+ * This routine is only called when the current process is trying to acquire
+ * P_CURPROC.  Since it is already in-context we cut it some slack.
+ */
 static __inline
 int
 test_resched(struct proc *curp, struct proc *newp)
 {
-       if (newp->p_priority / PPQ <= curp->p_priority / PPQ)
+       if (curp->p_priority - newp->p_priority < PPQ)
                return(1);
        return(0);
 }
 
 /*
- * chooseproc() is called when a cpu needs a user process to LWKT schedule.
- * chooseproc() will select a user process and return it.
+ * chooseproc() is called when a cpu needs a user process to LWKT schedule,
+ * it selects a user process and returns it.  If chkp is non-NULL and chkp
+ * has the same or higher priority then the process that would otherwise be
+ * chosen, NULL is returned.
  */
 static
 struct proc *
@@ -157,10 +165,10 @@ chooseproc(struct proc *chkp)
        KASSERT(p, ("chooseproc: no proc on busy queue"));
 
        /*
-        * If the chosen process is not at a higher priority then chkp
-        * then return NULL without dequeueing a new process.
+        * If the passed process is better then the selected process,
+        * return NULL. 
         */
-       if (chkp && !test_resched(chkp, p))
+       if (chkp && test_resched(chkp, p))
                return(NULL);
 
 #ifdef SMP
@@ -333,15 +341,16 @@ setrunqueue(struct proc *p)
 
        /*
         * Check cpu affinity for user preemption (when the curprocmask bit
-        * is set)
+        * is set).  Note that gd_upri is a speculative field (we modify
+        * another cpu's gd_upri to avoid sending ipiq storms).
         */
        if (gd == mycpu) {
-               if (p->p_priority / PPQ < gd->gd_upri / PPQ) {
+               if (p->p_priority - gd->gd_upri <= -PPQ) {
                        need_resched();
                        --count;
                }
        } else if (remote_resched) {
-               if (p->p_priority / PPQ < gd->gd_upri / PPQ) {
+               if (p->p_priority - gd->gd_upri <= -PPQ) {
                        gd->gd_upri = p->p_priority;
                        lwkt_send_ipiq(gd, need_resched_remote, NULL);
                        --count;
@@ -395,7 +404,7 @@ setrunqueue(struct proc *p)
                if (rdyprocmask & (1 << cpuid)) {
                        gd = globaldata_find(cpuid);
 
-                       if (p->p_priority / PPQ < gd->gd_upri / PPQ - 2) {
+                       if (p->p_priority - gd->gd_upri <= -PPQ) {
                                gd->gd_upri = p->p_priority;
                                lwkt_send_ipiq(gd, need_resched_remote, NULL);
                                ++remote_resched_nonaffinity;
@@ -403,7 +412,8 @@ setrunqueue(struct proc *p)
                }
        }
 #else
-       if (p->p_priority / PPQ < gd->gd_upri / PPQ) {
+       if (p->p_priority - gd->gd_upri <= -PPQ) {
+               /* do not set gd_upri */
                need_resched();
        }
 #endif
@@ -593,6 +603,7 @@ acquire_curproc(struct proc *p)
                        break;
                }
                lwkt_deschedule_self();
+               p->p_stats->p_ru.ru_nivcsw++;   /* involuntary context sw */
                p->p_flag &= ~P_CP_RELEASED;
                setrunqueue(p);
                lwkt_switch();  /* CPU CAN CHANGE DUE TO SETRUNQUEUE() */
@@ -613,6 +624,9 @@ acquire_curproc(struct proc *p)
  * ensure that we are running at a kernel LWKT priority, and this priority
  * is not lowered through the reacquisition and rerelease sequence to ensure
  * that we do not deadlock against a higher priority *user* process.
+ *
+ * We call lwkt_setpri_self() to rotate our thread to the end of the lwkt
+ * run queue.
  */
 void
 uio_yield(void)
@@ -620,11 +634,10 @@ uio_yield(void)
        struct thread *td = curthread;
        struct proc *p = td->td_proc;
 
+       lwkt_setpri_self(td->td_pri & TDPRI_MASK);
        if (p) {
                p->p_flag |= P_PASSIVE_ACQ;
                lwkt_switch();
-               acquire_curproc(p);
-               release_curproc(p);
                p->p_flag &= ~P_PASSIVE_ACQ;
        } else {
                lwkt_switch();
index 92285b3..f9c1257 100644 (file)
@@ -23,7 +23,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $DragonFly: src/sys/kern/lwkt_thread.c,v 1.56 2004/03/01 06:33:17 dillon Exp $
+ * $DragonFly: src/sys/kern/lwkt_thread.c,v 1.57 2004/03/28 08:03:02 dillon Exp $
  */
 
 /*
@@ -985,6 +985,10 @@ lwkt_deschedule(thread_t td)
  * We have to retain the critical section count which uses the high bits
  * of the td_pri field.  The specified priority may also indicate zero or
  * more critical sections by adding TDPRI_CRIT*N.
+ *
+ * Note that we requeue the thread whether it winds up on a different runq
+ * or not.  uio_yield() depends on this and the routine is not normally
+ * called with the same priority otherwise.
  */
 void
 lwkt_setpri(thread_t td, int pri)
index 7e7661b..4266ca8 100644 (file)
@@ -35,7 +35,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/i386/i386/swtch.s,v 1.89.2.10 2003/01/23 03:36:24 ps Exp $
- * $DragonFly: src/sys/platform/pc32/i386/swtch.s,v 1.30 2004/02/21 06:37:07 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/i386/swtch.s,v 1.31 2004/03/28 08:03:05 dillon Exp $
  */
 
 #include "use_npx.h"
@@ -532,7 +532,7 @@ ENTRY(cpu_kthread_restore)
  */
 ENTRY(cpu_lwkt_switch)
        movl    4(%esp),%eax
-       pushl   %ebp
+       pushl   %ebp    /* note: GDB hacked to locate ebp relative to td_sp */
        pushl   %ebx
        pushl   %esi
        pushl   %edi
index 95c474c..2bdf604 100644 (file)
@@ -36,7 +36,7 @@
  *
  *     from: @(#)trap.c        7.4 (Berkeley) 5/13/91
  * $FreeBSD: src/sys/i386/i386/trap.c,v 1.147.2.11 2003/02/27 19:09:59 luoqi Exp $
- * $DragonFly: src/sys/platform/pc32/i386/trap.c,v 1.46 2004/03/06 19:40:23 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/i386/trap.c,v 1.47 2004/03/28 08:03:05 dillon Exp $
  */
 
 /*
@@ -236,7 +236,11 @@ userexit(struct proc *p)
         *
         * Lowering our priority may make other higher priority threads
         * runnable. lwkt_setpri_self() does not switch away, so call
-        * lwkt_maybe_switch() to deal with it.  
+        * lwkt_maybe_switch() to deal with it.  We do this *before* we
+        * acquire P_CURPROC because another thread may also be intending
+        * to return to userland and if it has a higher user priority then
+        * us it will have to block and force us to reschedule, resulting in
+        * unnecessary extra context switches.
         *
         * WARNING!  Once our priority is lowered to a user level priority
         * it is possible, once we return to user mode (or if we were to
@@ -246,10 +250,14 @@ userexit(struct proc *p)
        td->td_release = NULL;
        if ((p->p_flag & (P_CP_RELEASED|P_CURPROC)) == P_CURPROC) {
                ++fast_release;
+               lwkt_maybe_switch();
        } else {
                ++slow_release;
+               lwkt_setpri_self(TDPRI_USER_NORM);
+               lwkt_maybe_switch();
                acquire_curproc(p);
-
+#if 0
+               /* POSSIBLE FUTURE */
                switch(p->p_rtprio.type) {
                case RTP_PRIO_IDLE:
                        lwkt_setpri_self(TDPRI_USER_IDLE);
@@ -262,8 +270,8 @@ userexit(struct proc *p)
                        lwkt_setpri_self(TDPRI_USER_NORM);
                        break;
                }
+#endif
        }
-       lwkt_maybe_switch();
 }