Commit 5e963a82 authored by Ian Lance Taylor's avatar Ian Lance Taylor

runtime: reduce lock contention via wakeup on scheduler unlock.

R=rsc
CC=golang-dev
https://golang.org/cl/4275043
parent c01238a5
...@@ -78,6 +78,13 @@ struct Sched { ...@@ -78,6 +78,13 @@ struct Sched {
Sched runtime·sched; Sched runtime·sched;
int32 gomaxprocs; int32 gomaxprocs;
// An m which is waiting for notewakeup(&m->havenextg). This may be
// only be accessed while the scheduler lock is held. This is used to
// minimize the number of times we call notewakeup while the scheduler
// lock is held, since the m will normally move quickly to lock the
// scheduler itself, producing lock contention.
static M* mwakeup;
// Scheduling helpers. Sched must be locked. // Scheduling helpers. Sched must be locked.
static void gput(G*); // put/get on ghead/gtail static void gput(G*); // put/get on ghead/gtail
static G* gget(void); static G* gget(void);
...@@ -133,6 +140,26 @@ runtime·schedinit(void) ...@@ -133,6 +140,26 @@ runtime·schedinit(void)
m->nomemprof--; m->nomemprof--;
} }
// Lock the scheduler.
static void
schedlock(void)
{
runtime·lock(&runtime·sched);
}
// Unlock the scheduler.
static void
schedunlock(void)
{
M *m;
m = mwakeup;
mwakeup = nil;
runtime·unlock(&runtime·sched);
if(m != nil)
runtime·notewakeup(&m->havenextg);
}
// Called after main·init_function; main·main will be called on return. // Called after main·init_function; main·main will be called on return.
void void
runtime·initdone(void) runtime·initdone(void)
...@@ -144,9 +171,9 @@ runtime·initdone(void) ...@@ -144,9 +171,9 @@ runtime·initdone(void)
// If main·init_function started other goroutines, // If main·init_function started other goroutines,
// kick off new ms to handle them, like ready // kick off new ms to handle them, like ready
// would have, had it not been pre-dawn. // would have, had it not been pre-dawn.
runtime·lock(&runtime·sched); schedlock();
matchmg(); matchmg();
runtime·unlock(&runtime·sched); schedunlock();
} }
void void
...@@ -264,9 +291,9 @@ mget(G *g) ...@@ -264,9 +291,9 @@ mget(G *g)
void void
runtime·ready(G *g) runtime·ready(G *g)
{ {
runtime·lock(&runtime·sched); schedlock();
readylocked(g); readylocked(g);
runtime·unlock(&runtime·sched); schedunlock();
} }
// Mark g ready to run. Sched is already locked. // Mark g ready to run. Sched is already locked.
...@@ -317,7 +344,9 @@ mnextg(M *m, G *g) ...@@ -317,7 +344,9 @@ mnextg(M *m, G *g)
m->nextg = g; m->nextg = g;
if(m->waitnextg) { if(m->waitnextg) {
m->waitnextg = 0; m->waitnextg = 0;
runtime·notewakeup(&m->havenextg); if(mwakeup != nil)
runtime·notewakeup(&mwakeup->havenextg);
mwakeup = m;
} }
} }
...@@ -338,7 +367,7 @@ nextgandunlock(void) ...@@ -338,7 +367,7 @@ nextgandunlock(void)
if(m->nextg != nil) { if(m->nextg != nil) {
gp = m->nextg; gp = m->nextg;
m->nextg = nil; m->nextg = nil;
runtime·unlock(&runtime·sched); schedunlock();
return gp; return gp;
} }
...@@ -356,7 +385,7 @@ nextgandunlock(void) ...@@ -356,7 +385,7 @@ nextgandunlock(void)
continue; continue;
} }
runtime·sched.mcpu++; // this m will run gp runtime·sched.mcpu++; // this m will run gp
runtime·unlock(&runtime·sched); schedunlock();
return gp; return gp;
} }
// Otherwise, wait on global m queue. // Otherwise, wait on global m queue.
...@@ -371,7 +400,7 @@ nextgandunlock(void) ...@@ -371,7 +400,7 @@ nextgandunlock(void)
runtime·sched.waitstop = 0; runtime·sched.waitstop = 0;
runtime·notewakeup(&runtime·sched.stopped); runtime·notewakeup(&runtime·sched.stopped);
} }
runtime·unlock(&runtime·sched); schedunlock();
runtime·notesleep(&m->havenextg); runtime·notesleep(&m->havenextg);
if((gp = m->nextg) == nil) if((gp = m->nextg) == nil)
...@@ -385,7 +414,7 @@ nextgandunlock(void) ...@@ -385,7 +414,7 @@ nextgandunlock(void)
void void
runtime·stoptheworld(void) runtime·stoptheworld(void)
{ {
runtime·lock(&runtime·sched); schedlock();
runtime·gcwaiting = 1; runtime·gcwaiting = 1;
runtime·sched.mcpumax = 1; runtime·sched.mcpumax = 1;
while(runtime·sched.mcpu > 1) { while(runtime·sched.mcpu > 1) {
...@@ -395,11 +424,11 @@ runtime·stoptheworld(void) ...@@ -395,11 +424,11 @@ runtime·stoptheworld(void)
// so this is okay. // so this is okay.
runtime·noteclear(&runtime·sched.stopped); runtime·noteclear(&runtime·sched.stopped);
runtime·sched.waitstop = 1; runtime·sched.waitstop = 1;
runtime·unlock(&runtime·sched); schedunlock();
runtime·notesleep(&runtime·sched.stopped); runtime·notesleep(&runtime·sched.stopped);
runtime·lock(&runtime·sched); schedlock();
} }
runtime·unlock(&runtime·sched); schedunlock();
} }
// TODO(rsc): Remove. This is only temporary, // TODO(rsc): Remove. This is only temporary,
...@@ -407,11 +436,11 @@ runtime·stoptheworld(void) ...@@ -407,11 +436,11 @@ runtime·stoptheworld(void)
void void
runtime·starttheworld(void) runtime·starttheworld(void)
{ {
runtime·lock(&runtime·sched); schedlock();
runtime·gcwaiting = 0; runtime·gcwaiting = 0;
runtime·sched.mcpumax = runtime·gomaxprocs; runtime·sched.mcpumax = runtime·gomaxprocs;
matchmg(); matchmg();
runtime·unlock(&runtime·sched); schedunlock();
} }
// Called to start an M. // Called to start an M.
...@@ -500,7 +529,7 @@ matchmg(void) ...@@ -500,7 +529,7 @@ matchmg(void)
static void static void
schedule(G *gp) schedule(G *gp)
{ {
runtime·lock(&runtime·sched); schedlock();
if(gp != nil) { if(gp != nil) {
if(runtime·sched.predawn) if(runtime·sched.predawn)
runtime·throw("init rescheduling"); runtime·throw("init rescheduling");
...@@ -584,7 +613,7 @@ runtime·entersyscall(void) ...@@ -584,7 +613,7 @@ runtime·entersyscall(void)
runtime·gosave(&g->sched); runtime·gosave(&g->sched);
if(runtime·sched.predawn) if(runtime·sched.predawn)
return; return;
runtime·lock(&runtime·sched); schedlock();
g->status = Gsyscall; g->status = Gsyscall;
runtime·sched.mcpu--; runtime·sched.mcpu--;
runtime·sched.msyscall++; runtime·sched.msyscall++;
...@@ -594,7 +623,7 @@ runtime·entersyscall(void) ...@@ -594,7 +623,7 @@ runtime·entersyscall(void)
runtime·sched.waitstop = 0; runtime·sched.waitstop = 0;
runtime·notewakeup(&runtime·sched.stopped); runtime·notewakeup(&runtime·sched.stopped);
} }
runtime·unlock(&runtime·sched); schedunlock();
} }
// The goroutine g exited its system call. // The goroutine g exited its system call.
...@@ -607,13 +636,13 @@ runtime·exitsyscall(void) ...@@ -607,13 +636,13 @@ runtime·exitsyscall(void)
if(runtime·sched.predawn) if(runtime·sched.predawn)
return; return;
runtime·lock(&runtime·sched); schedlock();
runtime·sched.msyscall--; runtime·sched.msyscall--;
runtime·sched.mcpu++; runtime·sched.mcpu++;
// Fast path - if there's room for this m, we're done. // Fast path - if there's room for this m, we're done.
if(runtime·sched.mcpu <= runtime·sched.mcpumax) { if(runtime·sched.mcpu <= runtime·sched.mcpumax) {
g->status = Grunning; g->status = Grunning;
runtime·unlock(&runtime·sched); schedunlock();
return; return;
} }
// Tell scheduler to put g back on the run queue: // Tell scheduler to put g back on the run queue:
...@@ -621,7 +650,7 @@ runtime·exitsyscall(void) ...@@ -621,7 +650,7 @@ runtime·exitsyscall(void)
// but keeps the garbage collector from thinking // but keeps the garbage collector from thinking
// that g is running right now, which it's not. // that g is running right now, which it's not.
g->readyonstop = 1; g->readyonstop = 1;
runtime·unlock(&runtime·sched); schedunlock();
// Slow path - all the cpus are taken. // Slow path - all the cpus are taken.
// The scheduler will ready g and put this m to sleep. // The scheduler will ready g and put this m to sleep.
...@@ -815,7 +844,7 @@ runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc) ...@@ -815,7 +844,7 @@ runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc)
if(siz > 1024) if(siz > 1024)
runtime·throw("runtime.newproc: too many args"); runtime·throw("runtime.newproc: too many args");
runtime·lock(&runtime·sched); schedlock();
if((newg = gfget()) != nil){ if((newg = gfget()) != nil){
newg->status = Gwaiting; newg->status = Gwaiting;
...@@ -848,7 +877,7 @@ runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc) ...@@ -848,7 +877,7 @@ runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc)
newg->goid = runtime·goidgen; newg->goid = runtime·goidgen;
newprocreadylocked(newg); newprocreadylocked(newg);
runtime·unlock(&runtime·sched); schedunlock();
return newg; return newg;
//printf(" goid=%d\n", newg->goid); //printf(" goid=%d\n", newg->goid);
...@@ -1156,7 +1185,7 @@ runtime·gomaxprocsfunc(int32 n) ...@@ -1156,7 +1185,7 @@ runtime·gomaxprocsfunc(int32 n)
{ {
int32 ret; int32 ret;
runtime·lock(&runtime·sched); schedlock();
ret = runtime·gomaxprocs; ret = runtime·gomaxprocs;
if (n <= 0) if (n <= 0)
n = ret; n = ret;
...@@ -1164,7 +1193,7 @@ runtime·gomaxprocsfunc(int32 n) ...@@ -1164,7 +1193,7 @@ runtime·gomaxprocsfunc(int32 n)
runtime·sched.mcpumax = n; runtime·sched.mcpumax = n;
// handle fewer procs? // handle fewer procs?
if(runtime·sched.mcpu > runtime·sched.mcpumax) { if(runtime·sched.mcpu > runtime·sched.mcpumax) {
runtime·unlock(&runtime·sched); schedunlock();
// just give up the cpu. // just give up the cpu.
// we'll only get rescheduled once the // we'll only get rescheduled once the
// number has come down. // number has come down.
...@@ -1173,7 +1202,7 @@ runtime·gomaxprocsfunc(int32 n) ...@@ -1173,7 +1202,7 @@ runtime·gomaxprocsfunc(int32 n)
} }
// handle more procs // handle more procs
matchmg(); matchmg();
runtime·unlock(&runtime·sched); schedunlock();
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment