-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathbenchmark.cr
164 lines (155 loc) · 4.75 KB
/
benchmark.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
require "./benchmark/**"
# The Benchmark module provides methods for benchmarking Crystal code, giving
# detailed reports on the time and memory taken for each task.
#
# NOTE: To use `Benchmark`, you must explicitly import it with `require "benchmark"`
#
# ### Measure the number of iterations per second of each task
#
# ```
# require "benchmark"
#
# Benchmark.ips do |x|
# x.report("short sleep") { sleep 10.milliseconds }
# x.report("shorter sleep") { sleep 1.millisecond }
# end
# ```
#
# This generates the following output showing the mean iterations per second,
# the mean times per iteration, the standard deviation relative to the mean, and a comparison:
#
# ```text
# short sleep 88.7 ( 11.27ms) (± 3.33%) 8.90× slower
# shorter sleep 789.7 ( 1.27ms) (± 3.02%) fastest
# ```
#
# `Benchmark::IPS` defaults to 2 seconds of warmup time and 5 seconds of
# calculation time. This can be configured:
#
# ```
# require "benchmark"
#
# Benchmark.ips(warmup: 4, calculation: 10) do |x|
# x.report("sleep") { sleep 10.milliseconds }
# end
# ```
#
# ### Measure the time to construct the string given by the expression: `"a"*1_000_000_000`
#
# ```
# require "benchmark"
#
# puts Benchmark.measure { "a"*1_000_000_000 }
# ```
#
# This generates the following output:
#
# ```text
# 0.190000 0.220000 0.410000 ( 0.420185)
# ```
#
# This report shows the user CPU time, system CPU time, the sum of
# the user and system CPU times, and the elapsed real time. The unit
# of time is seconds.
#
# ### Do some experiments sequentially using the `#bm` method:
#
# ```
# require "benchmark"
#
# n = 5000000
# Benchmark.bm do |x|
# x.report("times:") do
# n.times do
# a = "1"
# end
# end
# x.report("upto:") do
# 1.upto(n) do
# a = "1"
# end
# end
# end
# ```
#
# The result:
#
# ```text
# user system total real
# times: 0.010000 0.000000 0.010000 ( 0.008976)
# upto: 0.010000 0.000000 0.010000 ( 0.010466)
# ```
#
# NOTE: Make sure to always benchmark code by compiling with the `--release` flag.
module Benchmark
extend self
# Main interface of the `Benchmark` module. Yields a `Job` to which
# one can report the benchmarks. See the module's description.
def bm(&)
{% if !flag?(:release) %}
puts "Warning: benchmarking without the `--release` flag won't yield useful results"
{% end %}
report = BM::Job.new
yield report
report.execute
report
end
# Instruction per second interface of the `Benchmark` module. Yields a `Job`
# to which one can report the benchmarks. See the module's description.
#
# The optional parameters *calculation* and *warmup* set the duration of
# those stages. For more detail on these stages see
# `Benchmark::IPS`. When the *interactive* parameter is `true`, results are
# displayed and updated as they are calculated, otherwise all at once after they finished.
def ips(calculation : Time::Span = 5.seconds, warmup : Time::Span = 2.seconds, interactive : Bool = STDOUT.tty?, &)
{% if !flag?(:release) %}
puts "Warning: benchmarking without the `--release` flag won't yield useful results"
{% end %}
job = IPS::Job.new(calculation, warmup, interactive)
yield job
job.execute
job.report
job
end
# Instruction per second interface of the `Benchmark` module. Yields a `Job`
# to which one can report the benchmarks. See the module's description.
#
# The optional parameters *calculation* and *warmup* set the duration of
# those stages in seconds. For more detail on these stages see
# `Benchmark::IPS`. When the *interactive* parameter is `true`, results are
# displayed and updated as they are calculated, otherwise all at once after they finished.
@[Deprecated("Use `#ips(Time::Span, Time::Span, Bool, &)` instead.")]
def ips(calculation = 5, warmup = 2, interactive = STDOUT.tty?, &)
ips(calculation.seconds, warmup.seconds, !!interactive) { |job| yield job }
end
# Returns the time used to execute the given block.
def measure(label = "", &) : BM::Tms
t0, r0 = Process.times, Time.monotonic
yield
t1, r1 = Process.times, Time.monotonic
BM::Tms.new(t1.utime - t0.utime,
t1.stime - t0.stime,
t1.cutime - t0.cutime,
t1.cstime - t0.cstime,
(r1 - r0).total_seconds,
label)
end
# Returns the elapsed real time used to execute the given block.
#
# ```
# Benchmark.realtime { "a" * 100_000 } # => 00:00:00.0005840
# ```
def realtime(&) : Time::Span
Time.measure { yield }
end
# Returns the memory in bytes that the given block consumes.
#
# ```
# Benchmark.memory { Array(Int32).new } # => 32
# ```
def memory(&)
bytes_before_measure = GC.stats.total_bytes
yield
(GC.stats.total_bytes - bytes_before_measure).to_i64
end
end