Skip to content

Commit dd58b84

Browse files
committed
cache: use malloc instead of calloc
Many profiles have a percentage or two in `manual.Alloc`. Switch to using `malloc` instead of `calloc`, which should speed up the allocations. We clear only the part that we cast to `*cache.Value`. We already clear the part that we use for block metadata (via `block.CastMetadataZero`).
1 parent 67288c5 commit dd58b84

File tree

3 files changed

+54
-6
lines changed

3 files changed

+54
-6
lines changed

internal/cache/value.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func Alloc(n int) *Value {
5959

6060
if valueEntryCanBeGoAllocated && valueEntryGoAllocated {
6161
// Note: if cgo is not enabled, manual.New will do a regular Go allocation.
62-
b := manual.New(manual.BlockCacheData, uintptr(n))
62+
b := manual.NewUninitialized(manual.BlockCacheData, uintptr(n))
6363
v := &Value{buf: b.Slice()}
6464
v.ref.init(1)
6565
// Note: this is a no-op if invariants and tracing are disabled or race is
@@ -77,7 +77,10 @@ func Alloc(n int) *Value {
7777
// When we're not performing leak detection, the lifetime of the returned
7878
// Value is exactly the lifetime of the backing buffer and we can manually
7979
// allocate both.
80-
b := manual.New(manual.BlockCacheData, ValueMetadataSize+uintptr(n))
80+
b := manual.NewUninitialized(manual.BlockCacheData, ValueMetadataSize+uintptr(n))
81+
// We must clear the slice because Value contains pointers. See
82+
// manual.NewUninitialized.
83+
clear(b.Slice()[:ValueMetadataSize])
8184
v := (*Value)(b.Data())
8285
v.buf = b.Slice()[ValueMetadataSize:]
8386
v.ref.init(1)

internal/manual/manual_cgo.go

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ var useGoAllocation = invariants.RaceEnabled && rand.Uint32()%2 == 0
3939
// runtime page allocator and allocate large chunks of memory using mmap or
4040
// similar.
4141

42-
// New allocates a slice of size n. The returned slice is from manually
43-
// managed memory and MUST be released by calling Free. Failure to do so will
44-
// result in a memory leak.
42+
// New allocates a slice of size n. The returned slice is from manually managed
43+
// memory and MUST be released by calling Free. Failure to do so will result in
44+
// a memory leak.
4545
func New(purpose Purpose, n uintptr) Buf {
4646
if n == 0 {
4747
return Buf{}
@@ -67,6 +67,8 @@ func New(purpose Purpose, n uintptr) Buf {
6767
// passing uninitialized C memory to Go code if the Go code is going to
6868
// store pointer values in it. Zero out the memory in C before passing it
6969
// to Go.
70+
//
71+
// See https://github.com/golang/go/issues/19928
7072
ptr := C.calloc(C.size_t(n), 1)
7173
if ptr == nil {
7274
// NB: throw is like panic, except it guarantees the process will be
@@ -77,6 +79,36 @@ func New(purpose Purpose, n uintptr) Buf {
7779
return Buf{data: ptr, n: n}
7880
}
7981

82+
// NewUninitialized allocates a slice of size n without zeroing it out. The
83+
// returned slice is from manually managed memory and MUST be released by
84+
// calling Free. Failure to do so will result in a memory leak (see
85+
// https://github.com/golang/go/issues/19928).
86+
//
87+
// If the caller does an unsafe cast from the slice to any type containing
88+
// pointers, the relevant part of the slice *must* be zeroed out.
89+
func NewUninitialized(purpose Purpose, n uintptr) Buf {
90+
if n == 0 {
91+
return Buf{}
92+
}
93+
recordAlloc(purpose, n)
94+
95+
// In race-enabled builds, we sometimes make allocations using Go to allow
96+
// the race detector to observe concurrent memory access to memory allocated
97+
// by this package. See the definition of useGoAllocation for more details.
98+
if invariants.RaceEnabled && useGoAllocation {
99+
b := make([]byte, n)
100+
return Buf{data: unsafe.Pointer(&b[0]), n: n}
101+
}
102+
ptr := C.malloc(C.size_t(n))
103+
if ptr == nil {
104+
// NB: throw is like panic, except it guarantees the process will be
105+
// terminated. The call below is exactly what the Go runtime invokes when
106+
// it cannot allocate memory.
107+
throw("out of memory")
108+
}
109+
return Buf{data: ptr, n: n}
110+
}
111+
80112
// Free frees the specified slice. It has to be exactly the slice that was
81113
// returned by New.
82114
func Free(purpose Purpose, b Buf) {

internal/manual/manual_nocgo.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ import (
1515
// Provides versions of New and Free when cgo is not available (e.g. cross
1616
// compilation).
1717

18-
// New allocates a slice of size n.
18+
// New allocates a slice of size n. The returned slice is from manually managed
19+
// memory and MUST be released by calling Free. Failure to do so will result in
20+
// a memory leak.
1921
func New(purpose Purpose, n uintptr) Buf {
2022
if n == 0 {
2123
return Buf{}
@@ -28,6 +30,17 @@ func New(purpose Purpose, n uintptr) Buf {
2830
}
2931
}
3032

33+
// NewUninitialized allocates a slice of size n without zeroing it out. The
34+
// returned slice is from manually managed memory and MUST be released by
35+
// calling Free. Failure to do so will result in a memory leak (see
36+
// https://github.com/golang/go/issues/19928).
37+
//
38+
// If the caller does an unsafe cast from the slice to any type containing
39+
// pointers, the relevant part of the slice *must* be zeroed out.
40+
func NewUninitialized(purpose Purpose, n uintptr) Buf {
41+
return New(purpose, n)
42+
}
43+
3144
// Free frees the specified slice. It has to be exactly the slice that was
3245
// returned by New.
3346
func Free(purpose Purpose, b Buf) {

0 commit comments

Comments
 (0)