Skip to content

Conversation

@arjan-bal
Copy link
Contributor

Benchmarks

# Test command
$  go run benchmark/benchmain/main.go -benchtime=60s -workloads=unary \
   -compression=off -maxConcurrentCalls=200 -trace=off \
   -reqSizeBytes=100 -respSizeBytes=100 -networkMode=Local -resultFile="${RUN_NAME}"

$ go run benchmark/benchresult/main.go unary-before unary-after
               Title       Before        After Percentage
            TotalOps      7801951      7889246     1.12%
             SendOps            0            0      NaN%
             RecvOps            0            0      NaN%
            Bytes/op     10005.90      9911.48    -0.94%
           Allocs/op       146.91       143.91    -2.04%
             ReqT/op 104026013.33 105189946.67     1.12%
            RespT/op 104026013.33 105189946.67     1.12%
            50th-Lat   1.375183ms   1.360319ms    -1.08%
            90th-Lat   2.293816ms   2.249015ms    -1.95%
            99th-Lat   3.162307ms    3.13568ms    -0.84%
             Avg-Lat   1.536462ms   1.519465ms    -1.11%
           GoVersion     go1.24.8     go1.24.8
         GrpcVersion   1.77.0-dev   1.77.0-dev

RELEASE NOTES: N/A

@arjan-bal arjan-bal added this to the 1.77 Release milestone Oct 23, 2025
@arjan-bal arjan-bal added Type: Performance Performance improvements (CPU, network, memory, etc) Area: Transport Includes HTTP/2 client/server and HTTP server handler transports and advanced transport features. labels Oct 23, 2025
@codecov
Copy link

codecov bot commented Oct 23, 2025

Codecov Report

❌ Patch coverage is 92.30769% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 83.17%. Comparing base (f448a97) to head (ead8434).
⚠️ Report is 10 commits behind head on master.

Files with missing lines Patch % Lines
internal/transport/transport.go 75.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8668      +/-   ##
==========================================
+ Coverage   81.97%   83.17%   +1.20%     
==========================================
  Files         417      417              
  Lines       40788    32178    -8610     
==========================================
- Hits        33435    26764    -6671     
+ Misses       5991     4034    -1957     
- Partials     1362     1380      +18     
Files with missing lines Coverage Δ
internal/transport/http2_client.go 92.71% <100.00%> (+0.64%) ⬆️
preloader.go 65.21% <ø> (+6.88%) ⬆️
rpc_util.go 83.43% <100.00%> (+1.33%) ⬆️
stream.go 81.83% <100.00%> (+0.23%) ⬆️
internal/transport/transport.go 88.70% <75.00%> (-2.02%) ⬇️

... and 359 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment on lines -506 to -508
closeStream: func(err error) {
s.Close(err)
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What heap allocation does this reduce?

Copy link
Contributor Author

@arjan-bal arjan-bal Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The closeStream closure captures a variable, s, from outside its own body.

The Go compiler performs escape analysis to determine if a variable can safely live on the stack or if it must be "escaped" to the heap. A variable on the stack is automatically freed when its parent function returns. The closeStream function might be called long after the function that created it has returned (for example, if it's stored in a struct field that lives on).

If s remained on the stack of the creating function, the closeStream function would be left holding an invalid pointer to garbage memory. To prevent this, the compiler sees that s is captured by a closure that might outlive the current function. It therefore allocates s on the heap instead of the stack. The closure then holds a valid pointer to this heap-allocated s.

Note that even though s already points to data on the heap, the s variable itself (the pointer) also escapes. This is different from simply assigning s to a struct field. In that case, a copy of the pointer s is stored in the field. With a closure, the closure captures a reference to the original s variable, forcing that variable to move to the heap so it can be safely shared.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. That helps. The last paragraph was what I wanted. Because I was thinking the ClientStream should already be on the heap, so what is this change preventing. Thanks.

@easwars easwars assigned arjan-bal and unassigned easwars Oct 28, 2025
@arjan-bal arjan-bal assigned easwars and unassigned arjan-bal Oct 28, 2025
Comment on lines -506 to -508
closeStream: func(err error) {
s.Close(err)
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. That helps. The last paragraph was what I wanted. Because I was thinking the ClientStream should already be on the heap, so what is this change preventing. Thanks.

@easwars easwars assigned arjan-bal and unassigned easwars Oct 28, 2025
@arjan-bal arjan-bal merged commit f959da6 into grpc:master Oct 30, 2025
14 checks passed
@arjan-bal arjan-bal deleted the optimize-heap-allocs-1 branch October 30, 2025 05:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area: Transport Includes HTTP/2 client/server and HTTP server handler transports and advanced transport features. Type: Performance Performance improvements (CPU, network, memory, etc)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants