Skip to content

Commit 01089b2

Browse files
ZhouyihaiDingdfawley
authored andcommitted
Remove buf copy when the compressor exist (grpc#1427)
1 parent c29d638 commit 01089b2

File tree

11 files changed

+111
-89
lines changed

11 files changed

+111
-89
lines changed

call.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,17 +99,17 @@ func sendRequest(ctx context.Context, dopts dialOptions, compressor Compressor,
9999
Client: true,
100100
}
101101
}
102-
outBuf, err := encode(dopts.codec, args, compressor, cbuf, outPayload)
102+
hdr, data, err := encode(dopts.codec, args, compressor, cbuf, outPayload)
103103
if err != nil {
104104
return err
105105
}
106106
if c.maxSendMessageSize == nil {
107107
return Errorf(codes.Internal, "callInfo maxSendMessageSize field uninitialized(nil)")
108108
}
109-
if len(outBuf) > *c.maxSendMessageSize {
110-
return Errorf(codes.ResourceExhausted, "grpc: trying to send message larger than max (%d vs. %d)", len(outBuf), *c.maxSendMessageSize)
109+
if len(data) > *c.maxSendMessageSize {
110+
return Errorf(codes.ResourceExhausted, "grpc: trying to send message larger than max (%d vs. %d)", len(data), *c.maxSendMessageSize)
111111
}
112-
err = t.Write(stream, outBuf, opts)
112+
err = t.Write(stream, hdr, data, opts)
113113
if err == nil && outPayload != nil {
114114
outPayload.SentTime = time.Now()
115115
dopts.copts.StatsHandler.HandleRPC(ctx, outPayload)

call_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,12 @@ func (h *testStreamHandler) handleStream(t *testing.T, s *transport.Stream) {
104104
}
105105
}
106106
// send a response back to end the stream.
107-
reply, err := encode(testCodec{}, &expectedResponse, nil, nil, nil)
107+
hdr, data, err := encode(testCodec{}, &expectedResponse, nil, nil, nil)
108108
if err != nil {
109109
t.Errorf("Failed to encode the response: %v", err)
110110
return
111111
}
112-
h.t.Write(s, reply, &transport.Options{})
112+
h.t.Write(s, hdr, data, &transport.Options{})
113113
h.t.WriteStatus(s, status.New(codes.OK, ""))
114114
}
115115

rpc_util.go

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -288,19 +288,20 @@ func (p *parser) recvMsg(maxReceiveMessageSize int) (pf payloadFormat, msg []byt
288288
return pf, msg, nil
289289
}
290290

