Skip to content

Commit

Permalink
Merge pull request #18 from ortuman/ortuman/rename-slab-arena-2-monot…
Browse files Browse the repository at this point in the history
…onic-arena

renamed slab arena to monotonic arena
  • Loading branch information
ortuman authored Mar 6, 2024
2 parents bc1ca4f + 302a625 commit f56c9fa
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 70 deletions.
42 changes: 21 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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.
Expand All @@ -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)
Expand Down Expand Up @@ -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)

Expand All @@ -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
Expand Down
40 changes: 20 additions & 20 deletions slab_arena.go → monotonic_arena.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
Expand All @@ -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
Expand All @@ -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
}
Expand All @@ -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)
}
}
58 changes: 29 additions & 29 deletions slab_arena_test.go → monotonic_arena_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,40 +12,42 @@ 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++ {
refs = append(refs, New[int](arena))
}

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)
})

Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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()
Expand All @@ -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()
Expand Down Expand Up @@ -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()
Expand All @@ -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()
Expand Down

0 comments on commit f56c9fa

Please sign in to comment.