From 92e340701434b7d63e32f081b69f139237caa10e Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 19 Dec 2016 09:46:08 +1100 Subject: [PATCH 1/4] Add benchmarks for OutMessage.Reset. --- internal/buffer/out_message_test.go | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 internal/buffer/out_message_test.go diff --git a/internal/buffer/out_message_test.go b/internal/buffer/out_message_test.go new file mode 100644 index 0000000..bf08110 --- /dev/null +++ b/internal/buffer/out_message_test.go @@ -0,0 +1,36 @@ +package buffer + +import ( + "fmt" + "testing" + "unsafe" +) + +func BenchmarkOutMessageReset(b *testing.B) { + // A single buffer, which should fit in some level of CPU cache. + b.Run("Single buffer", func(b *testing.B) { + b.SetBytes(int64(unsafe.Sizeof(OutMessage{}))) + + var om OutMessage + for i := 0; i < b.N; i++ { + om.Reset() + } + }) + + // Many megabytes worth of buffers, which should defeat the CPU cache. + b.Run("Many buffers", func(b *testing.B) { + b.SetBytes(int64(unsafe.Sizeof(OutMessage{}))) + + // The number of messages; intentionally a power of two. + const numMessages = 128 + + var oms [numMessages]OutMessage + if s := unsafe.Sizeof(oms); s < 128<<20 { + panic(fmt.Sprintf("Array is too small; total size: %d", s)) + } + + for i := 0; i < b.N; i++ { + oms[i%numMessages].Reset() + } + }) +} From d31e0a4eae18ed6d45aab60cfaedeceddc1618eb Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 19 Dec 2016 10:19:29 +1100 Subject: [PATCH 2/4] Add a test for memclr. --- internal/buffer/out_message_test.go | 51 +++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/internal/buffer/out_message_test.go b/internal/buffer/out_message_test.go index bf08110..77b7de9 100644 --- a/internal/buffer/out_message_test.go +++ b/internal/buffer/out_message_test.go @@ -1,11 +1,62 @@ package buffer import ( + "crypto/rand" "fmt" + "io" "testing" "unsafe" ) +func randBytes(n int) (b []byte, err error) { + b = make([]byte, n) + _, err = io.ReadFull(rand.Reader, b) + return +} + +func TestMemclr(t *testing.T) { + // All sizes up to 32 bytes. + var sizes []int + for i := 0; i <= 32; i++ { + sizes = append(sizes, i) + } + + // And a few hand-chosen sizes. + sizes = append(sizes, []int{ + 39, 41, 64, 127, 128, 129, + 1<<20 - 1, + 1 << 20, + 1<<20 + 1, + }...) + + // For each size, fill a buffer with random bytes and then zero it. + for _, size := range sizes { + size := size + t.Run(fmt.Sprintf("size=%d", size), func(t *testing.T) { + // Generate + b, err := randBytes(size) + if err != nil { + t.Fatalf("randBytes: %v", err) + } + + // Clear + var p unsafe.Pointer + if len(b) != 0 { + p = unsafe.Pointer(&b[0]) + } + + memclr(p, uintptr(len(b))) + + // Check + for i, x := range b { + if x != 0 { + t.Fatalf("non-zero byte %d at offset %d", x, i) + } + } + }) + } +} + func BenchmarkOutMessageReset(b *testing.B) { // A single buffer, which should fit in some level of CPU cache. b.Run("Single buffer", func(b *testing.B) { From b87ffb528b7c06457024488d664dd851fb904e70 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 19 Dec 2016 10:26:19 +1100 Subject: [PATCH 3/4] Fix a bug in BenchmarkOutMessageReset. I misunderstood what was being zeroed. Only the header is. --- internal/buffer/out_message_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/buffer/out_message_test.go b/internal/buffer/out_message_test.go index 77b7de9..af7f901 100644 --- a/internal/buffer/out_message_test.go +++ b/internal/buffer/out_message_test.go @@ -60,18 +60,16 @@ func TestMemclr(t *testing.T) { func BenchmarkOutMessageReset(b *testing.B) { // A single buffer, which should fit in some level of CPU cache. b.Run("Single buffer", func(b *testing.B) { - b.SetBytes(int64(unsafe.Sizeof(OutMessage{}))) - var om OutMessage for i := 0; i < b.N; i++ { om.Reset() } + + b.SetBytes(int64(om.offset)) }) // Many megabytes worth of buffers, which should defeat the CPU cache. b.Run("Many buffers", func(b *testing.B) { - b.SetBytes(int64(unsafe.Sizeof(OutMessage{}))) - // The number of messages; intentionally a power of two. const numMessages = 128 @@ -83,5 +81,7 @@ func BenchmarkOutMessageReset(b *testing.B) { for i := 0; i < b.N; i++ { oms[i%numMessages].Reset() } + + b.SetBytes(int64(oms[0].offset)) }) } From 72fc9c96caa4b66010e61316aea5c235babd3ce5 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Mon, 19 Dec 2016 10:35:20 +1100 Subject: [PATCH 4/4] Add a benchmark for growing and shrinking. This should better stress memclr. --- internal/buffer/out_message_test.go | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/internal/buffer/out_message_test.go b/internal/buffer/out_message_test.go index af7f901..80d9485 100644 --- a/internal/buffer/out_message_test.go +++ b/internal/buffer/out_message_test.go @@ -85,3 +85,34 @@ func BenchmarkOutMessageReset(b *testing.B) { b.SetBytes(int64(oms[0].offset)) }) } + +func BenchmarkOutMessageGrowShrink(b *testing.B) { + // A single buffer, which should fit in some level of CPU cache. + b.Run("Single buffer", func(b *testing.B) { + var om OutMessage + for i := 0; i < b.N; i++ { + om.Grow(MaxReadSize) + om.ShrinkTo(OutMessageInitialSize) + } + + b.SetBytes(int64(MaxReadSize)) + }) + + // Many megabytes worth of buffers, which should defeat the CPU cache. + b.Run("Many buffers", func(b *testing.B) { + // The number of messages; intentionally a power of two. + const numMessages = 128 + + var oms [numMessages]OutMessage + if s := unsafe.Sizeof(oms); s < 128<<20 { + panic(fmt.Sprintf("Array is too small; total size: %d", s)) + } + + for i := 0; i < b.N; i++ { + oms[i%numMessages].Grow(MaxReadSize) + oms[i%numMessages].ShrinkTo(OutMessageInitialSize) + } + + b.SetBytes(int64(MaxReadSize)) + }) +}