291-
// encode serializes msg and prepends the message header. If msg is nil, it
292-
// generates the message header of 0 message length.
293-
func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer, outPayload *stats.OutPayload) ([]byte, error) {
294-
var (
295-
b []byte
296-
length uint
291+
// encode serializes msg and returns a buffer of message header and a buffer of msg.
292+
// If msg is nil, it generates the message header and an empty msg buffer.
293+
func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer, outPayload *stats.OutPayload) ([]byte, []byte, error) {
294+
var b []byte
295+
const (
296+
payloadLen = 1
297+
sizeLen = 4
297298
)
299+
298300
if msg != nil {
299301
var err error
300-
// TODO(zhaoq): optimize to reduce memory alloc and copying.
301302
b, err = c.Marshal(msg)
302303
if err != nil {
303-
return nil, Errorf(codes.Internal, "grpc: error while marshaling: %v", err.Error())
304+
return nil, nil, Errorf(codes.Internal, "grpc: error while marshaling: %v", err.Error())
304305
}
305306
if outPayload != nil {
306307
outPayload.Payload = msg
@@ -310,39 +311,28 @@ func encode(c Codec, msg interface{}, cp Compressor, cbuf *bytes.Buffer, outPayl
310311
}
311312
if cp != nil {
312313
if err := cp.Do(cbuf, b); err != nil {
313-
return nil, Errorf(codes.Internal, "grpc: error while compressing: %v", err.Error())
314+
return nil, nil, Errorf(codes.Internal, "grpc: error while compressing: %v", err.Error())
314315
}
315316
b = cbuf.Bytes()
316317
}
317-
length = uint(len(b))
318-
}
319-
if length > math.MaxUint32 {
320-
return nil, Errorf(codes.ResourceExhausted, "grpc: message too large (%d bytes)", length)
321318
}
322319

323-
const (
324-
payloadLen = 1
325-
sizeLen = 4
326-
)
327-
328-
var buf = make([]byte, payloadLen+sizeLen+len(b))
320+
if len(b) > math.MaxUint32 {
321+
return nil, nil, Errorf(codes.ResourceExhausted, "grpc: message too large (%d bytes)", len(b))
322+
}
329323

330-
// Write payload format
324+
bufHeader := make([]byte, payloadLen+sizeLen)
331325
if cp == nil {
332-
buf[0] = byte(compressionNone)
326+
bufHeader[0] = byte(compressionNone)
333327
} else {
334-
buf[0] = byte(compressionMade)
328+
bufHeader[0] = byte(compressionMade)
335329
}
336330
// Write length of b into buf
337-
binary.BigEndian.PutUint32(buf[1:], uint32(length))
338-
// Copy encoded msg to buf
339-
copy(buf[5:], b)
340-
331+
binary.BigEndian.PutUint32(bufHeader[payloadLen:], uint32(len(b)))
341332
if outPayload != nil {
342-
outPayload.WireLength = len(buf)
333+
outPayload.WireLength = payloadLen + sizeLen + len(b)
343334
}
344-
345-
return buf, nil
335+
return bufHeader, b, nil
346336
}
347337

348338
func checkRecvPayload(pf payloadFormat, recvCompress string, dc Decompressor) error {

rpc_util_test.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,15 @@ func TestEncode(t *testing.T) {
104104
msg proto.Message
105105
cp Compressor
106106
// outputs
107-
b []byte
108-
err error
107+
hdr []byte
108+
data []byte
109+
err error
109110
}{
110-
{nil, nil, []byte{0, 0, 0, 0, 0}, nil},
111+
{nil, nil, []byte{0, 0, 0, 0, 0}, []byte{}, nil},
111112
} {
112-
b, err := encode(protoCodec{}, test.msg, nil, nil, nil)
113-
if err != test.err || !bytes.Equal(b, test.b) {
114-
t.Fatalf("encode(_, _, %v, _) = %v, %v\nwant %v, %v", test.cp, b, err, test.b, test.err)
113+
hdr, data, err := encode(protoCodec{}, test.msg, nil, nil, nil)
114+
if err != test.err || !bytes.Equal(hdr, test.hdr) || !bytes.Equal(data, test.data) {
115+
t.Fatalf("encode(_, _, %v, _) = %v, %v, %v\nwant %v, %v, %v", test.cp, hdr, data, err, test.hdr, test.data, test.err)
115116
}
116117
}
117118
}
@@ -164,8 +165,8 @@ func TestToRPCErr(t *testing.T) {
164165
// bytes.
165166
func bmEncode(b *testing.B, mSize int) {
166167
msg := &perfpb.Buffer{Body: make([]byte, mSize)}
167-
encoded, _ := encode(protoCodec{}, msg, nil, nil, nil)
168-
encodedSz := int64(len(encoded))
168+
encodeHdr, encodeData, _ := encode(protoCodec{}, msg, nil, nil, nil)
169+
encodedSz := int64(len(encodeHdr) + len(encodeData))
169170
b.ReportAllocs()
170171
b.ResetTimer()
171172
for i := 0; i < b.N; i++ {

server.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -677,15 +677,15 @@ func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Str
677677
if s.opts.statsHandler != nil {
678678
outPayload = &stats.OutPayload{}
679679
}
680-
p, err := encode(s.opts.codec, msg, cp, cbuf, outPayload)
680+
hdr, data, err := encode(s.opts.codec, msg, cp, cbuf, outPayload)
681681
if err != nil {
682682
grpclog.Errorln("grpc: server failed to encode response: ", err)
683683
return err
684684
}
685-
if len(p) > s.opts.maxSendMessageSize {
686-
return status.Errorf(codes.ResourceExhausted, "grpc: trying to send message larger than max (%d vs. %d)", len(p), s.opts.maxSendMessageSize)
685+
if len(data) > s.opts.maxSendMessageSize {
686+
return status.Errorf(codes.ResourceExhausted, "grpc: trying to send message larger than max (%d vs. %d)", len(data), s.opts.maxSendMessageSize)
687687
}
688-
err = t.Write(stream, p, opts)
688+
err = t.Write(stream, hdr, data, opts)
689689
if err == nil && outPayload != nil {
690690
outPayload.SentTime = time.Now()
691691
s.opts.statsHandler.HandleRPC(stream.Context(), outPayload)

stream.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
362362
Client: true,
363363
}
364364
}
365-
out, err := encode(cs.codec, m, cs.cp, cs.cbuf, outPayload)
365+
hdr, data, err := encode(cs.codec, m, cs.cp, cs.cbuf, outPayload)
366366
defer func() {
367367
if cs.cbuf != nil {
368368
cs.cbuf.Reset()
@@ -374,10 +374,10 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
374374
if cs.c.maxSendMessageSize == nil {
375375
return Errorf(codes.Internal, "callInfo maxSendMessageSize field uninitialized(nil)")
376376
}
377-
if len(out) > *cs.c.maxSendMessageSize {
378-
return Errorf(codes.ResourceExhausted, "trying to send message larger than max (%d vs. %d)", len(out), *cs.c.maxSendMessageSize)
377+
if len(data) > *cs.c.maxSendMessageSize {
378+
return Errorf(codes.ResourceExhausted, "trying to send message larger than max (%d vs. %d)", len(data), *cs.c.maxSendMessageSize)
379379
}
380-
err = cs.t.Write(cs.s, out, &transport.Options{Last: false})
380+
err = cs.t.Write(cs.s, hdr, data, &transport.Options{Last: false})
381381
if err == nil && outPayload != nil {
382382
outPayload.SentTime = time.Now()
383383
cs.statsHandler.HandleRPC(cs.statsCtx, outPayload)
@@ -449,7 +449,7 @@ func (cs *clientStream) RecvMsg(m interface{}) (err error) {
449449
}
450450

451451
func (cs *clientStream) CloseSend() (err error) {
452-
err = cs.t.Write(cs.s, nil, &transport.Options{Last: true})
452+
err = cs.t.Write(cs.s, nil, nil, &transport.Options{Last: true})
453453
defer func() {
454454
if err != nil {
455455
cs.finish(err)
@@ -608,7 +608,7 @@ func (ss *serverStream) SendMsg(m interface{}) (err error) {
608608
if ss.statsHandler != nil {
609609
outPayload = &stats.OutPayload{}
610610
}
611-
out, err := encode(ss.codec, m, ss.cp, ss.cbuf, outPayload)
611+
hdr, data, err := encode(ss.codec, m, ss.cp, ss.cbuf, outPayload)
612612
defer func() {
613613
if ss.cbuf != nil {
614614
ss.cbuf.Reset()
@@ -617,10 +617,10 @@ func (ss *serverStream) SendMsg(m interface{}) (err error) {
617617
if err != nil {
618618
return err
619619
}
620-
if len(out) > ss.maxSendMessageSize {
621-
return Errorf(codes.ResourceExhausted, "trying to send message larger than max (%d vs. %d)", len(out), ss.maxSendMessageSize)
620+
if len(data) > ss.maxSendMessageSize {
621+
return Errorf(codes.ResourceExhausted, "trying to send message larger than max (%d vs. %d)", len(data), ss.maxSendMessageSize)
622622
}
623-
if err := ss.t.Write(ss.s, out, &transport.Options{Last: false}); err != nil {
623+
if err := ss.t.Write(ss.s, hdr, data, &transport.Options{Last: false}); err != nil {
624624
return toRPCErr(err)
625625
}
626626
if outPayload != nil {

transport/handler_server.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,10 @@ func (ht *serverHandlerTransport) writeCommonHeaders(s *Stream) {
255255
}
256256
}
257257

258-
func (ht *serverHandlerTransport) Write(s *Stream, data []byte, opts *Options) error {
258+
func (ht *serverHandlerTransport) Write(s *Stream, hdr []byte, data []byte, opts *Options) error {
259259
return ht.do(func() {
260260
ht.writeCommonHeaders(s)
261+
ht.rw.Write(hdr)
261262
ht.rw.Write(data)
262263
if !opts.Delay {
263264
ht.rw.(http.Flusher).Flush()

transport/http2_client.go

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -683,8 +683,15 @@ func (t *http2Client) GracefulClose() error {
683683
// should proceed only if Write returns nil.
684684
// TODO(zhaoq): opts.Delay is ignored in this implementation. Support it later
685685
// if it improves the performance.
686-
func (t *http2Client) Write(s *Stream, data []byte, opts *Options) error {
687-
r := bytes.NewBuffer(data)
686+
func (t *http2Client) Write(s *Stream, hdr []byte, data []byte, opts *Options) error {
687+
secondStart := http2MaxFrameLen - len(hdr)%http2MaxFrameLen
688+
if len(data) < secondStart {
689+
secondStart = len(data)
690+
}
691+
hdr = append(hdr, data[:secondStart]...)
692+
data = data[secondStart:]
693+
isLastSlice := (len(data) == 0)
694+
r := bytes.NewBuffer(hdr)
688695
var (
689696
p []byte
690697
oqv uint32
@@ -726,9 +733,6 @@ func (t *http2Client) Write(s *Stream, data []byte, opts *Options) error {
726733
endStream bool
727734
forceFlush bool
728735
)
729-
if opts.Last && r.Len() == 0 {
730-
endStream = true
731-
}
732736
// Indicate there is a writer who is about to write a data frame.
733737
t.framer.adjustNumWriters(1)
734738
// Got some quota. Try to acquire writing privilege on the transport.
@@ -768,10 +772,22 @@ func (t *http2Client) Write(s *Stream, data []byte, opts *Options) error {
768772
t.writableChan <- 0
769773
continue
770774
}
771-
if r.Len() == 0 && t.framer.adjustNumWriters(0) == 1 {
772-
// Do a force flush iff this is last frame for the entire gRPC message
773-
// and the caller is the only writer at this moment.
774-
forceFlush = true
775+
if r.Len() == 0 {
776+
if isLastSlice {
777+
if opts.Last {
778+
endStream = true
779+
}
780+
if t.framer.adjustNumWriters(0) == 1 {
781+
// Do a force flush iff this is last frame for the entire gRPC message
782+
// and the caller is the only writer at this moment.
783+
forceFlush = true
784+
}
785+
} else {
786+
isLastSlice = true
787+
if len(data) != 0 {
788+
r = bytes.NewBuffer(data)
789+
}
790+
}
775791
}
776792
// If WriteData fails, all the pending streams will be handled
777793
// by http2Client.Close(). No explicit CloseStream() needs to be

transport/http2_server.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -827,8 +827,15 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
827827

828828
// Write converts the data into HTTP2 data frame and sends it out. Non-nil error
829829
// is returns if it fails (e.g., framing error, transport error).
830-
func (t *http2Server) Write(s *Stream, data []byte, opts *Options) (err error) {
830+
func (t *http2Server) Write(s *Stream, hdr []byte, data []byte, opts *Options) (err error) {
831831
// TODO(zhaoq): Support multi-writers for a single stream.
832+
secondStart := http2MaxFrameLen - len(hdr)%http2MaxFrameLen
833+
if len(data) < secondStart {
834+
secondStart = len(data)
835+
}
836+
hdr = append(hdr, data[:secondStart]...)
837+
data = data[secondStart:]
838+
isLastSlice := (len(data) == 0)
832839
var writeHeaderFrame bool
833840
s.mu.Lock()
834841
if s.state == streamDone {
@@ -842,7 +849,7 @@ func (t *http2Server) Write(s *Stream, data []byte, opts *Options) (err error) {
842849
if writeHeaderFrame {
843850
t.WriteHeader(s, nil)
844851
}
845-
r := bytes.NewBuffer(data)
852+
r := bytes.NewBuffer(hdr)
846853
var (
847854
p []byte
848855
oqv uint32
@@ -921,8 +928,15 @@ func (t *http2Server) Write(s *Stream, data []byte, opts *Options) (err error) {
921928
continue
922929
}
923930
var forceFlush bool
924-
if r.Len() == 0 && t.framer.adjustNumWriters(0) == 1 && !opts.Last {
925-
forceFlush = true
931+
if r.Len() == 0 {
932+
if isLastSlice {
933+
if t.framer.adjustNumWriters(0) == 1 && !opts.Last {
934+
forceFlush = true
935+
}
936+
} else {
937+
r = bytes.NewBuffer(data)
938+
isLastSlice = true
939+
}
926940
}
927941
// Reset ping strikes when sending data since this might cause
928942
// the peer to send ping.

transport/transport.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ type ClientTransport interface {
564564

565565
// Write sends the data for the given stream. A nil stream indicates
566566
// the write is to be performed on the transport as a whole.
567-
Write(s *Stream, data []byte, opts *Options) error
567+
Write(s *Stream, hdr []byte, data []byte, opts *Options) error
568568

569569
// NewStream creates a Stream for an RPC.
570570
NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, error)
@@ -606,7 +606,7 @@ type ServerTransport interface {
606606

607607
// Write sends the data for the given stream.
608608
// Write may not be called on all streams.
609-
Write(s *Stream, data []byte, opts *Options) error
609+
Write(s *Stream, hdr []byte, data []byte, opts *Options) error
610610

611611
// WriteStatus sends the status of a stream to the client. WriteStatus is
612612
// the final call made on a stream and always occurs.

0 commit comments

Comments
 (0)