@@ -12,6 +12,7 @@ import (
12
12
"compress/gzip"
13
13
"compress/zlib"
14
14
"context"
15
+ crand "crypto/rand"
15
16
"crypto/tls"
16
17
"encoding/json"
17
18
"errors"
@@ -5075,12 +5076,44 @@ func getNoBody(urlStr string) (*Response, error) {
5075
5076
5076
5077
// A benchmark for profiling the client without the HTTP server code.
5077
5078
// The server code runs in a subprocess.
5079
+ //
5080
+ // For use like:
5081
+ //
5082
+ // $ go test -c
5083
+ // $ ./http.test -test.run=XX -test.bench=BenchmarkClient$ -test.benchtime=15s -test.cpuprofile=http.prof
5084
+ // $ go tool pprof http.test http.prof
5085
+ // (pprof) web
5078
5086
func BenchmarkClient (b * testing.B ) {
5087
+ var data = []byte ("Hello world.\n " )
5088
+
5089
+ url := startBenchmarkServer (b , func (w ResponseWriter , r * Request ) {
5090
+ w .Header ().Set ("Content-Type" , "text/html; charset=utf-8" )
5091
+ w .Write (data )
5092
+ })
5093
+
5094
+ // Do b.N requests to the server.
5095
+ b .StartTimer ()
5096
+ for i := 0 ; i < b .N ; i ++ {
5097
+ res , err := Get (url )
5098
+ if err != nil {
5099
+ b .Fatalf ("Get: %v" , err )
5100
+ }
5101
+ body , err := io .ReadAll (res .Body )
5102
+ res .Body .Close ()
5103
+ if err != nil {
5104
+ b .Fatalf ("ReadAll: %v" , err )
5105
+ }
5106
+ if ! bytes .Equal (body , data ) {
5107
+ b .Fatalf ("Got body: %q" , body )
5108
+ }
5109
+ }
5110
+ b .StopTimer ()
5111
+ }
5112
+
5113
+ func startBenchmarkServer (b * testing.B , handler func (ResponseWriter , * Request )) string {
5079
5114
b .ReportAllocs ()
5080
5115
b .StopTimer ()
5081
- defer afterTest (b )
5082
5116
5083
- var data = []byte ("Hello world.\n " )
5084
5117
if server := os .Getenv ("TEST_BENCH_SERVER" ); server != "" {
5085
5118
// Server process mode.
5086
5119
port := os .Getenv ("TEST_BENCH_SERVER_PORT" ) // can be set by user
@@ -5093,21 +5126,33 @@ func BenchmarkClient(b *testing.B) {
5093
5126
os .Exit (1 )
5094
5127
}
5095
5128
fmt .Println (ln .Addr ().String ())
5129
+
5096
5130
HandleFunc ("/" , func (w ResponseWriter , r * Request ) {
5097
5131
r .ParseForm ()
5098
5132
if r .Form .Get ("stop" ) != "" {
5099
5133
os .Exit (0 )
5100
5134
}
5101
- w .Header ().Set ("Content-Type" , "text/html; charset=utf-8" )
5102
- w .Write (data )
5135
+ handler (w , r )
5103
5136
})
5104
5137
var srv Server
5105
5138
log .Fatal (srv .Serve (ln ))
5106
5139
}
5107
5140
5141
+ var benchmarkName string
5142
+ if pc , _ , _ , ok := runtime .Caller (1 ); ok {
5143
+ details := runtime .FuncForPC (pc )
5144
+ name := details .Name ()
5145
+ _ , benchmarkName , _ = strings .Cut (name , "." )
5146
+ if ! strings .HasPrefix (benchmarkName , "Benchmark" ) {
5147
+ b .Fatalf ("Expected to be called from BenchmarkXxx function, called from %s" , name )
5148
+ }
5149
+ } else {
5150
+ b .Fatal ("Failed to get caller name" )
5151
+ }
5152
+
5108
5153
// Start server process.
5109
5154
ctx , cancel := context .WithCancel (context .Background ())
5110
- cmd := testenv .CommandContext (b , ctx , os .Args [0 ], "-test.run=^$" , "-test.bench=^BenchmarkClient $" )
5155
+ cmd := testenv .CommandContext (b , ctx , os .Args [0 ], "-test.run=^$" , "-test.bench=" + benchmarkName + " $" )
5111
5156
cmd .Env = append (cmd .Environ (), "TEST_BENCH_SERVER=yes" )
5112
5157
cmd .Stderr = os .Stderr
5113
5158
stdout , err := cmd .StdoutPipe ()
@@ -5123,10 +5168,6 @@ func BenchmarkClient(b *testing.B) {
5123
5168
done <- cmd .Wait ()
5124
5169
close (done )
5125
5170
}()
5126
- defer func () {
5127
- cancel ()
5128
- <- done
5129
- }()
5130
5171
5131
5172
// Wait for the server in the child process to respond and tell us
5132
5173
// its listening address, once it's started listening:
@@ -5139,29 +5180,57 @@ func BenchmarkClient(b *testing.B) {
5139
5180
b .Fatalf ("initial probe of child process failed: %v" , err )
5140
5181
}
5141
5182
5183
+ // Instruct server process to stop.
5184
+ b .Cleanup (func () {
5185
+ getNoBody (url + "?stop=yes" )
5186
+ if err := <- done ; err != nil {
5187
+ b .Fatalf ("subprocess failed: %v" , err )
5188
+ }
5189
+
5190
+ cancel ()
5191
+ <- done
5192
+
5193
+ afterTest (b )
5194
+ })
5195
+
5196
+ return url
5197
+ }
5198
+
5199
+ func BenchmarkClientGzip (b * testing.B ) {
5200
+ const nRandBytes = 1024 * 1024
5201
+
5202
+ var buf bytes.Buffer
5203
+ gz := gzip .NewWriter (& buf )
5204
+ if _ , err := io .CopyN (gz , crand .Reader , nRandBytes ); err != nil {
5205
+ fmt .Fprintln (os .Stderr , err .Error ())
5206
+ os .Exit (1 )
5207
+ }
5208
+ gz .Close ()
5209
+
5210
+ data := buf .Bytes ()
5211
+
5212
+ url := startBenchmarkServer (b , func (w ResponseWriter , r * Request ) {
5213
+ w .Header ().Set ("Content-Encoding" , "gzip" )
5214
+ w .Write (data )
5215
+ })
5216
+
5142
5217
// Do b.N requests to the server.
5143
5218
b .StartTimer ()
5144
5219
for i := 0 ; i < b .N ; i ++ {
5145
5220
res , err := Get (url )
5146
5221
if err != nil {
5147
5222
b .Fatalf ("Get: %v" , err )
5148
5223
}
5149
- body , err := io .ReadAll ( res .Body )
5224
+ n , err := io .Copy ( io . Discard , res .Body )
5150
5225
res .Body .Close ()
5151
5226
if err != nil {
5152
5227
b .Fatalf ("ReadAll: %v" , err )
5153
5228
}
5154
- if ! bytes . Equal ( body , data ) {
5155
- b .Fatalf ("Got body: %q " , body )
5229
+ if n != nRandBytes {
5230
+ b .Fatalf ("ReadAll: expected %d, got %d " , nRandBytes , n )
5156
5231
}
5157
5232
}
5158
5233
b .StopTimer ()
5159
-
5160
- // Instruct server process to stop.
5161
- getNoBody (url + "?stop=yes" )
5162
- if err := <- done ; err != nil {
5163
- b .Fatalf ("subprocess failed: %v" , err )
5164
- }
5165
5234
}
5166
5235
5167
5236
func BenchmarkServerFakeConnNoKeepAlive (b * testing.B ) {
0 commit comments