From 19feb6505c5d42932163224f16e2675a81af5ad6 Mon Sep 17 00:00:00 2001 From: M1xA Date: Sat, 9 Mar 2024 13:27:55 +0200 Subject: [PATCH 1/5] nuke/monotoniic: reset's behavior and a little bit of performance improvements --- monotonic_arena.go | 16 ++++++++-------- monotonic_arena_test.go | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/monotonic_arena.go b/monotonic_arena.go index decf552..9125f41 100644 --- a/monotonic_arena.go +++ b/monotonic_arena.go @@ -26,8 +26,8 @@ func (s *monotonicBuffer) alloc(size, alignment uintptr) (unsafe.Pointer, bool) s.ptr = unsafe.Pointer(unsafe.SliceData(buf)) } alignOffset := uintptr(0) - for alignedPtr := uintptr(s.ptr) + s.offset; alignedPtr%alignment != 0; alignedPtr++ { - alignOffset++ + if delta := (uintptr(s.ptr) + s.offset) % alignment; delta != 0 { + alignOffset = delta } allocSize := size + alignOffset @@ -46,10 +46,10 @@ func (s *monotonicBuffer) reset(release bool) { } s.offset = 0 + s.zeroOutBuffer() + if release { s.ptr = nil - } else { - s.zeroOutBuffer() } } @@ -71,7 +71,7 @@ func (s *monotonicBuffer) availableBytes() uintptr { // NewMonotonicArena creates a new monotonic arena with a specified number of buffers and a buffer size. func NewMonotonicArena(bufferSize, bufferCount int) Arena { - a := &monotonicArena{} + a := &monotonicArena{buffers: make([]*monotonicBuffer, 0, bufferCount)} for i := 0; i < bufferCount; i++ { a.buffers = append(a.buffers, newMonotonicBuffer(bufferSize)) } @@ -80,7 +80,7 @@ func NewMonotonicArena(bufferSize, bufferCount int) Arena { // Alloc satisfies the Arena interface. func (a *monotonicArena) Alloc(size, alignment uintptr) unsafe.Pointer { - for i := 0; i < len(a.buffers); i++ { + for i := range a.buffers { ptr, ok := a.buffers[i].alloc(size, alignment) if ok { return ptr @@ -91,7 +91,7 @@ func (a *monotonicArena) Alloc(size, alignment uintptr) unsafe.Pointer { // Reset satisfies the Arena interface. func (a *monotonicArena) Reset(release bool) { - for _, s := range a.buffers { - s.reset(release) + for i := range a.buffers { + a.buffers[i].reset(release) } } diff --git a/monotonic_arena_test.go b/monotonic_arena_test.go index ca68820..410d338 100644 --- a/monotonic_arena_test.go +++ b/monotonic_arena_test.go @@ -111,7 +111,7 @@ func isMonotonicArenaPtr(a Arena, ptr unsafe.Pointer) bool { func BenchmarkRuntimeNewObject(b *testing.B) { a := newRuntimeAllocator[int]() - for _, objectCount := range []int{100, 1_000, 10_000, 100_000} { + for _, objectCount := range []int{100, 1_000, 10_000, 100_000, 1_000_000} { b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -127,7 +127,7 @@ func BenchmarkMonotonicArenaNewObject(b *testing.B) { monotonicArena := NewMonotonicArena(2*1024*1024, 32) // 2Mb buffer size (64Mb max size) a := newArenaAllocator[int](monotonicArena) - for _, objectCount := range []int{100, 1_000, 10_000, 100_000} { + for _, objectCount := range []int{100, 1_000, 10_000, 100_000, 1_000_000} { b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -144,7 +144,7 @@ func BenchmarkConcurrentMonotonicArenaNewObject(b *testing.B) { monotonicArena := NewMonotonicArena(2*1024*1024, 32) // 2Mb buffer size (64Mb max size) a := newArenaAllocator[int](NewConcurrentArena(monotonicArena)) - for _, objectCount := range []int{100, 1_000, 10_000, 100_000} { + for _, objectCount := range []int{100, 1_000, 10_000, 100_000, 1_000_000} { b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -159,7 +159,7 @@ func BenchmarkConcurrentMonotonicArenaNewObject(b *testing.B) { func BenchmarkRuntimeMakeSlice(b *testing.B) { a := newRuntimeAllocator[int]() - for _, objectCount := range []int{100, 1_000, 10_000, 100_000} { + for _, objectCount := range []int{100, 1_000, 10_000, 100_000, 1_000_000} { b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -175,7 +175,7 @@ func BenchmarkMonotonicArenaMakeSlice(b *testing.B) { monotonicArena := NewMonotonicArena(2*1024*1024, 32) // 2Mb buffer size (64Mb max size) a := newArenaAllocator[int](monotonicArena) - for _, objectCount := range []int{100, 1_000, 10_000, 100_000} { + for _, objectCount := range []int{100, 1_000, 10_000, 100_000, 1_000_000} { b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -192,7 +192,7 @@ func BenchmarkConcurrentMonotonicArenaMakeSlice(b *testing.B) { monotonicArena := NewMonotonicArena(2*1024*1024, 32) // 2Mb buffer size (64Mb max size) a := newArenaAllocator[int](NewConcurrentArena(monotonicArena)) - for _, objectCount := range []int{100, 1_000, 10_000, 100_000} { + for _, objectCount := range []int{100, 1_000, 10_000, 100_000, 1_000_000} { b.Run(fmt.Sprintf("%d", objectCount), func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { From 18c9c83026349255a5a8542840de9c9233f385ab Mon Sep 17 00:00:00 2001 From: M1xA Date: Sun, 10 Mar 2024 18:01:50 +0200 Subject: [PATCH 2/5] nuke/monotonic: fix for alignment --- monotonic_arena.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monotonic_arena.go b/monotonic_arena.go index 9125f41..ea8f074 100644 --- a/monotonic_arena.go +++ b/monotonic_arena.go @@ -26,8 +26,8 @@ func (s *monotonicBuffer) alloc(size, alignment uintptr) (unsafe.Pointer, bool) s.ptr = unsafe.Pointer(unsafe.SliceData(buf)) } alignOffset := uintptr(0) - if delta := (uintptr(s.ptr) + s.offset) % alignment; delta != 0 { - alignOffset = delta + if delta := (uintptr(s.ptr) + s.offset) % alignment; delta > 0 { + alignOffset = alignment - delta } allocSize := size + alignOffset From 7c8787bf20c615d2fd0f4185035a3fd5a0d4e063 Mon Sep 17 00:00:00 2001 From: M1xA Date: Sat, 16 Mar 2024 19:21:07 +0200 Subject: [PATCH 3/5] use built-in 'clear' function to zero out a buffer --- monotonic_arena.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/monotonic_arena.go b/monotonic_arena.go index ea8f074..ee81a14 100644 --- a/monotonic_arena.go +++ b/monotonic_arena.go @@ -54,15 +54,7 @@ func (s *monotonicBuffer) reset(release bool) { } func (s *monotonicBuffer) zeroOutBuffer() { - b := unsafe.Slice((*byte)(s.ptr), s.size) - - // This piece of code will be translated into a runtime.memclrNoHeapPointers - // invocation by the compiler, which is an assembler optimized implementation. - // Architecture specific code can be found at src/runtime/memclr_$GOARCH.s - // in Go source (since https://codereview.appspot.com/137880043). - for i := range b { - b[i] = 0 - } + clear(unsafe.Slice((*byte)(s.ptr), s.size)) } func (s *monotonicBuffer) availableBytes() uintptr { From 9fe0883b07a56b7a3573f9ba2f509e659c3dca38 Mon Sep 17 00:00:00 2001 From: M1xA Date: Sat, 16 Mar 2024 19:22:06 +0200 Subject: [PATCH 4/5] update Go and dependencies --- go.mod | 4 ++-- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 11d2ccd..81b866e 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/ortuman/nuke -go 1.21.7 +go 1.21.8 -require github.com/stretchr/testify v1.8.4 +require github.com/stretchr/testify v1.9.0 require ( github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index fa4b6e6..60ce688 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= From bfca201aaa52e3acb8202e605b314508a84eaf12 Mon Sep 17 00:00:00 2001 From: M1xA Date: Sat, 16 Mar 2024 19:49:52 +0200 Subject: [PATCH 5/5] use built-in 'clear' function to zero out a buffer --- monotonic_arena.go | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/monotonic_arena.go b/monotonic_arena.go index 247a765..f708a63 100644 --- a/monotonic_arena.go +++ b/monotonic_arena.go @@ -37,15 +37,7 @@ func (s *monotonicBuffer) alloc(size, alignment uintptr) (unsafe.Pointer, bool) ptr := unsafe.Pointer(uintptr(s.ptr) + s.offset + alignOffset) s.offset += allocSize - // This piece of code will be translated into a runtime.memclrNoHeapPointers - // invocation by the compiler, which is an assembler optimized implementation. - // Architecture specific code can be found at src/runtime/memclr_$GOARCH.s - // in Go source (since https://codereview.appspot.com/137880043). - b := unsafe.Slice((*byte)(ptr), size) - - for i := range b { - b[i] = 0 - } + clear(unsafe.Slice((*byte)(ptr), size)) return ptr, true }