From 9b26d44a629fde26a4fb1b6697d05979934ccff6 Mon Sep 17 00:00:00 2001 From: Ignacio Van Droogenbroeck Date: Mon, 2 Mar 2026 19:12:38 -0600 Subject: [PATCH] perf(decode): pool rec buffer in unmarshalValue Replaces per-call make([]byte, 0, 64) with a sync.Pool of reusable recording buffers in unmarshalValue. Eliminates 1 alloc per Unmarshaler.UnmarshalMsgpack call. Oversized buffers (>4KB) are dropped to prevent unbounded pool growth. DecodeRaw is not pooled because the caller owns the returned slice. --- decode_value.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/decode_value.go b/decode_value.go index 47c81fd..5b89803 100644 --- a/decode_value.go +++ b/decode_value.go @@ -5,10 +5,18 @@ import ( "errors" "fmt" "reflect" + "sync" "github.com/vmihailenco/msgpack/v5/msgpcode" ) +var recBufPool = sync.Pool{ + New: func() interface{} { + b := make([]byte, 0, 64) + return &b + }, +} + var ( interfaceType = reflect.TypeOf((*interface{})(nil)).Elem() stringType = reflect.TypeOf((*string)(nil)).Elem() @@ -233,17 +241,29 @@ func decodeCustomValue(d *Decoder, v reflect.Value) error { } func unmarshalValue(d *Decoder, v reflect.Value) error { - var b []byte + bp := recBufPool.Get().(*[]byte) + *bp = (*bp)[:0] - d.rec = make([]byte, 0, 64) + d.rec = *bp if err := d.Skip(); err != nil { + d.rec = nil + *bp = (*bp)[:0] + recBufPool.Put(bp) return err } - b = d.rec + b := d.rec d.rec = nil unmarshaler := v.Interface().(Unmarshaler) - return unmarshaler.UnmarshalMsgpack(b) + err := unmarshaler.UnmarshalMsgpack(b) + + // Return buffer to pool; drop oversized buffers. + if cap(b) <= 4096 { + *bp = b + recBufPool.Put(bp) + } + + return err } // unmarshalBinaryOrTextValue peeks at the wire format to choose between