diff --git a/internal/buffer/out_message_test.go b/internal/buffer/out_message_test.go new file mode 100644 index 0000000..80d9485 --- /dev/null +++ b/internal/buffer/out_message_test.go @@ -0,0 +1,118 @@ +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) { + 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) { + // 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() + } + + 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)) + }) +}