mirror of
https://github.com/zap-proto/http.git
synced 2026-10-04 03:07:19 +00:00
perf: zero-alloc frame read via bufio Peek/Discard
readFrameInto passed a stack [4]byte through io.ReadFull's io.Reader interface, forcing the length-prefix array to the heap every call (the last 2 allocs/req: server + client). Peek the prefix out of the concrete *bufio.Reader's buffer instead — zero-copy, no escape. Full round trip is now 0 allocs/req at default GOGC; ZAP beats fasthttp on tiny/small/chat loopback.
This commit is contained in:
+12
-3
@@ -39,11 +39,18 @@ func readFrame(r *bufio.Reader) ([]byte, error) {
|
||||
// are consumed synchronously by Unmarshal{Request,Response} (which copy into the
|
||||
// fasthttp store), so the buffer is free to reuse on the next call.
|
||||
func readFrameInto(r *bufio.Reader, buf []byte) ([]byte, error) {
|
||||
var hdr [4]byte
|
||||
if _, err := io.ReadFull(r, hdr[:]); err != nil {
|
||||
// Peek the length prefix out of the bufio buffer directly rather than
|
||||
// io.ReadFull into a local [4]byte: passing a stack array through the
|
||||
// io.Reader interface forces it to the heap on every call. Peek returns a
|
||||
// view into the reader's own buffer — no copy, no escape.
|
||||
hdr, err := r.Peek(4)
|
||||
if err != nil {
|
||||
return buf, err
|
||||
}
|
||||
n := binary.BigEndian.Uint32(hdr)
|
||||
if _, err := r.Discard(4); err != nil {
|
||||
return buf, err
|
||||
}
|
||||
n := binary.BigEndian.Uint32(hdr[:])
|
||||
if n == 0 {
|
||||
return buf, fmt.Errorf("zaphttp: zero-length frame")
|
||||
}
|
||||
@@ -55,6 +62,8 @@ func readFrameInto(r *bufio.Reader, buf []byte) ([]byte, error) {
|
||||
} else {
|
||||
buf = buf[:n]
|
||||
}
|
||||
// buf is already heap-backed (persistent across calls), so ReadFull's
|
||||
// interface indirection costs no new allocation here.
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return buf, fmt.Errorf("zaphttp: short frame: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user