Commit cea46387 authored by Jan Ziak's avatar Jan Ziak

runtime: add garbage collector statistics

If the constant CollectStats is non-zero and GOGCTRACE=1
the garbage collector will print basic statistics about executed
GC instructions.

R=golang-dev, dvyukov
CC=golang-dev, rsc
https://golang.org/cl/7413049
parent a85fce28
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
enum { enum {
Debug = 0, Debug = 0,
DebugMark = 0, // run second pass to check mark DebugMark = 0, // run second pass to check mark
CollectStats = 0,
// Four bits per word (see #defines below). // Four bits per word (see #defines below).
wordsPerBitmapWord = sizeof(void*)*8/4, wordsPerBitmapWord = sizeof(void*)*8/4,
...@@ -165,8 +166,29 @@ enum { ...@@ -165,8 +166,29 @@ enum {
GC_DEFAULT_PTR = GC_NUM_INSTR, GC_DEFAULT_PTR = GC_NUM_INSTR,
GC_MAP_NEXT, GC_MAP_NEXT,
GC_CHAN, GC_CHAN,
GC_NUM_INSTR2
}; };
static struct {
struct {
uint64 sum;
uint64 cnt;
} ptr;
uint64 nbytes;
struct {
uint64 sum;
uint64 cnt;
uint64 notype;
uint64 typelookup;
} obj;
uint64 rescan;
uint64 rescanbytes;
uint64 instr[GC_NUM_INSTR2];
uint64 putempty;
uint64 getfull;
} gcstats;
// markonly marks an object. It returns true if the object // markonly marks an object. It returns true if the object
// has been marked by this function, false otherwise. // has been marked by this function, false otherwise.
// This function isn't thread-safe and doesn't append the object to any buffer. // This function isn't thread-safe and doesn't append the object to any buffer.
...@@ -315,6 +337,11 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf ...@@ -315,6 +337,11 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf
n = ptrbuf_end - ptrbuf; n = ptrbuf_end - ptrbuf;
*ptrbufpos = ptrbuf; *ptrbufpos = ptrbuf;
if(CollectStats) {
runtime·xadd64(&gcstats.ptr.sum, n);
runtime·xadd64(&gcstats.ptr.cnt, 1);
}
// If buffer is nearly full, get a new one. // If buffer is nearly full, get a new one.
if(wbuf == nil || nobj+n >= nelem(wbuf->obj)) { if(wbuf == nil || nobj+n >= nelem(wbuf->obj)) {
if(wbuf != nil) if(wbuf != nil)
...@@ -621,6 +648,12 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) ...@@ -621,6 +648,12 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
runtime·printf("scanblock %p %D\n", b, (int64)n); runtime·printf("scanblock %p %D\n", b, (int64)n);
} }
if(CollectStats) {
runtime·xadd64(&gcstats.nbytes, n);
runtime·xadd64(&gcstats.obj.sum, nobj);
runtime·xadd64(&gcstats.obj.cnt, 1);
}
if(ti != 0) { if(ti != 0) {
pc = (uintptr*)(ti & ~(uintptr)PC_BITS); pc = (uintptr*)(ti & ~(uintptr)PC_BITS);
precise_type = (ti & PRECISE); precise_type = (ti & PRECISE);
...@@ -634,8 +667,14 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) ...@@ -634,8 +667,14 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
stack_top.count = 1; stack_top.count = 1;
} }
} else if(UseSpanType) { } else if(UseSpanType) {
if(CollectStats)
runtime·xadd64(&gcstats.obj.notype, 1);
type = runtime·gettype(b); type = runtime·gettype(b);
if(type != 0) { if(type != 0) {
if(CollectStats)
runtime·xadd64(&gcstats.obj.typelookup, 1);
t = (Type*)(type & ~(uintptr)(PtrSize-1)); t = (Type*)(type & ~(uintptr)(PtrSize-1));
switch(type & (PtrSize-1)) { switch(type & (PtrSize-1)) {
case TypeInfo_SingleObject: case TypeInfo_SingleObject:
...@@ -692,6 +731,9 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) ...@@ -692,6 +731,9 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
end_b = (uintptr)b + n - PtrSize; end_b = (uintptr)b + n - PtrSize;
for(;;) { for(;;) {
if(CollectStats)
runtime·xadd64(&gcstats.instr[pc[0]], 1);
obj = nil; obj = nil;
objti = 0; objti = 0;
switch(pc[0]) { switch(pc[0]) {
...@@ -807,6 +849,10 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) ...@@ -807,6 +849,10 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking)
// Found a value that may be a pointer. // Found a value that may be a pointer.
// Do a rescan of the entire block. // Do a rescan of the entire block.
enqueue((Obj){b, n, 0}, &wbuf, &wp, &nobj); enqueue((Obj){b, n, 0}, &wbuf, &wp, &nobj);
if(CollectStats) {
runtime·xadd64(&gcstats.rescan, 1);
runtime·xadd64(&gcstats.rescanbytes, n);
}
break; break;
} }
} }
...@@ -1164,6 +1210,9 @@ getempty(Workbuf *b) ...@@ -1164,6 +1210,9 @@ getempty(Workbuf *b)
static void static void
putempty(Workbuf *b) putempty(Workbuf *b)
{ {
if(CollectStats)
runtime·xadd64(&gcstats.putempty, 1);
runtime·lfstackpush(&work.empty, &b->node); runtime·lfstackpush(&work.empty, &b->node);
} }
...@@ -1173,6 +1222,9 @@ getfull(Workbuf *b) ...@@ -1173,6 +1222,9 @@ getfull(Workbuf *b)
{ {
int32 i; int32 i;
if(CollectStats)
runtime·xadd64(&gcstats.getfull, 1);
if(b != nil) if(b != nil)
runtime·lfstackpush(&work.empty, &b->node); runtime·lfstackpush(&work.empty, &b->node);
b = (Workbuf*)runtime·lfstackpop(&work.full); b = (Workbuf*)runtime·lfstackpop(&work.full);
...@@ -1747,7 +1799,7 @@ static void ...@@ -1747,7 +1799,7 @@ static void
gc(struct gc_args *args) gc(struct gc_args *args)
{ {
int64 t0, t1, t2, t3, t4; int64 t0, t1, t2, t3, t4;
uint64 heap0, heap1, obj0, obj1; uint64 heap0, heap1, obj0, obj1, ninstr;
GCStats stats; GCStats stats;
M *mp; M *mp;
uint32 i; uint32 i;
...@@ -1764,6 +1816,9 @@ gc(struct gc_args *args) ...@@ -1764,6 +1816,9 @@ gc(struct gc_args *args)
m->gcing = 1; m->gcing = 1;
runtime·stoptheworld(); runtime·stoptheworld();
if(CollectStats)
runtime·memclr((byte*)&gcstats, sizeof(gcstats));
for(mp=runtime·allm; mp; mp=mp->alllink) for(mp=runtime·allm; mp; mp=mp->alllink)
runtime·settype_flush(mp, false); runtime·settype_flush(mp, false);
...@@ -1859,6 +1914,27 @@ gc(struct gc_args *args) ...@@ -1859,6 +1914,27 @@ gc(struct gc_args *args)
stats.nhandoff, stats.nhandoffcnt, stats.nhandoff, stats.nhandoffcnt,
work.sweepfor->nsteal, work.sweepfor->nstealcnt, work.sweepfor->nsteal, work.sweepfor->nstealcnt,
stats.nprocyield, stats.nosyield, stats.nsleep); stats.nprocyield, stats.nosyield, stats.nsleep);
if(CollectStats) {
runtime·printf("scan: %D bytes, %D objects, %D untyped, %D types from MSpan\n",
gcstats.nbytes, gcstats.obj.cnt, gcstats.obj.notype, gcstats.obj.typelookup);
if(gcstats.ptr.cnt != 0)
runtime·printf("avg ptrbufsize: %D (%D/%D)\n",
gcstats.ptr.sum/gcstats.ptr.cnt, gcstats.ptr.sum, gcstats.ptr.cnt);
if(gcstats.obj.cnt != 0)
runtime·printf("avg nobj: %D (%D/%D)\n",
gcstats.obj.sum/gcstats.obj.cnt, gcstats.obj.sum, gcstats.obj.cnt);
runtime·printf("rescans: %D, %D bytes\n", gcstats.rescan, gcstats.rescanbytes);
runtime·printf("instruction counts:\n");
ninstr = 0;
for(i=0; i<nelem(gcstats.instr); i++) {
runtime·printf("\t%d:\t%D\n", i, gcstats.instr[i]);
ninstr += gcstats.instr[i];
}
runtime·printf("\ttotal:\t%D\n", ninstr);
runtime·printf("putempty: %D, getfull: %D\n", gcstats.putempty, gcstats.getfull);
}
} }
runtime·MProf_GC(); runtime·MProf_GC();
......
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