forked from crystal-lang/crystal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
benchmark.cr
137 lines (130 loc) · 3.62 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
require "./benchmark/**"
# The Benchmark module provides methods for benchmarking Crystal code, giving
# detailed reports on the time taken for each task.
#
# ### Measure the number of iterations per second of each task
#
# ```
# require "benchmark"
#
# Benchmark.ips do |x|
# x.report("short sleep") { sleep 0.01 }
# x.report("shorter sleep") { sleep 0.001 }
# end
# ```
#
# This generates the following output showing the mean iterations per second,
# the standard deviation relative to the mean, and a comparison:
#
# ```text
# short sleep 91.82 (± 2.51%) 8.72× slower
# shorter sleep 800.98 (± 1.10%) fastest
# ```
#
# `Benchmark::IPS` defaults to 2 seconds of warmup time and 5 seconds of
# calculation time. This can be configured:
#
# ```
# Benchmark.ips(warmup: 4, calculation: 10) do |x|
# x.report("sleep") { sleep 0.01 }
# end
# ```
#
# Make sure to always benchmark code by compiling with the `--release` flag.
#
# ### 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:") { n.times do
# a = "1"
# end }
# x.report("upto:") { 1.upto(n) do
# a = "1"
# 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)
# ```
#
# 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 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.
def ips(calculation = 5, warmup = 2, interactive = 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
# Returns the time used to execute the given block.
def measure(label = "") : BM::Tms
t0, r0 = Process.times, Time.now
yield
t1, r1 = Process.times, Time.now
BM::Tms.new(t1.utime - t0.utime,
t1.stime - t0.stime,
t1.cutime - t0.cutime,
t1.cstime - t0.cstime,
(r1.ticks - r0.ticks).to_f / Time::Span::TicksPerSecond,
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
r0 = Time.now
yield
Time.now - r0
end
end