v / vlib / benchmark
Raw file | 227 loc (201 sloc) | 6.41 KB | Latest commit hash 46e431764
1module benchmark
2
3import time
4import term
5
6pub const (
7 b_ok = term.ok_message('OK ')
8 b_fail = term.fail_message('FAIL')
9 b_skip = term.warn_message('SKIP')
10 b_spent = term.ok_message('SPENT')
11)
12
13pub struct Benchmark {
14pub mut:
15 bench_timer time.StopWatch
16 verbose bool
17 no_cstep bool
18 step_timer time.StopWatch
19 ntotal int
20 nok int
21 nfail int
22 nskip int
23 nexpected_steps int
24 njobs int
25 cstep int
26 bok string
27 bfail string
28}
29
30// new_benchmark returns a `Benchmark` instance on the stack.
31pub fn new_benchmark() Benchmark {
32 return Benchmark{
33 bench_timer: time.new_stopwatch()
34 verbose: true
35 }
36}
37
38// new_benchmark_no_cstep returns a new `Benchmark` instance with step counting disabled.
39pub fn new_benchmark_no_cstep() Benchmark {
40 return Benchmark{
41 bench_timer: time.new_stopwatch()
42 verbose: true
43 no_cstep: true
44 }
45}
46
47// new_benchmark_pointer returns a new `Benchmark` instance allocated on the heap.
48// This is useful for long-lived use of `Benchmark` instances.
49pub fn new_benchmark_pointer() &Benchmark {
50 return &Benchmark{
51 bench_timer: time.new_stopwatch()
52 verbose: true
53 }
54}
55
56// set_total_expected_steps sets the the total amount of steps the benchmark is expected to take.
57pub fn (mut b Benchmark) set_total_expected_steps(n int) {
58 b.nexpected_steps = n
59}
60
61// stop stops the internal benchmark timer.
62pub fn (mut b Benchmark) stop() {
63 b.bench_timer.stop()
64}
65
66// step increases the step count by 1 and restarts the internal timer.
67pub fn (mut b Benchmark) step() {
68 b.step_timer.restart()
69 if !b.no_cstep {
70 b.cstep++
71 }
72}
73
74// fail increases the fail count by 1 and stops the internal timer.
75pub fn (mut b Benchmark) fail() {
76 b.step_timer.stop()
77 b.ntotal++
78 b.nfail++
79}
80
81// ok increases the ok count by 1 and stops the internal timer.
82pub fn (mut b Benchmark) ok() {
83 b.step_timer.stop()
84 b.ntotal++
85 b.nok++
86}
87
88// skip increases the skip count by 1 and stops the internal timer.
89pub fn (mut b Benchmark) skip() {
90 b.step_timer.stop()
91 b.ntotal++
92 b.nskip++
93}
94
95// fail_many increases the fail count by `n` and stops the internal timer.
96pub fn (mut b Benchmark) fail_many(n int) {
97 b.step_timer.stop()
98 b.ntotal += n
99 b.nfail += n
100}
101
102// ok_many increases the ok count by `n` and stops the internal timer.
103pub fn (mut b Benchmark) ok_many(n int) {
104 b.step_timer.stop()
105 b.ntotal += n
106 b.nok += n
107}
108
109// neither_fail_nor_ok stops the internal timer.
110pub fn (mut b Benchmark) neither_fail_nor_ok() {
111 b.step_timer.stop()
112}
113
114// start returns a new, running, instance of `Benchmark`.
115// This is a shorthand for calling `new_benchmark().step()`.
116pub fn start() Benchmark {
117 mut b := new_benchmark()
118 b.step()
119 return b
120}
121
122// measure prints the current time spent doing `label`, since the benchmark was started, or since its last call
123pub fn (mut b Benchmark) measure(label string) i64 {
124 b.ok()
125 res := b.step_timer.elapsed().microseconds()
126 println(b.step_message_with_label(benchmark.b_spent, 'in ${label}'))
127 b.step()
128 return res
129}
130
131// step_message_with_label_and_duration returns a string describing the current step.
132pub fn (b &Benchmark) step_message_with_label_and_duration(label string, msg string, sduration time.Duration) string {
133 timed_line := b.tdiff_in_ms(msg, sduration.microseconds())
134 if b.nexpected_steps > 1 {
135 mut sprogress := ''
136 if b.nexpected_steps < 10 {
137 sprogress = if b.no_cstep {
138 'TMP1/${b.nexpected_steps:1d}'
139 } else {
140 '${b.cstep:1d}/${b.nexpected_steps:1d}'
141 }
142 } else if b.nexpected_steps >= 10 && b.nexpected_steps < 100 {
143 sprogress = if b.no_cstep {
144 'TMP2/${b.nexpected_steps:2d}'
145 } else {
146 '${b.cstep:2d}/${b.nexpected_steps:2d}'
147 }
148 } else if b.nexpected_steps >= 100 && b.nexpected_steps < 1000 {
149 sprogress = if b.no_cstep {
150 'TMP3/${b.nexpected_steps:3d}'
151 } else {
152 '${b.cstep:3d}/${b.nexpected_steps:3d}'
153 }
154 } else {
155 sprogress = if b.no_cstep {
156 'TMP4/${b.nexpected_steps:4d}'
157 } else {
158 '${b.cstep:4d}/${b.nexpected_steps:4d}'
159 }
160 }
161 return '${label:-5s} [${sprogress}] ${timed_line}'
162 }
163 return '${label:-5s}${timed_line}'
164}
165
166// step_message_with_label returns a string describing the current step using current time as duration.
167pub fn (b &Benchmark) step_message_with_label(label string, msg string) string {
168 return b.step_message_with_label_and_duration(label, msg, b.step_timer.elapsed())
169}
170
171// step_message returns a string describing the current step.
172pub fn (b &Benchmark) step_message(msg string) string {
173 return b.step_message_with_label('', msg)
174}
175
176// step_message_ok returns a string describing the current step with an standard "OK" label.
177pub fn (b &Benchmark) step_message_ok(msg string) string {
178 return b.step_message_with_label(benchmark.b_ok, msg)
179}
180
181// step_message_fail returns a string describing the current step with an standard "FAIL" label.
182pub fn (b &Benchmark) step_message_fail(msg string) string {
183 return b.step_message_with_label(benchmark.b_fail, msg)
184}
185
186// step_message_skip returns a string describing the current step with an standard "SKIP" label.
187pub fn (b &Benchmark) step_message_skip(msg string) string {
188 return b.step_message_with_label(benchmark.b_skip, msg)
189}
190
191// total_message returns a string with total summary of the benchmark run.
192pub fn (b &Benchmark) total_message(msg string) string {
193 the_label := term.colorize(term.gray, msg)
194 mut tmsg := term.colorize(term.bold, 'Summary for ${the_label}:') + ' '
195 if b.nfail > 0 {
196 tmsg += term.colorize(term.bold, term.colorize(term.red, '${b.nfail} failed')) + ', '
197 }
198 if b.nok > 0 {
199 tmsg += term.colorize(term.bold, term.colorize(term.green, '${b.nok} passed')) + ', '
200 }
201 if b.nskip > 0 {
202 tmsg += term.colorize(term.bold, term.colorize(term.yellow, '${b.nskip} skipped')) + ', '
203 }
204 mut njobs_label := ''
205 if b.njobs > 0 {
206 if b.njobs == 1 {
207 njobs_label = ', on ${term.colorize(term.bold, 1.str())} job'
208 } else {
209 njobs_label = ', on ${term.colorize(term.bold, b.njobs.str())} parallel jobs'
210 }
211 }
212 tmsg += '${b.ntotal} total. ${term.colorize(term.bold, 'Runtime:')} ${b.bench_timer.elapsed().microseconds() / 1000} ms${njobs_label}.\n'
213 return tmsg
214}
215
216// total_duration returns the duration in ms.
217pub fn (b &Benchmark) total_duration() i64 {
218 return b.bench_timer.elapsed().milliseconds()
219}
220
221// tdiff_in_ms prefixes `s` with a time difference calculation.
222fn (b &Benchmark) tdiff_in_ms(s string, tdiff i64) string {
223 if b.verbose {
224 return '${f64(tdiff) / 1000.0:9.3f} ms ${s}'
225 }
226 return s
227}