@@ -689,30 +689,17 @@ type clientStream struct {
689689
690690 cancel context.CancelFunc // cancels all attempts
691691
692- sentLast bool // sent an end stream
693-
694- receivedFirstMsg bool // set after the first message is received
695-
696692 methodConfig * MethodConfig
697693
698694 ctx context.Context // the application's context, wrapped by stats/tracing
699695
700696 retryThrottler * retryThrottler // The throttler active when the RPC began.
701697
702698 binlogs []binarylog.MethodLogger
703- // serverHeaderBinlogged is a boolean for whether server header has been
704- // logged. Server header will be logged when the first time one of those
705- // happens: stream.Header(), stream.Recv().
706- //
707- // It's only read and used by Recv() and Header(), so it doesn't need to be
708- // synchronized.
709- serverHeaderBinlogged bool
710699
711700 mu sync.Mutex
712- firstAttempt bool // if true, transparent retry is valid
713- numRetries int // exclusive of transparent retry attempt(s)
714- numRetriesSincePushback int // retries since pushback; to reset backoff
715- finished bool // TODO: replace with atomic cmpxchg or sync.Once?
701+ numRetries int // exclusive of transparent retry attempt(s)
702+ numRetriesSincePushback int // retries since pushback; to reset backoff
716703 // attempt is the active client stream attempt.
717704 // The only place where it is written is the newAttemptLocked method and this method never writes nil.
718705 // So, attempt can be nil only inside newClientStream function when clientStream is first created.
@@ -722,13 +709,33 @@ type clientStream struct {
722709 // place where we need to check if the attempt is nil.
723710 attempt * csAttempt
724711 // TODO(hedging): hedging will have multiple attempts simultaneously.
725- committed bool // active attempt committed for retry?
726712 onCommit func ()
727713 replayBuffer []replayOp // operations to replay on retry
728714 replayBufferSize int // current size of replayBuffer
715+
716+ // Bool fields are grouped at the tail to eliminate the alignment padding
717+ // that would otherwise follow each bool when the next field is pointer- or
718+ // int-sized. See https://github.com/grpc/grpc-go/issues/9280 for benchmarks.
719+ // Add new bool fields here, not inline above.
720+
721+ // Not guarded by mu.
722+ sentLast bool // sent an end stream
723+ receivedFirstMsg bool // set after the first message is received
724+ // serverHeaderBinlogged is a boolean for whether server header has been
725+ // logged. Server header will be logged when the first time one of those
726+ // happens: stream.Header(), stream.Recv().
727+ //
728+ // It's only read and used by Recv() and Header(), so it doesn't need to be
729+ // synchronized.
730+ serverHeaderBinlogged bool
729731 // nameResolutionDelay indicates if there was a delay in the name resolution.
730732 // This field is only valid on client side, it's always false on server side.
731733 nameResolutionDelay bool
734+
735+ // Guarded by mu.
736+ firstAttempt bool // if true, transparent retry is valid
737+ finished bool // TODO: replace with atomic cmpxchg or sync.Once?
738+ committed bool // active attempt committed for retry?
732739}
733740
734741type replayOp struct {
0 commit comments