Skip to content

Instantly share code, notes, and snippets.

@zeebo
Created July 13, 2021 14:09
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeebo/4c9e28ac277c74ae450ad1bff8068f93 to your computer and use it in GitHub Desktop.
Save zeebo/4c9e28ac277c74ae450ad1bff8068f93 to your computer and use it in GitHub Desktop.
Buffer Benchmark
package buffer
import (
"errors"
"os"
"unsafe"
)
const (
entrySize = 32 // or maybe 28?
bufferSize = 16384
numEntries = bufferSize / entrySize
)
type Entry [entrySize]byte
type Buffer struct {
fh *os.File
flushes int
n uint
buf [numEntries]Entry
}
func (b *Buffer) Append(ent Entry) error {
if b.n < numEntries-1 {
b.buf[b.n] = ent
b.n++
return nil
}
return b.appendSlow(ent)
}
func (b *Buffer) appendSlow(ent Entry) error {
if b.n >= numEntries {
return errors.New("invalid call to AppendSlow")
}
b.buf[b.n] = ent
b.n++
return b.Flush()
}
func (b *Buffer) Flush() error {
if b.n == 0 {
return nil
}
// we do some unsafe stuff to access the buf as a flat byte array
buf := (*[bufferSize]byte)(unsafe.Pointer(&b.buf[0]))
_, err := b.fh.Write(buf[:b.n*entrySize])
if err != nil {
return err
}
b.n = 0
b.flushes++
return nil
}
package buffer
import (
"io"
"os"
"path/filepath"
"testing"
"time"
)
func BenchmarkBuffer(b *testing.B) {
fh := tempFile(b)
defer fh.Close()
buf := &Buffer{fh: fh}
now := time.Now()
ent := Entry{}
b.ResetTimer()
for i := 0; i < b.N; i++ {
fh.Seek(0, io.SeekStart)
for i := 0; i < 1e5; i++ {
_ = buf.Append(ent)
}
_ = buf.Flush()
}
b.ReportMetric(float64(time.Since(now).Nanoseconds())/float64(b.N)/1e5, "ns/key")
b.ReportMetric(float64(buf.flushes)/float64(b.N), "flushes")
}
func tempFile(b *testing.B) *os.File {
dir := b.TempDir()
fh, err := os.Create(filepath.Join(dir, "data"))
if err != nil {
b.Fatal(err)
}
return fh
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment