winr 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +152 -0
- data/bin/winr +314 -0
- data/test/sum.rb +38 -0
- data/winr.gemspec +14 -0
- metadata +49 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b5b9f7f96814ddc74f0cf997f96355ca43be031355fa06c58de54421ec395ee9
|
4
|
+
data.tar.gz: 073a1747bac59f25a0eaa161fdff7ef491eca92eb917ef36094bd6cf12077f35
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 99584b189c83cc9bac6558a6e379b70f074c59f9e73f95425096d0ad557ef5b239eb2feaad9ad55c90e60b99ae03c9f23d523fb73edc0d0d37a2af2286ae6176
|
7
|
+
data.tar.gz: 4ee9ad210345e670ed3bcae76a5d08ced2dd47a1c1a61fb417d457960c73fc17e89b68a63d6f02ed71a5949b03339b6d62ac812801024b4f7290baa8fcf6528c
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 Steve Shreeve
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
# winr
|
2
|
+
|
3
|
+
A quick and lightweight benchmarking tool for Ruby
|
4
|
+
|
5
|
+
## Goals
|
6
|
+
|
7
|
+
1. Provide a simple way to benchmark code
|
8
|
+
2. Easy to configure and compare results
|
9
|
+
|
10
|
+
## Overview
|
11
|
+
|
12
|
+
There are several tools for benchmarking Ruby code. Unfortunately, it's hard
|
13
|
+
to remember the way to set up the benchmarks, what format to use to represent
|
14
|
+
what to test, and how to perform the testing. One of the most popular tools
|
15
|
+
used for benchmarking Ruby code is `benchmark-driver`, which was used heavily
|
16
|
+
by the Ruby 3x3 team. It uses three levels of abstraction and is powerful,
|
17
|
+
but there is a lot of inconsistency in naming conventions and using the tool.
|
18
|
+
|
19
|
+
The idea of `winr` is to offer an easy to configure benchmarking tool that is
|
20
|
+
easy to use. There are three levels of abstraction:
|
21
|
+
|
22
|
+
* environments - An environment is an optional top-level container for running
|
23
|
+
code. It can represent different versions Ruby, or different setup options
|
24
|
+
that will be shared by any nested contexts or tasks.
|
25
|
+
* contexts - A context represents an optional mid-level container for running
|
26
|
+
code. Any context configuration is shared by any contained tasks.
|
27
|
+
* tasks - A task represents a unit of work to be tested, within its optional
|
28
|
+
containing context and optional containing environment.
|
29
|
+
|
30
|
+
Within an environment, context, or task, the following can be defined:
|
31
|
+
|
32
|
+
* name - The name of the element, to use when displaying output
|
33
|
+
* begin - Code that should be run before any contained elements
|
34
|
+
* end - Code that should run after any contained elements
|
35
|
+
* script - Code that is contained within a task
|
36
|
+
* loops - The number of times that a task's script should be executed
|
37
|
+
* warmup - An optional top-level setting that is used when the number of
|
38
|
+
loops has not been defined. This value is the number of seconds to run
|
39
|
+
the task before determining the number of loops that have run. The
|
40
|
+
default value is 3 seconds, meaning that the number of loops is not
|
41
|
+
defined, the task will be allowed to run for 3 seconds and the number
|
42
|
+
of iterations during this time will be used.
|
43
|
+
|
44
|
+
## Examples
|
45
|
+
|
46
|
+
Configuration information is stored in a Ruby file, like the following:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
$ cat test/sum.rb
|
50
|
+
|
51
|
+
{
|
52
|
+
environments: [
|
53
|
+
{
|
54
|
+
name: "Shiny new Ruby",
|
55
|
+
command: "/opt/ruby-3.2.0/bin/ruby",
|
56
|
+
},
|
57
|
+
{
|
58
|
+
name: "Old trusted Ruby",
|
59
|
+
command: "/opt/ruby-2.7.6/bin/ruby",
|
60
|
+
},
|
61
|
+
],
|
62
|
+
contexts: [
|
63
|
+
{
|
64
|
+
begin: <<~"|",
|
65
|
+
require "digest/md5"
|
66
|
+
|
67
|
+
max = 1e5.to_i
|
68
|
+
|
|
69
|
+
},
|
70
|
+
],
|
71
|
+
tasks: [
|
72
|
+
{
|
73
|
+
name: "array splat",
|
74
|
+
script: <<~"|",
|
75
|
+
ary = [*1..max]
|
76
|
+
sum = ary.sum
|
77
|
+
|
|
78
|
+
},
|
79
|
+
{
|
80
|
+
name: "times",
|
81
|
+
script: <<~"|",
|
82
|
+
sum = 0
|
83
|
+
max.to_i.times {|n| sum += n }
|
84
|
+
|
|
85
|
+
},
|
86
|
+
],
|
87
|
+
warmup: 3,
|
88
|
+
}
|
89
|
+
```
|
90
|
+
|
91
|
+
The benchmark is run as follows:
|
92
|
+
|
93
|
+
```
|
94
|
+
$ winr test/sum.rb
|
95
|
+
|
96
|
+
┌──────────────────┬───────────────────────────────────────┐
|
97
|
+
│ Shiny new Ruby │ Results │
|
98
|
+
├──────────────────┼───────────┬─────────────┬─────────────┤
|
99
|
+
│ array splat │ 2.99 s │ 768.19 i/s │ 1.30 ms/i │
|
100
|
+
│ times │ 7.86 s │ 292.33 i/s │ 3.42 ms/i │
|
101
|
+
└──────────────────┴───────────┴─────────────┴─────────────┘
|
102
|
+
|
103
|
+
┌──────────────────┬───────────────────────────────────────┐
|
104
|
+
│ Old trusted Ruby │ Results │
|
105
|
+
├──────────────────┼───────────┬─────────────┬─────────────┤
|
106
|
+
│ array splat │ 2.96 s │ 777.46 i/s │ 1.29 ms/i │
|
107
|
+
│ times │ 7.73 s │ 297.19 i/s │ 3.36 ms/i │
|
108
|
+
└──────────────────┴───────────┴─────────────┴─────────────┘
|
109
|
+
|
110
|
+
┌──────────────────┬─────────────────────────────────────┐
|
111
|
+
│ Rank │ Performance │
|
112
|
+
├──────────────────┼─────────────┬──────────────┬────────┤
|
113
|
+
│ array splat │ 777.46 i/s │ fastest │ 2/1/1 │
|
114
|
+
│ array splat │ 768.19 i/s │ 1.01x slower │ 1/1/1 │
|
115
|
+
│ times │ 297.19 i/s │ 2.62x slower │ 2/1/2 │
|
116
|
+
│ times │ 292.33 i/s │ 2.66x slower │ 1/1/2 │
|
117
|
+
└──────────────────┴─────────────┴──────────────┴────────┘
|
118
|
+
```
|
119
|
+
|
120
|
+
## Install
|
121
|
+
|
122
|
+
Install via `rubygems` with:
|
123
|
+
|
124
|
+
```
|
125
|
+
gem install winr
|
126
|
+
```
|
127
|
+
|
128
|
+
## Options
|
129
|
+
|
130
|
+
```
|
131
|
+
$ winr -h
|
132
|
+
|
133
|
+
usage: winr [options] <dir ...>
|
134
|
+
-c, --[no-]color Enable color output (default is true)
|
135
|
+
-d, --debug Enable debug mode
|
136
|
+
-h, --help Show help and command usage
|
137
|
+
-i, --iterations <count> Force the number of iterations for each task
|
138
|
+
-r, --reverse Show contexts vertically and tasks horizontally
|
139
|
+
-s, --stats Comma-separated list of stats (loops, time, ips, spi)
|
140
|
+
-v, --verbose Show command, version details, and markdown backticks
|
141
|
+
|
142
|
+
Available statistics:
|
143
|
+
|
144
|
+
ips iterations per second
|
145
|
+
loops number of iterations
|
146
|
+
spi seconds for iteration
|
147
|
+
time time to run all iterations
|
148
|
+
```
|
149
|
+
|
150
|
+
## License
|
151
|
+
|
152
|
+
This software is licensed under terms of the MIT License.
|
data/bin/winr
ADDED
@@ -0,0 +1,314 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# ============================================================================
|
4
|
+
# winr - A quick and lightweight benchmarking tool for Ruby
|
5
|
+
#
|
6
|
+
# Author: Steve Shreeve (steve.shreeve@gmail.com)
|
7
|
+
# Date: Feb 14, 2023
|
8
|
+
# ============================================================================
|
9
|
+
# GOALS:
|
10
|
+
# 1. Provide a simple way to benchmark code
|
11
|
+
# 2. Easy to configure and compare results
|
12
|
+
# 3. Accurately measure times, see http://bit.ly/3ltE7MP
|
13
|
+
#
|
14
|
+
# TODO:
|
15
|
+
# 1. Enable YAML config files
|
16
|
+
# ============================================================================
|
17
|
+
|
18
|
+
trap("INT" ) { abort "\n" }
|
19
|
+
|
20
|
+
require "erb"
|
21
|
+
require "optparse"
|
22
|
+
require "shellwords"
|
23
|
+
require "tempfile"
|
24
|
+
|
25
|
+
OptionParser.new.instance_eval do
|
26
|
+
@banner = "usage: #{program_name} [options] <dir ...>"
|
27
|
+
|
28
|
+
on "-c" , "--[no-]color", "Enable color output (default is true)", TrueClass
|
29
|
+
on "-d" , "--debug" , "Enable debug mode", TrueClass
|
30
|
+
on "-h" , "--help" , "Show help and command usage" do Kernel.abort to_s; end
|
31
|
+
on "-i <count>" , "--iterations", "Force the number of iterations for each task", Integer
|
32
|
+
on "-r" , "--reverse" , "Show contexts vertically and tasks horizontally", TrueClass
|
33
|
+
on "-s <time,ips,spi>", "--stats " , "Comma-separated list of stats (loops, time, ips, spi)"
|
34
|
+
on "-v" , "--verbose" , "Show command, version details, and markdown backticks", TrueClass
|
35
|
+
|
36
|
+
separator <<~"end"
|
37
|
+
|
38
|
+
Available statistics:
|
39
|
+
|
40
|
+
ips iterations per second
|
41
|
+
loops number of iterations
|
42
|
+
spi seconds for iteration
|
43
|
+
time time to run all iterations
|
44
|
+
end
|
45
|
+
|
46
|
+
self
|
47
|
+
end.parse!(into: opts={}) rescue abort($!.message)
|
48
|
+
|
49
|
+
# option munging
|
50
|
+
ansi = opts[:color]; ansi = true if ansi.nil?
|
51
|
+
hack = opts[:debug]
|
52
|
+
hush = !opts[:verbose]
|
53
|
+
runs = opts[:iterations]; abort "invalid number of runs" if runs && runs < 1
|
54
|
+
show = opts[:stats] || "time,ips,spi"
|
55
|
+
show = show.downcase.scan(/[a-z]+/i).uniq & %w[ ips loops spi time ]
|
56
|
+
swap = !opts[:reverse]
|
57
|
+
|
58
|
+
# option errors
|
59
|
+
show.empty? and abort "invalid list of statistics #{opts[:stats].inspect}"
|
60
|
+
|
61
|
+
# ==[ Define some constants, ansi codes, and make hashes more flexible ]==
|
62
|
+
|
63
|
+
Infinity = 1.0 / 0
|
64
|
+
Overflow = "\n\nERROR: numeric overflow"
|
65
|
+
|
66
|
+
module Ansi
|
67
|
+
refine String do
|
68
|
+
$ansi = <<~"".scan(/(\d+)(?:\/(\d+))?=(\w+)/).inject({}) do |ansi, (code, undo, name)|
|
69
|
+
0=reset 1/22=bold 2/22=dim 3/23=italic 4/24=under 5/25=blink 7/27=inverse 9/29=strike
|
70
|
+
30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white 39=default
|
71
|
+
40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white 49=default
|
72
|
+
|
73
|
+
ansi[name + "_off" ] = undo if undo
|
74
|
+
ansi[name + (code[0,2] =~ /4\d/ ? "_bg" : "")] = code
|
75
|
+
ansi
|
76
|
+
end
|
77
|
+
def ansi (*list); list.map {|code| "\e[#{$ansi[code.to_s] || 0}m"}.join + self; end
|
78
|
+
def ansi!(*list); ansi(*list) + "\e[0m"; end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
using Ansi
|
83
|
+
|
84
|
+
module FlexHash
|
85
|
+
refine Hash do
|
86
|
+
alias_method :default_lookup, :[]
|
87
|
+
|
88
|
+
def [](key, miss=nil) # method_missing calls this with key as a symbol
|
89
|
+
key?(key) and return default_lookup(key) || miss
|
90
|
+
|
91
|
+
ary = key.to_s.split(/(?:[.\/\[]|\][.\/]?)/)
|
92
|
+
val = ary.inject(self) do |obj, sub|
|
93
|
+
if obj == self then default_lookup(sub.to_sym)
|
94
|
+
elsif obj == nil then break
|
95
|
+
elsif sub =~ /\A-?\d*\z/ then obj[sub.to_i]
|
96
|
+
else obj[sub.to_sym]
|
97
|
+
end
|
98
|
+
end or miss
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
using FlexHash
|
104
|
+
|
105
|
+
class Hash
|
106
|
+
def method_missing(name, *args)
|
107
|
+
name =~ /=$/ ? send(:[]=, $`.to_sym, *args) : send(:[], name, *args)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# ==[ Templates ]==
|
112
|
+
|
113
|
+
def compile(task, path)
|
114
|
+
<<~"|".strip
|
115
|
+
#{ task.begin }
|
116
|
+
|
117
|
+
# number of loops requested
|
118
|
+
__winr_iters = #{ task.loops.to_i }
|
119
|
+
|
120
|
+
# calculate loops if not supplied
|
121
|
+
if __winr_iters == 0
|
122
|
+
__winr_until = __winr_timer + #{ $config.warmup(3) }
|
123
|
+
while __winr_timer < __winr_until
|
124
|
+
#{ task.script&.strip }
|
125
|
+
__winr_iters += 1
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# calculate time wasted on loop overhead
|
130
|
+
__winr_waste = 0
|
131
|
+
__winr_loops = 0
|
132
|
+
__winr_begin = __winr_timer
|
133
|
+
while __winr_loops < __winr_iters
|
134
|
+
__winr_loops += 1
|
135
|
+
end
|
136
|
+
__winr_waste = __winr_timer - __winr_begin
|
137
|
+
|
138
|
+
# calculate time spent running our task
|
139
|
+
__winr_loops = 0
|
140
|
+
__winr_begin = __winr_timer
|
141
|
+
while __winr_loops < __winr_iters
|
142
|
+
#{ task.script&.strip }
|
143
|
+
__winr_loops += 1
|
144
|
+
end
|
145
|
+
__winr_delay = __winr_timer - __winr_begin
|
146
|
+
|
147
|
+
File.write(#{ path.inspect }, [__winr_loops, __winr_delay - __winr_waste].inspect)
|
148
|
+
|
149
|
+
#{ task.end }
|
150
|
+
|
|
151
|
+
end
|
152
|
+
|
153
|
+
# ==[ Helpers ]==
|
154
|
+
|
155
|
+
def boxlines(main, cols, runs=1)
|
156
|
+
[ "┌┬──┐",
|
157
|
+
"├┼┬─┤",
|
158
|
+
"└┴┴─┘" ]
|
159
|
+
.map do |str|
|
160
|
+
list = [main, *(cols * runs)]
|
161
|
+
list.map.with_index do |col, i|
|
162
|
+
chr = str[i < 2 ? i : (i - 1) % cols.size == 0 ? 1 : 2]
|
163
|
+
chr + str[3] * (col + 2)
|
164
|
+
end.join + str[-1]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def execute(command, path)
|
169
|
+
IO.popen(["ruby", path].join(" "), &:read)
|
170
|
+
$?.success? or raise
|
171
|
+
eval(File.read(path))
|
172
|
+
end
|
173
|
+
|
174
|
+
def scale(show, unit)
|
175
|
+
slot = 3
|
176
|
+
span = ["G", "M", "K", " ", "m", "µ", "p"]
|
177
|
+
[0, Infinity].include?(show) and abort Overflow
|
178
|
+
show *= 1000.0 and slot += 1 while show > 0 && show < 1.0
|
179
|
+
show /= 1000.0 and slot -= 1 while show >= 1000.0
|
180
|
+
slot.between?(0, 6) or abort Overflow
|
181
|
+
"%6.2f %s%s" % [show, span[slot], unit]
|
182
|
+
end
|
183
|
+
|
184
|
+
def stats(list, scope=nil)
|
185
|
+
list.map do |item|
|
186
|
+
pair = case item
|
187
|
+
when "loops" then ["runs" , "times"]
|
188
|
+
when "time" then ["time" , "s" ]
|
189
|
+
when "ips" then ["runs/time", "i/s" ]
|
190
|
+
when "spi" then ["time/runs", "s/i" ]
|
191
|
+
else abort "unknown statistic #{item.inspect}"
|
192
|
+
end
|
193
|
+
scope ? eval(pair[0], scope) : pair[1]
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def write(file, data)
|
198
|
+
file.puts(data)
|
199
|
+
file.close
|
200
|
+
yield file.path
|
201
|
+
end
|
202
|
+
|
203
|
+
# ==[ Workflow ]==
|
204
|
+
|
205
|
+
# read the winr script
|
206
|
+
winr = ARGV.first or abort "missing winr script"
|
207
|
+
tmpl = ERB.new(DATA.read)
|
208
|
+
|
209
|
+
# grok the config
|
210
|
+
$config = eval(File.read(winr))
|
211
|
+
es = $config.environments || [{}]
|
212
|
+
cs = $config.contexts || [{}]
|
213
|
+
ts = $config.tasks || [{}]
|
214
|
+
|
215
|
+
# box drawing
|
216
|
+
cols = stats(show)
|
217
|
+
full = cols.map(&:size).sum + cols.size * 11 - 3
|
218
|
+
wide = [*es.map {|e| e.name("").size}, *ts.map {|t| t.name("").size}].max
|
219
|
+
rank = []
|
220
|
+
|
221
|
+
# row: top, middle, bottom
|
222
|
+
rt, rm, rb = boxlines(wide, cols.map {|e| e.size + 8 }, (swap ? cs : ts).size)
|
223
|
+
|
224
|
+
# begin output
|
225
|
+
puts "```", [$0, *ARGV].shelljoin, "" unless hush
|
226
|
+
|
227
|
+
# loop over environment(s)
|
228
|
+
es.each_with_index do |e, ei|
|
229
|
+
puts IO.popen(["ruby", "-v"].join(" "), &:read) unless hush
|
230
|
+
puts rt
|
231
|
+
|
232
|
+
command = ["/usr/bin/env ruby"]
|
233
|
+
|
234
|
+
# loop over context(s) and task(s)
|
235
|
+
ys, xs = swap ? [ts, cs] : [cs, ts]
|
236
|
+
|
237
|
+
# row: content, header
|
238
|
+
rc = "Task" # or "Context"
|
239
|
+
rh = "│ %-*.*s │" % [wide, wide, e.name(es.size > 1 ? "Env ##{ei + 1}" : rc)]
|
240
|
+
rh = xs.inject(rh) {|s, x| s << " %-*.*s │" % [full, full, x.name("Results").center(full)] }
|
241
|
+
puts rh, rm
|
242
|
+
|
243
|
+
ys.each_with_index do |y, yi|
|
244
|
+
print "│ %-*.*s │" % [wide, wide, y.name("Results")]
|
245
|
+
xs.each_with_index do |x, xi|
|
246
|
+
t, ti, c, ci = swap ? [y, yi, x, xi] : [x, xi, y, yi]
|
247
|
+
delay = Tempfile.open(['winr-', '.rb']) do |file|
|
248
|
+
t.loops = runs if runs
|
249
|
+
code = tmpl.result(binding).rstrip + "\n"
|
250
|
+
write(file, code) do |path|
|
251
|
+
runs, time = execute(command, path)
|
252
|
+
t.loops ||= runs
|
253
|
+
vals = stats(show, binding)
|
254
|
+
rank << [runs/time, ei, ci, ti]
|
255
|
+
print vals.zip(cols).map {|pair| " %s │" % scale(*pair) }.join
|
256
|
+
end
|
257
|
+
puts "", code, "=" * 78 if hack
|
258
|
+
end
|
259
|
+
end
|
260
|
+
print "\n"
|
261
|
+
end
|
262
|
+
puts rb, ""
|
263
|
+
end
|
264
|
+
|
265
|
+
# show the comparison
|
266
|
+
rank.sort! {|a, b| b[0] <=> a[0] }
|
267
|
+
fast = rank.first[0]
|
268
|
+
slow = rank.last[0]
|
269
|
+
pict = "%.2fx slower"
|
270
|
+
room = (pict % [fast / slow]).size
|
271
|
+
cols = [11, room, 6]
|
272
|
+
cols.pop if (es.size == 1) && (cs.size == 1)
|
273
|
+
full = cols.sum + (cols.size - 1) * 3
|
274
|
+
rt, rm, rb = boxlines(wide, cols)
|
275
|
+
rh = "│ %-*.*s │" % [wide, wide, "Rank"]
|
276
|
+
rh << " %-*.*s │" % [full, full, "Performance".center(full)]
|
277
|
+
|
278
|
+
puts rt, rh, rm
|
279
|
+
flip = cs.size > 1 && ts.size == 1
|
280
|
+
rank.each do |ips, ei, ci, ti|
|
281
|
+
name = (flip ? cs[ci] : ts[ti]).name
|
282
|
+
print "│ %-*.*s │ %s │ " % [wide, wide, name, scale(ips, "i/s")]
|
283
|
+
if ips.round(2) == fast.round(2)
|
284
|
+
text = "fastest".center(room)
|
285
|
+
print ansi ? text.ansi!(:green, :bold) : text
|
286
|
+
else
|
287
|
+
print "%*.*s" % [room, room, pict % [fast/ips]]
|
288
|
+
end
|
289
|
+
print " │ %-6s" % ([ei+1,ci+1,ti+1] * "/") if cols.size > 2
|
290
|
+
print " │\n"
|
291
|
+
end
|
292
|
+
puts rb
|
293
|
+
puts "```" unless hush
|
294
|
+
|
295
|
+
__END__
|
296
|
+
|
297
|
+
# ============================================================================
|
298
|
+
# Environment <%= ei + 1 %>: <%= e.name %>
|
299
|
+
# Context <%= ci + 1 %>: <%= c.name %>
|
300
|
+
# Task <%= ti + 1 %>: <%= t.name %>
|
301
|
+
# ============================================================================
|
302
|
+
|
303
|
+
trap("INT") { exit }
|
304
|
+
|
305
|
+
# def __winr_timer; Process.clock_gettime(Process::CLOCK_MONOTONIC); end
|
306
|
+
def __winr_timer; Time.now.to_f; end
|
307
|
+
|
308
|
+
<%= e.begin %>
|
309
|
+
<%= c.begin %>
|
310
|
+
|
311
|
+
<%= compile(t, file.path) %>
|
312
|
+
|
313
|
+
<%= c.end %>
|
314
|
+
<%= e.end %>
|
data/test/sum.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
{
|
2
|
+
environments: [
|
3
|
+
{
|
4
|
+
name: "Shiny new Ruby",
|
5
|
+
command: "/opt/ruby-3.2.0/bin/ruby",
|
6
|
+
},
|
7
|
+
{
|
8
|
+
name: "Old trusted Ruby",
|
9
|
+
command: "/opt/ruby-2.7.6/bin/ruby",
|
10
|
+
},
|
11
|
+
],
|
12
|
+
contexts: [
|
13
|
+
{
|
14
|
+
begin: <<~"|",
|
15
|
+
require "digest/md5"
|
16
|
+
|
17
|
+
max = 1e5.to_i
|
18
|
+
|
|
19
|
+
},
|
20
|
+
],
|
21
|
+
tasks: [
|
22
|
+
{
|
23
|
+
name: "array splat",
|
24
|
+
script: <<~"|",
|
25
|
+
ary = [*1..max]
|
26
|
+
sum = ary.sum
|
27
|
+
|
|
28
|
+
},
|
29
|
+
{
|
30
|
+
name: "times",
|
31
|
+
script: <<~"|",
|
32
|
+
sum = 0
|
33
|
+
max.to_i.times {|n| sum += n }
|
34
|
+
|
|
35
|
+
},
|
36
|
+
],
|
37
|
+
warmup: 3,
|
38
|
+
}
|
data/winr.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "winr"
|
5
|
+
s.version = "1.0"
|
6
|
+
s.author = "Steve Shreeve"
|
7
|
+
s.email = "steve.shreeve@gmail.com"
|
8
|
+
s.summary =
|
9
|
+
s.description = "A quick and lightweight benchmarking tool for Ruby"
|
10
|
+
s.homepage = "https://github.com/shreeve/winr"
|
11
|
+
s.license = "MIT"
|
12
|
+
s.files = `git ls-files`.split("\n") - %w[.gitignore]
|
13
|
+
s.executables = `cd bin && git ls-files .`.split("\n")
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: winr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Steve Shreeve
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-02-14 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A quick and lightweight benchmarking tool for Ruby
|
14
|
+
email: steve.shreeve@gmail.com
|
15
|
+
executables:
|
16
|
+
- winr
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- Gemfile
|
21
|
+
- LICENSE
|
22
|
+
- README.md
|
23
|
+
- bin/winr
|
24
|
+
- test/sum.rb
|
25
|
+
- winr.gemspec
|
26
|
+
homepage: https://github.com/shreeve/winr
|
27
|
+
licenses:
|
28
|
+
- MIT
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubygems_version: 3.4.6
|
46
|
+
signing_key:
|
47
|
+
specification_version: 4
|
48
|
+
summary: A quick and lightweight benchmarking tool for Ruby
|
49
|
+
test_files: []
|