From 302a62578ac9206e779fb69bd421b9bad31b805f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel=20Ortu=C3=B1o?= Date: Wed, 6 Mar 2024 16:25:35 +0100 Subject: [PATCH] renamed slab arena to monotonic arena MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miguel Ángel Ortuño --- README.md | 42 +++++++------- slab_arena.go => monotonic_arena.go | 40 ++++++------- slab_arena_test.go => monotonic_arena_test.go | 58 +++++++++---------- 3 files changed, 70 insertions(+), 70 deletions(-) rename slab_arena.go => monotonic_arena.go (52%) rename slab_arena_test.go => monotonic_arena_test.go (69%) diff --git a/README.md b/README.md index d356086..428ccc9 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,9 @@ import ( type Foo struct { A int } func main() { - // Initialize a new memory arena with a slab size of 256KB + // Initialize a new monotonic arena with a buffer size of 256KB // and a max memory size of 20MB. - arena := nuke.NewSlabArena(256*1024, 20*1024*1024) + arena := nuke.NewMonotonicArena(256*1024, 80) // Allocate a new object of type Foo. fooRef := nuke.New[Foo](arena) @@ -63,7 +63,7 @@ func main() { // ... - // When done, reset the arena (releasing slab buffer memory). + // When done, reset the arena (releasing monotonic buffer memory). arena.Reset(true) // From here on, any arena reference is invalid. @@ -76,7 +76,7 @@ Additionally, we can inject a memory arena as part of a context, with the purpos ```go func httpHandler(w http.ResponseWriter, r *http.Request) { // Inject memory arena into request context. - arena := nuke.NewSlabArena(64*1024, 1024*1024) + arena := nuke.NewMonotonicArena(64*1024, 10) defer arena.Reset(true) ctx := nuke.InjectContextArena(r.Context(), arena) @@ -112,7 +112,7 @@ import ( func main() { arena := nuke.NewConcurrentArena( - nuke.NewSlabArena(256*1024, 20*1024*1024), + nuke.NewMonotonicArena(256*1024, 20), ) defer arena.Reset(true) @@ -130,26 +130,26 @@ BenchmarkRuntimeNewObject/100-8 1394955 846.6 ns/op BenchmarkRuntimeNewObject/1000-8 143031 8357 ns/op 8000 B/op 1000 allocs/op BenchmarkRuntimeNewObject/10000-8 14371 83562 ns/op 80000 B/op 10000 allocs/op BenchmarkRuntimeNewObject/100000-8 1428 835474 ns/op 800005 B/op 100000 allocs/op -BenchmarkSlabArenaNewObject/100-8 124495 15469 ns/op 0 B/op 0 allocs/op -BenchmarkSlabArenaNewObject/1000-8 76744 19602 ns/op 0 B/op 0 allocs/op -BenchmarkSlabArenaNewObject/10000-8 24104 50845 ns/op 0 B/op 0 allocs/op -BenchmarkSlabArenaNewObject/100000-8 3282 366044 ns/op 0 B/op 0 allocs/op -BenchmarkConcurrentSlabArenaNewObject/100-8 90392 16679 ns/op 0 B/op 0 allocs/op -BenchmarkConcurrentSlabArenaNewObject/1000-8 43753 29823 ns/op 0 B/op 0 allocs/op -BenchmarkConcurrentSlabArenaNewObject/10000-8 8037 149923 ns/op 0 B/op 0 allocs/op -BenchmarkConcurrentSlabArenaNewObject/100000-8 879 1364377 ns/op 0 B/op 0 allocs/op +BenchmarkMonotonicArenaNewObject/100-8 124495 15469 ns/op 0 B/op 0 allocs/op +BenchmarkMonotonicArenaNewObject/1000-8 76744 19602 ns/op 0 B/op 0 allocs/op +BenchmarkMonotonicArenaNewObject/10000-8 24104 50845 ns/op 0 B/op 0 allocs/op +BenchmarkMonotonicArenaNewObject/100000-8 3282 366044 ns/op 0 B/op 0 allocs/op +BenchmarkConcurrentMonotonicArenaNewObject/100-8 90392 16679 ns/op 0 B/op 0 allocs/op +BenchmarkConcurrentMonotonicArenaNewObject/1000-8 43753 29823 ns/op 0 B/op 0 allocs/op +BenchmarkConcurrentMonotonicArenaNewObject/10000-8 8037 149923 ns/op 0 B/op 0 allocs/op +BenchmarkConcurrentMonotonicArenaNewObject/100000-8 879 1364377 ns/op 0 B/op 0 allocs/op BenchmarkRuntimeMakeSlice/100-8 58166 19684 ns/op 204800 B/op 100 allocs/op BenchmarkRuntimeMakeSlice/1000-8 5916 196412 ns/op 2048010 B/op 1000 allocs/op BenchmarkRuntimeMakeSlice/10000-8 600 1965622 ns/op 20480106 B/op 10001 allocs/op BenchmarkRuntimeMakeSlice/100000-8 60 19664140 ns/op 204801155 B/op 100012 allocs/op -BenchmarkSlabArenaMakeSlice/100-8 166300 14520 ns/op 0 B/op 0 allocs/op -BenchmarkSlabArenaMakeSlice/1000-8 43785 36938 ns/op 0 B/op 0 allocs/op -BenchmarkSlabArenaMakeSlice/10000-8 2707 427398 ns/op 0 B/op 0 allocs/op -BenchmarkSlabArenaMakeSlice/100000-8 87 14048963 ns/op 70582284 B/op 34464 allocs/op -BenchmarkConcurrentSlabArenaMakeSlice/100-8 91959 17944 ns/op 0 B/op 0 allocs/op -BenchmarkConcurrentSlabArenaMakeSlice/1000-8 27384 42790 ns/op 0 B/op 0 allocs/op -BenchmarkConcurrentSlabArenaMakeSlice/10000-8 2406 480474 ns/op 0 B/op 0 allocs/op -BenchmarkConcurrentSlabArenaMakeSlice/100000-8 84 14702775 ns/op 70582280 B/op 34464 allocs/op +BenchmarkMonotonicArenaMakeSlice/100-8 166300 14520 ns/op 0 B/op 0 allocs/op +BenchmarkMonotonicArenaMakeSlice/1000-8 43785 36938 ns/op 0 B/op 0 allocs/op +BenchmarkMonotonicArenaMakeSlice/10000-8 2707 427398 ns/op 0 B/op 0 allocs/op +BenchmarkMonotonicArenaMakeSlice/100000-8 87 14048963 ns/op 70582284 B/op 34464 allocs/op +BenchmarkConcurrentMonotonicArenaMakeSlice/100-8 91959 17944 ns/op 0 B/op 0 allocs/op +BenchmarkConcurrentMonotonicArenaMakeSlice/1000-8 27384 42790 ns/op 0 B/op 0 allocs/op +BenchmarkConcurrentMonotonicArenaMakeSlice/10000-8 2406 480474 ns/op 0 B/op 0 allocs/op +BenchmarkConcurrentMonotonicArenaMakeSlice/100000-8 84 14702775 ns/op 70582280 B/op 34464 allocs/op ``` ## Contributing diff --git a/slab_arena.go b/monotonic_arena.go similarity index 52% rename from slab_arena.go rename to monotonic_arena.go index fa63aaa..fd50ecf 100644 --- a/slab_arena.go +++ b/monotonic_arena.go @@ -6,23 +6,23 @@ import ( "unsafe" ) -type slabArena struct { - slabs []*slab +type monotonicArena struct { + buffers []*monotonicBuffer } -type slab struct { +type monotonicBuffer struct { ptr unsafe.Pointer offset int size int } -func newSlab(size int) *slab { - return &slab{size: size} +func newMonotonicBuffer(size int) *monotonicBuffer { + return &monotonicBuffer{size: size} } -func (s *slab) alloc(size int) (unsafe.Pointer, bool) { +func (s *monotonicBuffer) alloc(size int) (unsafe.Pointer, bool) { if s.ptr == nil { - buf := make([]byte, s.size) // allocate slab buffer lazily + buf := make([]byte, s.size) // allocate monotonic buffer lazily s.ptr = unsafe.Pointer(unsafe.SliceData(buf)) } if s.availableBytes() < size { @@ -34,7 +34,7 @@ func (s *slab) alloc(size int) (unsafe.Pointer, bool) { return ptr, true } -func (s *slab) reset(release bool) { +func (s *monotonicBuffer) reset(release bool) { if s.offset == 0 { return } @@ -47,7 +47,7 @@ func (s *slab) reset(release bool) { } } -func (s *slab) zeroOutBuffer() { +func (s *monotonicBuffer) zeroOutBuffer() { b := unsafe.Slice((*byte)(s.ptr), s.size) // This piece of code will be translated into a runtime.memclrNoHeapPointers @@ -59,23 +59,23 @@ func (s *slab) zeroOutBuffer() { } } -func (s *slab) availableBytes() int { +func (s *monotonicBuffer) availableBytes() int { return s.size - s.offset } -// NewSlabArena creates a new slab arena with a specified number of slabs and slab size. -func NewSlabArena(slabSize, slabCount int) Arena { - a := &slabArena{} - for i := 0; i < slabCount; i++ { - a.slabs = append(a.slabs, newSlab(slabSize)) +// NewMonotonicArena creates a new monotonic arena with a specified number of buffers and a buffer size. +func NewMonotonicArena(bufferSize, bufferCount int) Arena { + a := &monotonicArena{} + for i := 0; i < bufferCount; i++ { + a.buffers = append(a.buffers, newMonotonicBuffer(bufferSize)) } return a } // Alloc satisfies the Arena interface. -func (a *slabArena) Alloc(size int) unsafe.Pointer { - for i := 0; i < len(a.slabs); i++ { - ptr, ok := a.slabs[i].alloc(size) +func (a *monotonicArena) Alloc(size int) unsafe.Pointer { + for i := 0; i < len(a.buffers); i++ { + ptr, ok := a.buffers[i].alloc(size) if ok { return ptr } @@ -84,8 +84,8 @@ func (a *slabArena) Alloc(size int) unsafe.Pointer { } // Reset satisfies the Arena interface. -func (a *slabArena) Reset(release bool) { - for _, s := range a.slabs { +func (a *monotonicArena) Reset(release bool) { + for _, s := range a.buffers { s.reset(release) } } diff --git a/slab_arena_test.go b/monotonic_arena_test.go similarity index 69% rename from slab_arena_test.go rename to monotonic_arena_test.go index cad6aea..48fb3c5 100644 --- a/slab_arena_test.go +++ b/monotonic_arena_test.go @@ -12,8 +12,8 @@ import ( "github.com/stretchr/testify/require" ) -func TestSlabArenaAllocateObject(t *testing.T) { - arena := NewSlabArena(8182, 1) // 8KB +func TestMonotonicArenaAllocateObject(t *testing.T) { + arena := NewMonotonicArena(8182, 1) // 8KB var refs []*int for i := 0; i < 1_000; i++ { @@ -21,31 +21,33 @@ func TestSlabArenaAllocateObject(t *testing.T) { } for i := 0; i < 1_000; i++ { - require.True(t, isSlabArenaPtr(arena, unsafe.Pointer(refs[i]))) + require.True(t, isMonotonicArenaPtr(arena, unsafe.Pointer(refs[i]))) } } -func TestSlabArenaSendObjectToHeap(t *testing.T) { +func TestMonotonicArenaAllocateSlice(t *testing.T) {} + +func TestMonotonicArenaSendObjectToHeap(t *testing.T) { var x int - arena := NewSlabArena(2*int(unsafe.Sizeof(x)), 1) // 2 ints room + arena := NewMonotonicArena(2*int(unsafe.Sizeof(x)), 1) // 2 ints room // Send the first two ints to the arena - require.True(t, isSlabArenaPtr(arena, unsafe.Pointer(New[int](arena)))) - require.True(t, isSlabArenaPtr(arena, unsafe.Pointer(New[int](arena)))) + require.True(t, isMonotonicArenaPtr(arena, unsafe.Pointer(New[int](arena)))) + require.True(t, isMonotonicArenaPtr(arena, unsafe.Pointer(New[int](arena)))) // Send last one to the heap - require.False(t, isSlabArenaPtr(arena, unsafe.Pointer(New[int](arena)))) + require.False(t, isMonotonicArenaPtr(arena, unsafe.Pointer(New[int](arena)))) } -func TestSlabArenaReset(t *testing.T) { - arena := NewSlabArena(1024, 1).(*slabArena) // one slab of 1KB +func TestMonotonicArenaReset(t *testing.T) { + arena := NewMonotonicArena(1024, 1).(*monotonicArena) // one monotonic buffer of 1KB - // Allocate slab buffer + // Allocate monotonic buffer _ = New[int](arena) // Configure finalizer gced := make(chan bool) - runtime.SetFinalizer((*byte)(arena.slabs[0].ptr), func(*byte) { + runtime.SetFinalizer((*byte)(arena.buffers[0].ptr), func(*byte) { close(gced) }) @@ -81,11 +83,9 @@ func TestSlabArenaReset(t *testing.T) { _ = New[int](arena) } -func TestSlabArenaAllocateSlice(t *testing.T) {} - -func isSlabArenaPtr(a Arena, ptr unsafe.Pointer) bool { - sa := a.(*slabArena) - for _, s := range sa.slabs { +func isMonotonicArenaPtr(a Arena, ptr unsafe.Pointer) bool { + ma := a.(*monotonicArena) + for _, s := range ma.buffers { if s.ptr == nil { break } @@ -113,10 +113,10 @@ func BenchmarkRuntimeNewObject(b *testing.B) { } } -func BenchmarkSlabArenaNewObject(b *testing.B) { - slabArena := NewSlabArena(1024*1024, 128) // 1Mb slab size (128 MB) +func BenchmarkMonotonicArenaNewObject(b *testing.B) { + monotonicArena := NewMonotonicArena(1024*1024, 128) // 1Mb buffer size (128 MB) - a := newArenaAllocator[int](slabArena) + a := newArenaAllocator[int](monotonicArena) for _, objectCount := range []int{100, 1_000, 10_000, 100_000} { b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) { b.ReportAllocs() @@ -130,10 +130,10 @@ func BenchmarkSlabArenaNewObject(b *testing.B) { } } -func BenchmarkConcurrentSlabArenaNewObject(b *testing.B) { - slabArena := NewSlabArena(1024*1024, 128) // 1Mb slab size (128 MB) +func BenchmarkConcurrentMonotonicArenaNewObject(b *testing.B) { + monotonicArena := NewMonotonicArena(1024*1024, 128) // 1Mb buffer size (128 MB) - a := newArenaAllocator[int](NewConcurrentArena(slabArena)) + a := newArenaAllocator[int](NewConcurrentArena(monotonicArena)) for _, objectCount := range []int{100, 1_000, 10_000, 100_000} { b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) { b.ReportAllocs() @@ -161,10 +161,10 @@ func BenchmarkRuntimeMakeSlice(b *testing.B) { } } -func BenchmarkSlabArenaMakeSlice(b *testing.B) { - slabArena := NewSlabArena(1024*1024, 128) // 1Mb slab size (128 MB) +func BenchmarkMonotonicArenaMakeSlice(b *testing.B) { + monotonicArena := NewMonotonicArena(1024*1024, 128) // 1Mb buffer size (128 MB) - a := newArenaAllocator[int](slabArena) + a := newArenaAllocator[int](monotonicArena) for _, objectCount := range []int{100, 1_000, 10_000, 100_000} { b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) { b.ReportAllocs() @@ -178,10 +178,10 @@ func BenchmarkSlabArenaMakeSlice(b *testing.B) { } } -func BenchmarkConcurrentSlabArenaMakeSlice(b *testing.B) { - slabArena := NewSlabArena(1024*1024, 128) // 1Mb slab size (128 MB) +func BenchmarkConcurrentMonotonicArenaMakeSlice(b *testing.B) { + monotonicArena := NewMonotonicArena(1024*1024, 128) // 1Mb buffer size (128 MB) - a := newArenaAllocator[int](NewConcurrentArena(slabArena)) + a := newArenaAllocator[int](NewConcurrentArena(monotonicArena)) for _, objectCount := range []int{100, 1_000, 10_000, 100_000} { b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) { b.ReportAllocs()