mirror of
https://github.com/golang/go
synced 2024-11-23 05:30:07 -07:00
runtime: faster parallel GC
Use per-thread work buffers instead of global mutex-protected pool. This eliminates contention from parallel scan phase. benchmark old ns/op new ns/op delta garbage.BenchmarkTree2-8 97100768 71417553 -26.45% garbage.BenchmarkTree2LastPause-8 970931485 714103692 -26.45% garbage.BenchmarkTree2Pause-8 469127802 345029253 -26.45% garbage.BenchmarkParser-8 2880950854 2715456901 -5.74% garbage.BenchmarkParserLastPause-8 137047399 103336476 -24.60% garbage.BenchmarkParserPause-8 80686028 58922680 -26.97% R=golang-dev, 0xe2.0x9a.0x9b, dave, adg, rsc, iant CC=golang-dev https://golang.org/cl/7816044
This commit is contained in:
parent
656bc3eb96
commit
d4c80d19a8
@ -140,6 +140,7 @@ static Workbuf* getempty(Workbuf*);
|
|||||||
static Workbuf* getfull(Workbuf*);
|
static Workbuf* getfull(Workbuf*);
|
||||||
static void putempty(Workbuf*);
|
static void putempty(Workbuf*);
|
||||||
static Workbuf* handoff(Workbuf*);
|
static Workbuf* handoff(Workbuf*);
|
||||||
|
static void gchelperstart(void);
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
uint64 full; // lock-free list of full blocks
|
uint64 full; // lock-free list of full blocks
|
||||||
@ -287,11 +288,12 @@ struct BufferList
|
|||||||
{
|
{
|
||||||
PtrTarget ptrtarget[IntermediateBufferCapacity];
|
PtrTarget ptrtarget[IntermediateBufferCapacity];
|
||||||
Obj obj[IntermediateBufferCapacity];
|
Obj obj[IntermediateBufferCapacity];
|
||||||
BufferList *next;
|
uint32 busy;
|
||||||
|
byte pad[CacheLineSize];
|
||||||
};
|
};
|
||||||
static BufferList *bufferList;
|
#pragma dataflag 16 // no pointers
|
||||||
|
static BufferList bufferList[MaxGcproc];
|
||||||
|
|
||||||
static Lock lock;
|
|
||||||
static Type *itabtype;
|
static Type *itabtype;
|
||||||
|
|
||||||
static void enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj);
|
static void enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj);
|
||||||
@ -598,23 +600,11 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
|
|||||||
|
|
||||||
// Allocate ptrbuf
|
// Allocate ptrbuf
|
||||||
{
|
{
|
||||||
runtime·lock(&lock);
|
scanbuffers = &bufferList[m->helpgc];
|
||||||
|
|
||||||
if(bufferList == nil) {
|
|
||||||
bufferList = runtime·SysAlloc(sizeof(*bufferList));
|
|
||||||
if(bufferList == nil)
|
|
||||||
runtime·throw("runtime: cannot allocate memory");
|
|
||||||
bufferList->next = nil;
|
|
||||||
}
|
|
||||||
scanbuffers = bufferList;
|
|
||||||
bufferList = bufferList->next;
|
|
||||||
|
|
||||||
ptrbuf = &scanbuffers->ptrtarget[0];
|
ptrbuf = &scanbuffers->ptrtarget[0];
|
||||||
ptrbuf_end = &scanbuffers->ptrtarget[0] + nelem(scanbuffers->ptrtarget);
|
ptrbuf_end = &scanbuffers->ptrtarget[0] + nelem(scanbuffers->ptrtarget);
|
||||||
objbuf = &scanbuffers->obj[0];
|
objbuf = &scanbuffers->obj[0];
|
||||||
objbuf_end = &scanbuffers->obj[0] + nelem(scanbuffers->obj);
|
objbuf_end = &scanbuffers->obj[0] + nelem(scanbuffers->obj);
|
||||||
|
|
||||||
runtime·unlock(&lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ptrbufpos = ptrbuf;
|
ptrbufpos = ptrbuf;
|
||||||
@ -1056,11 +1046,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
|
|||||||
nobj--;
|
nobj--;
|
||||||
}
|
}
|
||||||
|
|
||||||
endscan:
|
endscan:;
|
||||||
runtime·lock(&lock);
|
|
||||||
scanbuffers->next = bufferList;
|
|
||||||
bufferList = scanbuffers;
|
|
||||||
runtime·unlock(&lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// debug_scanblock is the debug copy of scanblock.
|
// debug_scanblock is the debug copy of scanblock.
|
||||||
@ -1688,6 +1674,8 @@ runtime·memorydump(void)
|
|||||||
void
|
void
|
||||||
runtime·gchelper(void)
|
runtime·gchelper(void)
|
||||||
{
|
{
|
||||||
|
gchelperstart();
|
||||||
|
|
||||||
// parallel mark for over gc roots
|
// parallel mark for over gc roots
|
||||||
runtime·parfordo(work.markfor);
|
runtime·parfordo(work.markfor);
|
||||||
|
|
||||||
@ -1701,6 +1689,7 @@ runtime·gchelper(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
runtime·parfordo(work.sweepfor);
|
runtime·parfordo(work.sweepfor);
|
||||||
|
bufferList[m->helpgc].busy = 0;
|
||||||
if(runtime·xadd(&work.ndone, +1) == work.nproc-1)
|
if(runtime·xadd(&work.ndone, +1) == work.nproc-1)
|
||||||
runtime·notewakeup(&work.alldone);
|
runtime·notewakeup(&work.alldone);
|
||||||
}
|
}
|
||||||
@ -1892,6 +1881,7 @@ gc(struct gc_args *args)
|
|||||||
|
|
||||||
t1 = runtime·nanotime();
|
t1 = runtime·nanotime();
|
||||||
|
|
||||||
|
gchelperstart();
|
||||||
runtime·parfordo(work.markfor);
|
runtime·parfordo(work.markfor);
|
||||||
scanblock(nil, nil, 0, true);
|
scanblock(nil, nil, 0, true);
|
||||||
|
|
||||||
@ -1903,6 +1893,7 @@ gc(struct gc_args *args)
|
|||||||
t2 = runtime·nanotime();
|
t2 = runtime·nanotime();
|
||||||
|
|
||||||
runtime·parfordo(work.sweepfor);
|
runtime·parfordo(work.sweepfor);
|
||||||
|
bufferList[m->helpgc].busy = 0;
|
||||||
t3 = runtime·nanotime();
|
t3 = runtime·nanotime();
|
||||||
|
|
||||||
if(work.nproc > 1)
|
if(work.nproc > 1)
|
||||||
@ -2043,6 +2034,15 @@ runtime∕debug·setGCPercent(intgo in, intgo out)
|
|||||||
FLUSH(&out);
|
FLUSH(&out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gchelperstart(void)
|
||||||
|
{
|
||||||
|
if(m->helpgc < 0 || m->helpgc >= MaxGcproc)
|
||||||
|
runtime·throw("gchelperstart: bad m->helpgc");
|
||||||
|
if(runtime·xchg(&bufferList[m->helpgc].busy, 1))
|
||||||
|
runtime·throw("gchelperstart: already busy");
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
runfinq(void)
|
runfinq(void)
|
||||||
{
|
{
|
||||||
|
@ -332,7 +332,7 @@ runtime·helpgc(int32 nproc)
|
|||||||
mp = mget();
|
mp = mget();
|
||||||
if(mp == nil)
|
if(mp == nil)
|
||||||
runtime·throw("runtime·gcprocs inconsistency");
|
runtime·throw("runtime·gcprocs inconsistency");
|
||||||
mp->helpgc = 1;
|
mp->helpgc = n;
|
||||||
mp->mcache = runtime·allp[pos]->mcache;
|
mp->mcache = runtime·allp[pos]->mcache;
|
||||||
pos++;
|
pos++;
|
||||||
runtime·notewakeup(&mp->park);
|
runtime·notewakeup(&mp->park);
|
||||||
@ -386,7 +386,7 @@ runtime·stoptheworld(void)
|
|||||||
static void
|
static void
|
||||||
mhelpgc(void)
|
mhelpgc(void)
|
||||||
{
|
{
|
||||||
m->helpgc = 1;
|
m->helpgc = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -485,7 +485,7 @@ runtime·mstart(void)
|
|||||||
m->mstartfn();
|
m->mstartfn();
|
||||||
|
|
||||||
if(m->helpgc) {
|
if(m->helpgc) {
|
||||||
m->helpgc = false;
|
m->helpgc = 0;
|
||||||
stopm();
|
stopm();
|
||||||
} else if(m != &runtime·m0) {
|
} else if(m != &runtime·m0) {
|
||||||
acquirep(m->nextp);
|
acquirep(m->nextp);
|
||||||
@ -794,8 +794,8 @@ retry:
|
|||||||
runtime·notesleep(&m->park);
|
runtime·notesleep(&m->park);
|
||||||
runtime·noteclear(&m->park);
|
runtime·noteclear(&m->park);
|
||||||
if(m->helpgc) {
|
if(m->helpgc) {
|
||||||
m->helpgc = 0;
|
|
||||||
runtime·gchelper();
|
runtime·gchelper();
|
||||||
|
m->helpgc = 0;
|
||||||
m->mcache = nil;
|
m->mcache = nil;
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user