Skip to content

Commit 778af0a

Browse files
committed
eliminate extra heap alloc
1 parent ae8c8df commit 778af0a

File tree

3 files changed

+40
-29
lines changed

3 files changed

+40
-29
lines changed

internal/transport/http2_client.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,11 +1178,6 @@ func (t *http2Client) updateFlowControl(n uint32) {
11781178
}
11791179

11801180
func (t *http2Client) handleData(f *parsedDataFrame) {
1181-
defer func() {
1182-
if f.data != nil {
1183-
f.data.Free()
1184-
}
1185-
}()
11861181
size := f.Header().Length
11871182
var sendBDPPing bool
11881183
if t.bdpEst != nil {
@@ -1233,8 +1228,8 @@ func (t *http2Client) handleData(f *parsedDataFrame) {
12331228
}
12341229
}
12351230
if dataLen > 0 {
1231+
f.data.Ref()
12361232
s.write(recvMsg{buffer: f.data})
1237-
f.data = nil
12381233
}
12391234
}
12401235
// The server has closed the stream without sending trailers. Record that
@@ -1701,6 +1696,7 @@ func (t *http2Client) reader(errCh chan<- error) {
17011696
t.operateHeaders(frame)
17021697
case *parsedDataFrame:
17031698
t.handleData(frame)
1699+
frame.data.Free()
17041700
case *http2.RSTStreamFrame:
17051701
t.handleRSTStream(frame)
17061702
case *http2.SettingsFrame:

internal/transport/http2_server.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,7 @@ func (t *http2Server) HandleStreams(ctx context.Context, handle func(*ServerStre
709709
}
710710
case *parsedDataFrame:
711711
t.handleData(frame)
712+
frame.data.Free()
712713
case *http2.RSTStreamFrame:
713714
t.handleRSTStream(frame)
714715
case *http2.SettingsFrame:
@@ -789,11 +790,6 @@ func (t *http2Server) updateFlowControl(n uint32) {
789790
}
790791

791792
func (t *http2Server) handleData(f *parsedDataFrame) {
792-
defer func() {
793-
if f.data != nil {
794-
f.data.Free()
795-
}
796-
}()
797793
size := f.Header().Length
798794
var sendBDPPing bool
799795
if t.bdpEst != nil {
@@ -845,8 +841,8 @@ func (t *http2Server) handleData(f *parsedDataFrame) {
845841
}
846842
}
847843
if dataLen > 0 {
844+
f.data.Ref()
848845
s.write(recvMsg{buffer: f.data})
849-
f.data = nil
850846
}
851847
}
852848
if f.StreamEnded() {

internal/transport/http_util.go

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -453,13 +453,13 @@ func (f *framer) readFrame() (any, error) {
453453
// Read the data frame directly from the underlying io.Reader to avoid
454454
// copies.
455455
if fh.Type == http2.FrameData {
456-
err = f.readDataFrame(fh, f.pool, &f.dataFrame)
456+
err = f.readDataFrame(fh)
457457
return &f.dataFrame, err
458458
}
459459
return f.fr.ReadFrameForHeader(fh)
460460
}
461461

462-
func (f *framer) readDataFrame(fh http2.FrameHeader, pool mem.BufferPool, df *parsedDataFrame) (err error) {
462+
func (f *framer) readDataFrame(fh http2.FrameHeader) (err error) {
463463
if fh.StreamID == 0 {
464464
// DATA frames MUST be associated with a stream. If a
465465
// DATA frame is received whose stream identifier
@@ -468,12 +468,25 @@ func (f *framer) readDataFrame(fh http2.FrameHeader, pool mem.BufferPool, df *pa
468468
// PROTOCOL_ERROR.
469469
return fmt.Errorf("DATA frame with stream ID 0")
470470
}
471-
payload := pool.Get(int(fh.Length))
472-
defer func() {
473-
if err != nil {
474-
f.pool.Put(payload)
475-
}
476-
}()
471+
// Converting a *[]byte to a mem.BufferSlice incurs a heap allocation. This
472+
// conversion is performed by mem.NewBuffer. To avoid the extra allocation
473+
// a []byte is allocated directly if required and casted to a
474+
// mem.BufferSlice.
475+
var buf []byte
476+
// poolHandle is the pointer returned by the buffer pool (if it's used.).
477+
var poolHandle *[]byte
478+
useBufferPool := !mem.IsBelowBufferPoolingThreshold(int(fh.Length))
479+
if useBufferPool {
480+
poolHandle = f.pool.Get(int(fh.Length))
481+
buf = *poolHandle
482+
defer func() {
483+
if err != nil {
484+
f.pool.Put(poolHandle)
485+
}
486+
}()
487+
} else {
488+
buf = make([]byte, int(fh.Length))
489+
}
477490
if fh.Flags.Has(http2.FlagDataPadded) {
478491
if fh.Length == 0 {
479492
return io.ErrUnexpectedEOF
@@ -482,28 +495,34 @@ func (f *framer) readDataFrame(fh http2.FrameHeader, pool mem.BufferPool, df *pa
482495
// but it allows the rest of the payload to be read directly to the
483496
// start of the destination slice. This makes it easy to return the
484497
// original slice back to the buffer pool.
485-
if _, err := io.ReadFull(f.reader, (*payload)[:1]); err != nil {
498+
if _, err := io.ReadFull(f.reader, buf[:1]); err != nil {
486499
return err
487500
}
488-
padSize := (*payload)[0]
489-
*payload = (*payload)[:len(*payload)-1]
490-
if _, err := io.ReadFull(f.reader, *payload); err != nil {
501+
padSize := buf[0]
502+
buf = buf[:len(buf)-1]
503+
if _, err := io.ReadFull(f.reader, buf); err != nil {
491504
return err
492505
}
493-
if int(padSize) > len(*payload) {
506+
if int(padSize) > len(buf) {
494507
// If the length of the padding is greater than the
495508
// length of the frame payload, the recipient MUST
496509
// treat this as a connection error.
497510
// Filed: https://github.com/http2/http2-spec/issues/610
498511
return fmt.Errorf("pad size larger than data payload")
499512
}
500-
*payload = (*payload)[:len(*payload)-int(padSize)]
501-
} else if _, err := io.ReadFull(f.reader, *payload); err != nil {
513+
buf = buf[:len(buf)-int(padSize)]
514+
} else if _, err := io.ReadFull(f.reader, buf); err != nil {
502515
return err
503516
}
504517

505-
df.FrameHeader = fh
506-
df.data = mem.NewBuffer(payload, pool)
518+
f.dataFrame.FrameHeader = fh
519+
if useBufferPool {
520+
// Update the handle to point to the (potentially re-sliced) buf.
521+
*poolHandle = buf
522+
f.dataFrame.data = mem.NewBuffer(poolHandle, f.pool)
523+
} else {
524+
f.dataFrame.data = mem.SliceBuffer(buf)
525+
}
507526
return nil
508527
}
509528

0 commit comments

Comments
 (0)