time_up 0.0.3 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b040cfa422a59cc1c7f2157e8f7261ed1bdc7c470da76fead75b026159f02c8
4
- data.tar.gz: 4b817578713714392dd0ab8f2694bce78c6ddf93e9b5eea16f302a5d4549c559
3
+ metadata.gz: c18c0412768d6e182c94ef02f66e630508f5c959f433d1d1ecf322ce16107c3a
4
+ data.tar.gz: c18ce6ae92e02cd52ddad357d8f542185869d8e6fa5cebf090ee3bc4b576a44c
5
5
  SHA512:
6
- metadata.gz: 3b4fe6bff25861a0b9b76b95684e25eed9056886091556f22b70820251ea3f41b08879b2628dd8b8bee74e03720b0f39d7af87af5664c0bcaa152f7e8b88d329
7
- data.tar.gz: e0b0953129fc39ed079d3b6addebf579d8defbebcda484649a9826ff0a3e3b2383360d7d8269572746b5f5469cb076d4e6fa770ed36735f1c2fe55f319c91c5b
6
+ metadata.gz: 8a73b80a2193a5df7ce3bae010b109c3e5c34de5329ca34c75cc6d7b296aae395c81ed1488a64e932069c7da8a496ea175d81f6ae4defe214445760c98a01551
7
+ data.tar.gz: 610cc5554be65e278b2187c56e0d2ebc382b5c916a2e666c39ba84da9ede77bc618ff3c4d58d2df22030bf9a8ad049cbd3eb45827bd0a7c7419e46d8166035f4
data/.standard.yml ADDED
@@ -0,0 +1 @@
1
+ ruby_version: 2.4
data/CHANGELOG.md CHANGED
@@ -1,3 +1,16 @@
1
+ # 0.0.6
2
+
3
+ - Add `TimeUp.all_timers`. It's weird that it's not a thing.
4
+
5
+ # 0.0.5
6
+
7
+ - Add `median` and `percentile` timer statistics, and added them to
8
+ `print_detailed_summary`
9
+
10
+ # 0.0.4
11
+
12
+ - Add `TimeUp.print_detailed_summary`
13
+
1
14
  # 0.0.3
2
15
 
3
16
  - Change the return value of TimeUp.start when passed a block to be the
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- time_up (0.0.3)
4
+ time_up (0.0.7)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -11,6 +11,9 @@ process and that you want to measure in aggregate. (For example, to see how
11
11
  much time your test suite spends creating factories, truncating the database, or
12
12
  invoking a critical code path.)
13
13
 
14
+ Here's a [blog post about time_up](https://blog.testdouble.com/posts/2021-07-19-benchmarking-your-ruby-with-time_up/) and
15
+ a [great example of when it can be useful](https://gist.github.com/searls/feee0b0eac7c329b390fed90c4714afb).
16
+
14
17
  ## Install
15
18
 
16
19
  Just run `gem install time_up` or add time_up to your Gemfile:
@@ -107,7 +110,7 @@ TimeUp.print_summary
107
110
  Which will output something like:
108
111
 
109
112
  ```
110
- TimeUp timers summary
113
+ TimeUp summary
111
114
  ========================
112
115
  :roast 0.07267s
113
116
  :veggies 0.03760s
@@ -117,10 +120,27 @@ TimeUp timers summary
117
120
  * Denotes that the timer is still active
118
121
  ```
119
122
 
123
+ And if you're calling the timers multiple times and want to see some basic
124
+ statistics in the print-out, you can call `TimeUp.print_detailed_summary`, which
125
+ will produce this:
126
+
127
+ ```
128
+ =============================================================================
129
+ Name | Elapsed | Count | Min | Max | Mean | Median | 95th %
130
+ -----------------------------------------------------------------------------
131
+ :roast | 0.08454 | 3 | 0.00128 | 0.07280 | 0.02818 | 0.01046 | 0.06657
132
+ :veggies | 0.03779 | 1 | 0.03779 | 0.03779 | 0.03779 | 0.03779 | 0.03779
133
+ :pasta | 0.01260 | 11 | 0.00000 | 0.01258 | 0.00115 | 0.00000 | 0.00630
134
+ :souffle* | 0.00024 | 1 | 0.00024 | 0.00025 | 0.00025 | 0.00025 | 0.00026
135
+
136
+ * Denotes that the timer is still active
137
+ ```
138
+
120
139
  ## API
121
140
 
122
141
  This gem defines a bunch of public methods but they're all pretty short and
123
- straightforward, so I'd encourage you to [read the code](/lib/time_up.rb).
142
+ straightforward, so when in doubt, I'd encourage you to [read the
143
+ code](/lib/time_up.rb).
124
144
 
