winr 1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|