Skip to content

Commit

Permalink
Merge pull request #8 from ortuman/ortuman/slab-arena-benchmarks
Browse files Browse the repository at this point in the history
added arena benchmarks
  • Loading branch information
ortuman authored Mar 3, 2024
2 parents 88e64de + 75af9dd commit 5145af5
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 1 deletion.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,35 @@ func main() {

By default, the arena implementation is not thread-safe, meaning it is not safe to access it concurrently from different goroutines. If the specific use case requires concurrent access, the library provides the `NewConcurrentArena` function, to which a base arena is passed and it returns a new instance that can be accessed concurrently.

## Benchmarks

```
BenchmarkRuntimeNewObject/100-8 1394955 846.6 ns/op 800 B/op 100 allocs/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
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
```

## Contributing

We welcome contributions from the community! If you'd like to contribute, please fork the repository, make your changes, and submit a pull request.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/ortuman/memnuke
module github.com/ortuman/nuke

go 1.21.7

Expand Down
122 changes: 122 additions & 0 deletions slab_arena_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package nuke

import (
"fmt"
"runtime"
"testing"
"time"
Expand Down Expand Up @@ -97,3 +98,124 @@ func isSlabArenaPtr(a Arena, ptr unsafe.Pointer) bool {
}
return false
}

func BenchmarkRuntimeNewObject(b *testing.B) {
a := newRuntimeAllocator[int]()
for _, objectCount := range []int{100, 1_000, 10_000, 100_000} {
b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for j := 0; j < objectCount; j++ {
_ = a.new()
}
}
})
}
}

func BenchmarkSlabArenaNewObject(b *testing.B) {
slabArena := NewSlabArena(1024*1024, 128) // 1Mb slab size (128 MB)

a := newArenaAllocator[int](slabArena)
for _, objectCount := range []int{100, 1_000, 10_000, 100_000} {
b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for j := 0; j < objectCount; j++ {
_ = a.new()
}
a.(*arenaAllocator[int]).a.Reset(false)
}
})
}
}

func BenchmarkConcurrentSlabArenaNewObject(b *testing.B) {
slabArena := NewSlabArena(1024*1024, 128) // 1Mb slab size (128 MB)

a := newArenaAllocator[int](NewConcurrentArena(slabArena))
for _, objectCount := range []int{100, 1_000, 10_000, 100_000} {
b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for j := 0; j < objectCount; j++ {
_ = a.new()
}
a.(*arenaAllocator[int]).a.Reset(false)
}
})
}
}

func BenchmarkRuntimeMakeSlice(b *testing.B) {
a := newRuntimeAllocator[int]()
for _, objectCount := range []int{100, 1_000, 10_000, 100_000} {
b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for j := 0; j < objectCount; j++ {
_ = a.makeSlice(0, 256)
}
}
})
}
}

func BenchmarkSlabArenaMakeSlice(b *testing.B) {
slabArena := NewSlabArena(1024*1024, 128) // 1Mb slab size (128 MB)

a := newArenaAllocator[int](slabArena)
for _, objectCount := range []int{100, 1_000, 10_000, 100_000} {
b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for j := 0; j < objectCount; j++ {
_ = a.makeSlice(0, 256)
}
a.(*arenaAllocator[int]).a.Reset(false)
}
})
}
}

func BenchmarkConcurrentSlabArenaMakeSlice(b *testing.B) {
slabArena := NewSlabArena(1024*1024, 128) // 1Mb slab size (128 MB)

a := newArenaAllocator[int](NewConcurrentArena(slabArena))
for _, objectCount := range []int{100, 1_000, 10_000, 100_000} {
b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for j := 0; j < objectCount; j++ {
_ = a.makeSlice(0, 256)
}
a.(*arenaAllocator[int]).a.Reset(false)
}
})
}
}

type allocator[T any] interface {
new() *T
makeSlice(len, cap int) []T
}

type runtimeAllocator[T any] struct{}

func newRuntimeAllocator[T any]() allocator[T] {
return &runtimeAllocator[T]{}
}

func (r *runtimeAllocator[T]) new() *T { return new(T) }
func (r *runtimeAllocator[T]) makeSlice(len, cap int) []T { return make([]T, len, cap) }

type arenaAllocator[T any] struct {
a Arena
}

func newArenaAllocator[T any](a Arena) allocator[T] {
return &arenaAllocator[T]{a: a}
}

func (r *arenaAllocator[T]) new() *T { return New[T](r.a) }
func (r *arenaAllocator[T]) makeSlice(len, cap int) []T { return MakeSlice[T](r.a, len, cap) }

0 comments on commit 5145af5

Please sign in to comment.