Skip to content

Commit c5ff751

Browse files
committed
test: add a light benchmark harness
1 parent de4cfed commit c5ff751

File tree

3 files changed

+291
-6
lines changed

3 files changed

+291
-6
lines changed

http-easy-test/info.rkt

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,17 @@
22

33
(define license 'BSD-3-Clause)
44
(define collection "tests")
5-
(define deps '("base"))
6-
(define build-deps '("http-easy"
7-
"net-cookies-lib"
8-
"rackunit-lib"
9-
("resource-pool-lib" #:version "0.1")
10-
"web-server-lib"))
5+
(define deps
6+
'("base"))
7+
(define build-deps
8+
'("http-easy"
9+
"monocle-lib"
10+
"net-cookies-lib"
11+
"pict-lib"
12+
"plot-gui-lib"
13+
"plot-lib"
14+
"rackunit-lib"
15+
("resource-pool-lib" #:version "0.1")
16+
"threading-lib"
17+
"web-server-lib"))
1118
(define update-implies '("http-easy"))
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
{
2+
"Racket chez-scheme 8.17.0.6 (macosx aarch64) [Darwin hercules.local 24.5.0 Darwin Kernel Version 24.5.0: Tue Apr 22 19:54:25 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T6020 arm64]": {
3+
"concurrent-GET": [
4+
{
5+
"cpu-time": 3607,
6+
"gc-time": 210,
7+
"real-time": 3730,
8+
"timestamp": 1752580690
9+
},
10+
{
11+
"cpu-time": 3776,
12+
"gc-time": 210,
13+
"real-time": 3902,
14+
"timestamp": 1752580672
15+
},
16+
{
17+
"cpu-time": 3778,
18+
"gc-time": 214,
19+
"real-time": 3876,
20+
"timestamp": 1752580652
21+
},
22+
{
23+
"cpu-time": 3804,
24+
"gc-time": 216,
25+
"real-time": 3900,
26+
"timestamp": 1752580562
27+
},
28+
{
29+
"cpu-time": 4093,
30+
"gc-time": 216,
31+
"real-time": 4189,
32+
"timestamp": 1752580492
33+
},
34+
{
35+
"cpu-time": 3881,
36+
"gc-time": 216,
37+
"real-time": 3979,
38+
"timestamp": 1752580466
39+
},
40+
{
41+
"cpu-time": 3763,
42+
"gc-time": 213,
43+
"real-time": 3860,
44+
"timestamp": 1752580446
45+
},
46+
{
47+
"cpu-time": 3567,
48+
"gc-time": 208,
49+
"real-time": 3690,
50+
"timestamp": 1752580421
51+
},
52+
{
53+
"cpu-time": 3790,
54+
"gc-time": 209,
55+
"real-time": 3916,
56+
"timestamp": 1752580355
57+
},
58+
{
59+
"cpu-time": 5532,
60+
"gc-time": 428,
61+
"real-time": 5632,
62+
"timestamp": 1752580332
63+
}
64+
],
65+
"sequential-GET": [
66+
{
67+
"cpu-time": 3484,
68+
"gc-time": 86,
69+
"real-time": 3784,
70+
"timestamp": 1752580686
71+
},
72+
{
73+
"cpu-time": 3886,
74+
"gc-time": 87,
75+
"real-time": 4162,
76+
"timestamp": 1752580667
77+
},
78+
{
79+
"cpu-time": 3529,
80+
"gc-time": 89,
81+
"real-time": 3821,
82+
"timestamp": 1752580417
83+
},
84+
{
85+
"cpu-time": 3866,
86+
"gc-time": 87,
87+
"real-time": 4143,
88+
"timestamp": 1752580350
89+
},
90+
{
91+
"cpu-time": 5881,
92+
"gc-time": 249,
93+
"real-time": 6193,
94+
"timestamp": 1752580325
95+
}
96+
]
97+
}
98+
}
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#lang racket/base
2+
3+
(require data/monocle
4+
json
5+
net/http-easy
6+
plot
7+
racket/date
8+
racket/format
9+
racket/list
10+
racket/match
11+
racket/promise
12+
racket/runtime-path
13+
rackunit
14+
threading
15+
(only-in web-server/http make-header response/full)
16+
"private/common.rkt")
17+
18+
(provide
19+
benchmarks)
20+
21+
(define-runtime-path benchmarks.json
22+
"benchmarks.json")
23+
24+
(define (~machine)
25+
(string->symbol
26+
(format
27+
"Racket ~a ~a (~a ~a) [~a]"
28+
(system-type 'vm)
29+
(version)
30+
(system-type 'os*)
31+
(system-type 'arch)
32+
(system-type 'machine))))
33+
34+
(define (keep lst n)
35+
(take lst (min (length lst) n)))
36+
37+
(define (read-benchmarks)
38+
(with-handlers ([exn:fail:filesystem? (λ (_) (hash))])
39+
(call-with-input-file benchmarks.json read-json)))
40+
41+
(define (write-benchmarks benchmarks)
42+
(call-with-output-file benchmarks.json
43+
#:exists 'truncate/replace
44+
(lambda (out)
45+
(write-json
46+
#:indent 2
47+
benchmarks out))))
48+
49+
(define (plot-results name)
50+
(define machine (~machine))
51+
(define benchmarks
52+
(~> (read-benchmarks)
53+
((&opt-hash-ref* machine name))
54+
(or null)
55+
(sort > #:key (λ (ht) (hash-ref ht 'timestamp)))
56+
(keep 5)
57+
(reverse)))
58+
(parameterize ([plot-y-label "Time (ms)"]
59+
[plot-x-label "Measurements"])
60+
(plot-pict
61+
#:title (~a name)
62+
(for/list ([(bench idx) (in-indexed (in-list benchmarks))])
63+
(define timestamp
64+
(hash-ref bench 'timestamp))
65+
(discrete-histogram
66+
#:label (date->string (seconds->date timestamp) #t)
67+
#:line-color (add1 idx)
68+
#:color (add1 idx)
69+
#:x-min (add1 idx)
70+
#:skip (+ (length benchmarks) 0.5)
71+
(list
72+
(list "Real" (hash-ref bench 'real-time))
73+
(list "CPU" (hash-ref bench 'cpu-time))
74+
(list "GC" (hash-ref bench 'gc-time))))))))
75+
76+
(define (delete-benchmark name)
77+
(define machine (~machine))
78+
(define benchmarks (read-benchmarks))
79+
(define &benchmark (&opt-hash-ref* machine name))
80+
(write-benchmarks (&benchmark benchmarks #f)))
81+
82+
(define-check (check-benchmark name tolerance proc)
83+
(define timestamp (current-seconds))
84+
(define machine (~machine))
85+
(sync (system-idle-evt))
86+
(collect-garbage)
87+
(collect-garbage)
88+
(define-values (_ cpu-time real-time gc-time)
89+
(time-apply proc null))
90+
(define benchmarks
91+
(read-benchmarks))
92+
(define &benchmark
93+
(&opt-hash-ref* machine name))
94+
(define existing-benchmarks
95+
(or (&benchmark benchmarks) null))
96+
(unless (null? existing-benchmarks)
97+
(match-define
98+
(hash* ['real-time old-real-time]
99+
['cpu-time old-cpu-time]
100+
['gc-time old-gc-time])
101+
(car existing-benchmarks))
102+
(when (or (> cpu-time (* old-cpu-time tolerance))
103+
(> real-time (* old-real-time tolerance))
104+
(> gc-time (* old-gc-time tolerance)))
105+
(fail-check
106+
(string-append
107+
(format "benchmark ~a failed~n" name)
108+
(format " cpu time: ~s (was: ~s; slowdown: ~a)~n" cpu-time old-cpu-time (~slowdown cpu-time old-cpu-time))
109+
(format " real time: ~s (was: ~s; slowdown: ~a)~n" real-time old-real-time (~slowdown real-time old-real-time))
110+
(format " gc time: ~s (was: ~s; slowdown: ~a)" gc-time old-gc-time (~slowdown gc-time old-gc-time))))))
111+
(write-benchmarks
112+
(&benchmark
113+
benchmarks
114+
(cons
115+
(hasheq
116+
'timestamp timestamp
117+
'real-time real-time
118+
'cpu-time cpu-time
119+
'gc-time gc-time)
120+
(keep existing-benchmarks 49)))))
121+
122+
(define (~slowdown current old)
123+
(define % (if (zero? old) 1.0 (/ current old)))
124+
(~a (~r #:precision '(= 2) (* % 100)) "%"))
125+
126+
(define (handle-OK _req)
127+
(response/full
128+
#;code 200
129+
#;message #"OK"
130+
#;seconds (current-seconds)
131+
#;mime #"text/plain"
132+
#;headers (list (make-header #"Content-Length" #"2"))
133+
#;body (list #"OK")))
134+
135+
(define benchmarks
136+
(test-suite
137+
"http-easy"
138+
139+
(test-suite
140+
"benchmarks"
141+
142+
(test-case "sequential GETs"
143+
(call-with-web-server
144+
handle-OK
145+
(lambda (addr)
146+
(parameterize ([current-session (make-session)])
147+
(check-benchmark
148+
'sequential-GET 1.20
149+
(lambda ()
150+
(for ([_ (in-range 10000)])
151+
(get addr))))
152+
(session-close! (current-session))))))
153+
154+
(test-case "concurrent GETs"
155+
(call-with-web-server
156+
handle-OK
157+
(lambda (addr)
158+
(define sema (make-semaphore 3))
159+
(parameterize ([current-session (make-session)])
160+
(check-benchmark
161+
'concurrent-GET 1.10
162+
(lambda ()
163+
(define promises
164+
(for/list ([_ (in-range 10000)])
165+
(delay/thread
166+
(call-with-semaphore sema
167+
(lambda ()
168+
(get addr))))))
169+
(for-each force promises)))
170+
(session-close! (current-session)))))))))
171+
172+
(module+ test
173+
(require rackunit/text-ui)
174+
(run-tests benchmarks))
175+
176+
(module+ main
177+
(require pict)
178+
(hc-append
179+
(plot-results 'sequential-GET)
180+
(plot-results 'concurrent-GET)))

0 commit comments

Comments
 (0)