125
145
  ### `TimeUp` module
126
146
 
@@ -128,51 +148,60 @@ straightforward, so I'd encourage you to [read the code](/lib/time_up.rb).
128
148
  if it doesn't exist)
129
149
 
130
150
  `TimeUp.start(name, [&blk])` - Starts (or restarts) a named
131
- [Timer](#timeuptimer-class). If passed with a block, will return whatever the
132
- block evaluates to. If passed without a block, it will return the timer object
151
+ [Timer](#timeuptimer-class). If passed a block, will return whatever the block
152
+ evaluates to. If called without a block, it will return the timer object
133
153
 
134
- `TimeUp.stop(name)` - Stops the named timer or raises if it's not defined
154
+ `TimeUp.stop(name)` - Stops the named timer
135
155
 
136
156
  `TimeUp.reset(name)` - Resets the named timer's elapsed time to 0, effectively
137
- restarting it if it's currently running. Raises if the timer isn't defined.
157
+ restarting it if it's currently running
138
158
 
139
159
  `TimeUp.elapsed(name)` - Returns a `Float` of the total elapsed seconds that the
140
- named timer has been running (and raises if no timer is defined with the given
141
- `name`)
160
+ named timer has been running
142
161
 
143
162
  `TimeUp.timings(name)` - Returns an array of each recorded start-to-stop
144
- duration of the timer (including the current one, if active)
163
+ duration (including the current one, if the timer is running) of the named timer
164
+
165
+ `TimeUp.count(name)` - The number of times the timer has been started (including
166
+ the current timing, if the timer is running)
167
+
168
+ `TimeUp.min(name)` - The shortest recording by the timer
145
169
 
146
- `TimeUp.count(name)` - The number of times the timer has been started and
147
- stopped (including the current timing, if active)
170
+ `TimeUp.max(name)` - The longest recording by the timer
148
171
 
149
- `TimeUp.min(name)` - The shortest recording of the timer (including the current
150
- one, if active)
172
+ `TimeUp.mean(name)` - The arithmetic mean of all recordings by the timer
151
173
 
152
- `TimeUp.max(name)` - The longest recording of the timer (including the current
153
- one, if active)
174
+ `TimeUp.median(name)` - The median of all recordings by the timer
154
175
 
155
- `TimeUp.mean(name)` - The arithmetic mean of all recorded durations of the timer
176
+ `TimeUp.percentile(name, percent)` - The timing for the given
177
+ [percentile](https://en.wikipedia.org/wiki/Percentile) of all recordings by the
178
+ timer
156
179
 
157
- `TimeUp.total_elapsed` - Returns a `Float` of the sum of `elapsed` for all the
158
- timers you've created
180
+ `TimeUp.total_elapsed` - Returns a `Float` of the sum of `elapsed` across all
181
+ the timers you've created (note that because you can easily run multiple logical
182
+ timers simultaneously, this figure may exceed the total time spent by the
183
+ computer)
159
184
 
160
- `TimeUp.all_elapsed` - Returns a hash of timer name keys mapped to their
161
- `elapsed` values. Handy for grabbing a snapshot of the state of things at a
162
- particular point in time without stopping all your timers
185
+ `TimeUp.all_elapsed` - Returns a Hash of timer name keys mapped to their
186
+ `elapsed` values. Handy for grabbing a reference to a snapshot of the state of
187
+ things without requiring you to stop your timers
163
188
 
164
- `TimeUp.all_stats` - Returns a hash of timer name keys mapped to another
165
- hash of their basic statistics (elapsed time, number of recordings, min, max,
166
- and mean)
189
+ `TimeUp.all_stats` - Returns a Hash of timer name keys mapped to another
190
+ hash of their basic statistics (`elapsed`, `count`, `min`, `max`,
191
+ and `mean`)
167
192
 
168
- `TimeUp.active_timers` - Returns an array of all timers that are currently
169
- running. Useful for detecting cases where you might be counting the same time in
170
- multiple places simultaneously
193
+ `TimeUp.active_timers` - Returns an Array of all timers that are currently
194
+ running. Useful for detecting cases where you might be keeping time in multiple
195
+ places simultaneously
171
196
 
172
197
  `TimeUp.print_summary([io])` - Pretty-prints a multi-line summary of all your
173
- timers to standard output (or the provided
198
+ timers' total elapsed times to standard output (or the provided
174
199
  [IO](https://ruby-doc.org/core-3.0.1/IO.html))
175
200
 
201
+ `TimeUp.print_detailed_summary([io])` - Pretty-prints a multi-line summary of
202
+ all your timers' elapsed times and basic statistics to standard output (or the
203
+ provided [IO](https://ruby-doc.org/core-3.0.1/IO.html))
204
+
176
205
  `TimeUp.stop_all` - Stops all timers
177
206
 
178
207
  `TimeUp.reset_all` - Resets all timers
@@ -188,20 +217,23 @@ reference to them
188
217
 
189
218
  `elapsed` - A `Float` of the total elapsed seconds the timer has been running
190
219
 
191
- `timings` - Returns an array of each recorded start-to-stop duration of the
192
- timer (including the current one, if active)
220
+ `timings` - Returns an Array of each recorded start-to-stop duration of the
221
+ timer (including the current one, if the timer is running)
193
222
 
194
- `count` - The number of times the timer has been started and stopped (including
195
- the current timing, if active)
223
+ `count` - The number of times the timer has been started and stopped
196
224
 
197
- `min` - The shortest recording of the timer (including the current one, if
198
- active)
225
+ `min` - The shortest recording of the timer
199
226
 
200
- `max` - The longest recording of the timer (including the current one, if
201
- active)
227
+ `max` - The longest recording of the timer
202
228
 
203
229
  `mean` - The arithmetic mean of all recorded durations of the timer
204
230
 
231
+ `median(name)` - The median of all recordings by the timer
232
+
233
+ `percentile(name, percent)` - The timing for the given
234
+ [percentile](https://en.wikipedia.org/wiki/Percentile) of all recordings by the
235
+ timer
236
+
205
237
  `active?` - Returns `true` if the timer is running
206
238
 
207
239
  `reset(force: false)` - Resets the timer to 0 elapsed seconds. If `force` is
data/lib/time_up.rb CHANGED
@@ -22,7 +22,8 @@ module TimeUp
22
22
  :count,
23
23
  :min,
24
24
  :max,
25
- :mean
25
+ :mean,
26
+ :median
26
27
  ].each do |method_name|
27
28
  define_singleton_method method_name do |name|
28
29
  __ensure_timer(name)
@@ -30,6 +31,11 @@ module TimeUp
30
31
  end
31
32
  end
32
33
 
34
+ def self.percentile(name, percentage)
35
+ __ensure_timer(name)
36
+ __timers[name].percentile(percentage)
37
+ end
38
+
33
39
  # Interrogative methods
34
40
  def self.total_elapsed
35
41
  __timers.values.sum(&:elapsed)
@@ -41,6 +47,10 @@ module TimeUp
41
47
  }.to_h
42
48
  end
43
49
 
50
+ def self.all_timers
51
+ __timers.values
52
+ end
53
+
44
54
  def self.all_stats
45
55
  __timers.values.map { |timer|
46
56
  [timer.name, {
@@ -48,7 +58,9 @@ module TimeUp
48
58
  count: timer.count,
49
59
  min: timer.min,
50
60
  max: timer.max,
51
- mean: timer.mean
61
+ mean: timer.mean,
62
+ median: timer.median,
63
+ "95th": timer.percentile(95)
52
64
  }]
53
65
  }.to_h
54
66
  end
@@ -65,7 +77,7 @@ module TimeUp
65
77
  }
66
78
  io.puts <<~SUMMARY
67
79
 
68
- TimeUp timers summary
80
+ TimeUp summary
69
81
  ========================
70
82
  #{summaries.join("\n")}
71
83
 
@@ -73,6 +85,56 @@ module TimeUp
73
85
  SUMMARY
74
86
  end
75
87
 
88
+ def self.print_detailed_summary(io = $stdout)
89
+ cols = {
90
+ names: ["Name"],
91
+ elapsed: ["Elapsed"],
92
+ count: ["Count"],
93
+ min: ["Min"],
94
+ max: ["Max"],
95
+ mean: ["Mean"],
96
+ median: ["Median"],
97
+ "95th": ["95th %"]
98
+ }
99
+ __timers.values.each { |timer|
100
+ cols[:names] << "#{timer.name.inspect}#{"*" if timer.active?}"
101
+ cols[:elapsed] << "%.5f" % timer.elapsed
102
+ cols[:count] << timer.count.to_s
103
+ cols[:min] << "%.5f" % timer.min
104
+ cols[:max] << "%.5f" % timer.max
105
+ cols[:mean] << "%.5f" % timer.mean
106
+ cols[:median] << "%.5f" % timer.median
107
+ cols[:"95th"] << "%.5f" % timer.percentile(95)
108
+ }
109
+
110
+ widths = cols.map { |name, vals|
111
+ [name, vals.map(&:length).max]
112
+ }.to_h
113
+
114
+ rows = cols[:names].size.times.map { |i|
115
+ if i == 0
116
+ cols.keys.map { |name|
117
+ cols[name][i].center(widths[name])
118
+ }
119
+ else
120
+ cols.keys.map { |name|
121
+ cols[name][i].ljust(widths[name])
122
+ }
123
+ end
124
+ }
125
+
126
+ full_width = widths.values.sum + (rows[0].size - 1) * 3
127
+ io.puts <<~SUMMARY
128
+
129
+ #{"=" * full_width}
130
+ #{rows[0].join(" | ")}
131
+ #{"-" * full_width}
132
+ #{rows[1..-1].map { |row| row.join(" | ") }.join("\n")}
133
+
134
+ #{"* Denotes that the timer is still active\n" if __timers.values.any?(&:active?)}
135
+ SUMMARY
136
+ end
137
+
76
138
  # Iterative methods
77
139
  def self.stop_all
78
140
  __timers.values.each(&:stop)
@@ -89,7 +151,7 @@ module TimeUp
89
151
 
90
152
  # Internal methods
91
153
  def self.__timers
92
- Thread.current[:time_up_timers]
154
+ Thread.current[:time_up_timers] ||= {}
93
155
  end
94
156
 
95
157
  def self.__ensure_timer(name)
@@ -165,6 +227,25 @@ module TimeUp
165
227
  times.sum / times.size
166
228
  end
167
229
 
230
+ def median
231
+ times = timings.sort
232
+ return if times.empty?
233
+ (times[(times.size - 1) / 2] + times[times.size / 2]) / 2.0
234
+ end
235
+
236
+ def percentile(percent)
237
+ times = timings.sort
238
+ return if times.empty?
239
+ return 0 if percent <= 0
240
+ return max if percent >= 100
241
+ return times.first if times.size == 1
242
+ position = (percent / 100.0) * (times.size - 1)
243
+
244
+ partial_ratio = position - position.floor
245
+ whole, partial = times[position.floor, 2]
246
+ whole + (partial - whole) * partial_ratio
247
+ end
248
+
168
249
  def timings
169
250
  if active?
170
251
  @past_timings + [now - @start_time]
@@ -1,3 +1,3 @@
1
1
  module TimeUp
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.7"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: time_up
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Searls
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-17 00:00:00.000000000 Z
11
+ date: 2021-07-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -19,6 +19,7 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - ".github/workflows/main.yml"
21
21
  - ".gitignore"
22
+ - ".standard.yml"
22
23
  - CHANGELOG.md
23
24
  - Gemfile
24
25
  - Gemfile.lock