mirror of
https://github.com/golang/go.git
synced 2025-11-07 20:21:01 +00:00
runtime: account for tiny allocs, for testing.AllocsPerRun
Fixes #8734. LGTM=r, bradfitz, dvyukov R=bradfitz, r, dvyukov CC=golang-codereviews, iant, khr https://golang.org/cl/143150043
This commit is contained in:
parent
a883f117a9
commit
e19d8a47d1
7 changed files with 43 additions and 3 deletions
|
|
@ -79,6 +79,8 @@ runtime·purgecachedstats(MCache *c)
|
||||||
h = &runtime·mheap;
|
h = &runtime·mheap;
|
||||||
mstats.heap_alloc += c->local_cachealloc;
|
mstats.heap_alloc += c->local_cachealloc;
|
||||||
c->local_cachealloc = 0;
|
c->local_cachealloc = 0;
|
||||||
|
mstats.tinyallocs += c->local_tinyallocs;
|
||||||
|
c->local_tinyallocs = 0;
|
||||||
mstats.nlookup += c->local_nlookup;
|
mstats.nlookup += c->local_nlookup;
|
||||||
c->local_nlookup = 0;
|
c->local_nlookup = 0;
|
||||||
h->largefree += c->local_largefree;
|
h->largefree += c->local_largefree;
|
||||||
|
|
@ -92,9 +94,10 @@ runtime·purgecachedstats(MCache *c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size of the trailing by_size array differs between Go and C,
|
// Size of the trailing by_size array differs between Go and C,
|
||||||
|
// and all data after by_size is local to C, not exported to Go.
|
||||||
// NumSizeClasses was changed, but we can not change Go struct because of backward compatibility.
|
// NumSizeClasses was changed, but we can not change Go struct because of backward compatibility.
|
||||||
// sizeof_C_MStats is what C thinks about size of Go struct.
|
// sizeof_C_MStats is what C thinks about size of Go struct.
|
||||||
uintptr runtime·sizeof_C_MStats = sizeof(MStats) - (NumSizeClasses - 61) * sizeof(mstats.by_size[0]);
|
uintptr runtime·sizeof_C_MStats = offsetof(MStats, by_size[61]);
|
||||||
|
|
||||||
#define MaxArena32 (2U<<30)
|
#define MaxArena32 (2U<<30)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,6 @@ func mallocgc(size uintptr, typ *_type, flags int) unsafe.Pointer {
|
||||||
// standalone escaping variables. On a json benchmark
|
// standalone escaping variables. On a json benchmark
|
||||||
// the allocator reduces number of allocations by ~12% and
|
// the allocator reduces number of allocations by ~12% and
|
||||||
// reduces heap size by ~20%.
|
// reduces heap size by ~20%.
|
||||||
|
|
||||||
tinysize := uintptr(c.tinysize)
|
tinysize := uintptr(c.tinysize)
|
||||||
if size <= tinysize {
|
if size <= tinysize {
|
||||||
tiny := unsafe.Pointer(c.tiny)
|
tiny := unsafe.Pointer(c.tiny)
|
||||||
|
|
@ -121,6 +120,7 @@ func mallocgc(size uintptr, typ *_type, flags int) unsafe.Pointer {
|
||||||
x = tiny
|
x = tiny
|
||||||
c.tiny = (*byte)(add(x, size))
|
c.tiny = (*byte)(add(x, size))
|
||||||
c.tinysize -= uintptr(size1)
|
c.tinysize -= uintptr(size1)
|
||||||
|
c.local_tinyallocs++
|
||||||
if debugMalloc {
|
if debugMalloc {
|
||||||
mp := acquirem()
|
mp := acquirem()
|
||||||
if mp.mallocing == 0 {
|
if mp.mallocing == 0 {
|
||||||
|
|
|
||||||
|
|
@ -278,6 +278,8 @@ struct MStats
|
||||||
uint64 nmalloc;
|
uint64 nmalloc;
|
||||||
uint64 nfree;
|
uint64 nfree;
|
||||||
} by_size[NumSizeClasses];
|
} by_size[NumSizeClasses];
|
||||||
|
|
||||||
|
uint64 tinyallocs; // number of tiny allocations that didn't cause actual allocation; not exported to Go directly
|
||||||
};
|
};
|
||||||
|
|
||||||
#define mstats runtime·memstats
|
#define mstats runtime·memstats
|
||||||
|
|
@ -331,6 +333,7 @@ struct MCache
|
||||||
// See "Tiny allocator" comment in malloc.goc.
|
// See "Tiny allocator" comment in malloc.goc.
|
||||||
byte* tiny;
|
byte* tiny;
|
||||||
uintptr tinysize;
|
uintptr tinysize;
|
||||||
|
uintptr local_tinyallocs; // number of tiny allocs not counted in other stats
|
||||||
// The rest is not accessed on every malloc.
|
// The rest is not accessed on every malloc.
|
||||||
MSpan* alloc[NumSizeClasses]; // spans to allocate from
|
MSpan* alloc[NumSizeClasses]; // spans to allocate from
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ func init() {
|
||||||
var memStats MemStats
|
var memStats MemStats
|
||||||
if sizeof_C_MStats != unsafe.Sizeof(memStats) {
|
if sizeof_C_MStats != unsafe.Sizeof(memStats) {
|
||||||
println(sizeof_C_MStats, unsafe.Sizeof(memStats))
|
println(sizeof_C_MStats, unsafe.Sizeof(memStats))
|
||||||
panic("MStats vs MemStatsType size mismatch")
|
gothrow("MStats vs MemStatsType size mismatch")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1245,6 +1245,7 @@ runtime·updatememstats(GCStats *stats)
|
||||||
mstats.by_size[i].nmalloc += runtime·mheap.nsmallfree[i];
|
mstats.by_size[i].nmalloc += runtime·mheap.nsmallfree[i];
|
||||||
smallfree += runtime·mheap.nsmallfree[i] * runtime·class_to_size[i];
|
smallfree += runtime·mheap.nsmallfree[i] * runtime·class_to_size[i];
|
||||||
}
|
}
|
||||||
|
mstats.nfree += mstats.tinyallocs;
|
||||||
mstats.nmalloc += mstats.nfree;
|
mstats.nmalloc += mstats.nfree;
|
||||||
|
|
||||||
// Calculate derived stats.
|
// Calculate derived stats.
|
||||||
|
|
|
||||||
|
|
@ -184,6 +184,8 @@ mheap_alloc(MHeap *h, uintptr npage, int32 sizeclass, bool large)
|
||||||
// transfer stats from cache to global
|
// transfer stats from cache to global
|
||||||
mstats.heap_alloc += g->m->mcache->local_cachealloc;
|
mstats.heap_alloc += g->m->mcache->local_cachealloc;
|
||||||
g->m->mcache->local_cachealloc = 0;
|
g->m->mcache->local_cachealloc = 0;
|
||||||
|
mstats.tinyallocs += g->m->mcache->local_tinyallocs;
|
||||||
|
g->m->mcache->local_tinyallocs = 0;
|
||||||
|
|
||||||
s = MHeap_AllocSpanLocked(h, npage);
|
s = MHeap_AllocSpanLocked(h, npage);
|
||||||
if(s != nil) {
|
if(s != nil) {
|
||||||
|
|
@ -465,6 +467,8 @@ mheap_free(MHeap *h, MSpan *s, int32 acct)
|
||||||
runtime·lock(&h->lock);
|
runtime·lock(&h->lock);
|
||||||
mstats.heap_alloc += g->m->mcache->local_cachealloc;
|
mstats.heap_alloc += g->m->mcache->local_cachealloc;
|
||||||
g->m->mcache->local_cachealloc = 0;
|
g->m->mcache->local_cachealloc = 0;
|
||||||
|
mstats.tinyallocs += g->m->mcache->local_tinyallocs;
|
||||||
|
g->m->mcache->local_tinyallocs = 0;
|
||||||
if(acct) {
|
if(acct) {
|
||||||
mstats.heap_alloc -= s->npages<<PageShift;
|
mstats.heap_alloc -= s->npages<<PageShift;
|
||||||
mstats.heap_objects--;
|
mstats.heap_objects--;
|
||||||
|
|
|
||||||
29
src/testing/allocs_test.go
Normal file
29
src/testing/allocs_test.go
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package testing_test
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var global interface{}
|
||||||
|
|
||||||
|
var allocsPerRunTests = []struct {
|
||||||
|
name string
|
||||||
|
fn func()
|
||||||
|
allocs float64
|
||||||
|
}{
|
||||||
|
{"alloc *byte", func() { global = new(*byte) }, 1},
|
||||||
|
{"alloc complex128", func() { global = new(complex128) }, 1},
|
||||||
|
{"alloc float64", func() { global = new(float64) }, 1},
|
||||||
|
{"alloc int32", func() { global = new(int32) }, 1},
|
||||||
|
{"alloc byte", func() { global = new(byte) }, 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllocsPerRun(t *testing.T) {
|
||||||
|
for _, tt := range allocsPerRunTests {
|
||||||
|
if allocs := testing.AllocsPerRun(100, tt.fn); allocs != tt.allocs {
|
||||||
|
t.Errorf("AllocsPerRun(100, %s) = %v, want %v", tt.name, allocs, tt.allocs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue