rbenchmarker 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +17 -0
- data/MIT-LICENSE +21 -0
- data/README.md +364 -0
- data/lib/rbenchmarker.rb +159 -0
- data/lib/rbenchmarker/class_methods.rb +73 -0
- data/lib/rbenchmarker/exceptions.rb +63 -0
- data/lib/rbenchmarker/prepend_modules.rb +41 -0
- data/lib/rbenchmarker/rbenchmarker_log.rb +37 -0
- data/lib/rbenchmarker/register_rbenchmarker_methods.rb +192 -0
- data/lib/rbenchmarker/version.rb +5 -0
- metadata +59 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2877e9a63034a510f81700591df732ac237b2f7f30e7a09ac34edee5d2315faf
|
4
|
+
data.tar.gz: 282590fe246b95a914dd91e3bafb9985b3cd9877d29272373af2790b389391d3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3e06e196167f76bb8a9bfef4e55bf81f02779fe3b842e969518dbb2df0978b216b0001b0567aa3595bc5a2ae15f22fc577429e6462c3c418fdaf99216d5c2c40
|
7
|
+
data.tar.gz: 7a9178fa2069857b395f3c92d3768b3ff08176c82d2dc3a43007df73220dd91fb460d639c593b940c3712efa6aa953f4753a0ed4fb78c3b11b7ad413e5b8342c
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Rbenchmarker Changelog
|
2
|
+
|
3
|
+
Doing our best at supporting [SemVer](http://semver.org/) with a nice looking [Changelog](http://keepachangelog.com).
|
4
|
+
|
5
|
+
## Version 0.1.1 <sub><sup>2021-02-05</sub></sup>
|
6
|
+
|
7
|
+
- Corrected incorrect description
|
8
|
+
|
9
|
+
## Version 0.1.0 <sub><sup>2020-02-05</sub></sup>
|
10
|
+
|
11
|
+
- Initial version
|
12
|
+
- Class methods benchmark
|
13
|
+
- Module methods benchmark
|
14
|
+
- Options to select the target
|
15
|
+
- Options to choose where to place the logs
|
16
|
+
- Benchmark option to select the number of executions
|
17
|
+
- Option to select dry run
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2021 daiki shibata
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,364 @@
|
|
1
|
+
# Rbenchmarker
|
2
|
+
|
3
|
+
Rbenchmarker is a gem that allows you to automatically benchmark the execution time of a method defined in a Ruby class and module.
|
4
|
+
Benchmark module (https://docs.ruby-lang.org/ja/latest/class/Benchmark.html) is used inside Rbenchmarker, and bm method is automatically applied to all target methods.
|
5
|
+
|
6
|
+
However, method itself to which Rbenchmarker is applied remains unchanged, takes the same arguments as before, and returns the same return value as before.
|
7
|
+
|
8
|
+
So you don't have to change the methods yourself, and you don't have to benchmark the methods one by one.
|
9
|
+
Just launch the application as before and will automatically benchmark all targeted methods!
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
# Gemfile
|
17
|
+
|
18
|
+
gem 'rbenchmarker'
|
19
|
+
```
|
20
|
+
|
21
|
+
And then execute:
|
22
|
+
|
23
|
+
$ bundle install
|
24
|
+
|
25
|
+
Or install it yourself as:
|
26
|
+
|
27
|
+
$ gem install rbenchmarker
|
28
|
+
|
29
|
+
## Add the launch process to your Ruby project
|
30
|
+
|
31
|
+
Add the following code to any file in your Ruby project.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
# Note that you need to run `Rbenchmarker.setup` after all the files have been read.
|
35
|
+
Rbenchmarker.setup switch: 'on',
|
36
|
+
output_log_path: nil,
|
37
|
+
except_classes: [],
|
38
|
+
except_modules: []
|
39
|
+
```
|
40
|
+
|
41
|
+
Or run it directly from console
|
42
|
+
|
43
|
+
```
|
44
|
+
irb(main):001:0> Rbenchmarker.setup switch: 'on', output_log_path: nil, except_classes: [], except_modules: []
|
45
|
+
=> true
|
46
|
+
```
|
47
|
+
|
48
|
+
The `setup` method executes the process of adding the benchmark function to all the methods in the specified class and module.
|
49
|
+
|
50
|
+
Note that you need to run `Rbenchmarker.setup` after all the files (classes, modules) have been read.
|
51
|
+
|
52
|
+
[Details of setup options](https://github.com/shibatadaiki/Rbenchmarker#about-setup-options)
|
53
|
+
|
54
|
+
### For Ruby on Rails projects, add the following settings to your `config`
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
# config/environments/development.rb
|
58
|
+
|
59
|
+
config.eager_load = true # Please note that this setting is mandatory!
|
60
|
+
|
61
|
+
config.after_initialize do
|
62
|
+
Rbenchmarker.setup switch: 'on', output_log_path: nil, except_classes: [], except_modules: []
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
Please note that when using the Rbenchmark feature, the server will need to be restarted for the file changes to take effect.
|
67
|
+
|
68
|
+
[Details of config.after_initialize and config.eager_load](https://guides.rubyonrails.org/configuring.html#rails-general-configuration)
|
69
|
+
|
70
|
+
## Add rbenchmarker to your Class
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
# app/models/sample_class.rb
|
74
|
+
|
75
|
+
class SampleClass
|
76
|
+
rbenchmarker all: __FILE__
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
Your Class is now a benchmarker!
|
81
|
+
|
82
|
+
When the method in the class in which rbenchmarker is set is executed, the following log will be output.
|
83
|
+
|
84
|
+
Logs are placed directly under the './log' directory if './log' directory exists, or directly under the current directory if does not exist.
|
85
|
+
|
86
|
+
`rbenchmarker.log`
|
87
|
+
|
88
|
+
```log
|
89
|
+
# Logfile created on 2020-12-22 16:24:06 +0900 by logger.rb/v1.4.2
|
90
|
+
I, [2020-12-22T16:24:06.327445 #54558] INFO -- : == Start recording Rbenchmarker ==
|
91
|
+
|
92
|
+
I, [2020-12-22T16:24:12.848277 #54558] INFO -- :
|
93
|
+
report def test_method1 class method: current time
|
94
|
+
user: 0.00000900, system: 0.00000700, total: 0.00001600, real: 0.00000700
|
95
|
+
report def test_method1 class method: total time for 1 times called
|
96
|
+
user: 0.00000900, system: 0.00000700, total: 0.00001600, real: 0.00000700
|
97
|
+
report def test_method1 class method: avarage time
|
98
|
+
user: 0.00000900, system: 0.00000700, total: 0.00001600, real: 0.00000700
|
99
|
+
|
100
|
+
I, [2020-12-22T16:24:14.009972 #54558] INFO -- :
|
101
|
+
report def test_method1 class method: current time
|
102
|
+
user: 0.00000500, system: 0.00000200, total: 0.00000700, real: 0.00000400
|
103
|
+
report def test_method1 class method: total time for 2 times called
|
104
|
+
user: 0.00001400, system: 0.00000900, total: 0.00002300, real: 0.00001100
|
105
|
+
report def test_method1 class method: avarage time
|
106
|
+
user: 0.00000700, system: 0.00000450, total: 0.00001150, real: 0.00000550
|
107
|
+
|
108
|
+
I, [2020-12-22T16:24:29.969068 #54558] INFO -- :
|
109
|
+
report def test_method2 instance method: current time
|
110
|
+
user: 0.00000600, system: 0.00000200, total: 0.00000800, real: 0.00000500
|
111
|
+
report def test_method2 instance method: total time for 1 times called
|
112
|
+
user: 0.00000600, system: 0.00000200, total: 0.00000800, real: 0.00000500
|
113
|
+
report def test_method2 instance method: avarage time
|
114
|
+
user: 0.00000600, system: 0.00000200, total: 0.00000800, real: 0.00000500
|
115
|
+
|
116
|
+
I, [2020-12-22T16:24:30.545224 #54558] INFO -- :
|
117
|
+
report def test_method2 instance method: current time
|
118
|
+
user: 0.00000600, system: 0.00000200, total: 0.00000800, real: 0.00000500
|
119
|
+
report def test_method2 instance method: total time for 2 times called
|
120
|
+
user: 0.00001200, system: 0.00000400, total: 0.00001600, real: 0.00001000
|
121
|
+
report def test_method2 instance method: avarage time
|
122
|
+
user: 0.00000600, system: 0.00000200, total: 0.00000800, real: 0.00000500
|
123
|
+
|
124
|
+
I, [2020-12-22T16:24:31.185216 #54558] INFO -- :
|
125
|
+
report def test_method2 instance method: current time
|
126
|
+
user: 0.00000600, system: 0.00000200, total: 0.00000800, real: 0.00000400
|
127
|
+
report def test_method2 instance method: total time for 3 times called
|
128
|
+
user: 0.00001800, system: 0.00000600, total: 0.00002400, real: 0.00001400
|
129
|
+
report def test_method2 instance method: avarage time
|
130
|
+
user: 0.00000600, system: 0.00000200, total: 0.00000800, real: 0.00000467
|
131
|
+
```
|
132
|
+
|
133
|
+
If repeat the same method, the total execution time and the average execution time of the number of executions will be output to the log.
|
134
|
+
|
135
|
+
## Add rbenchmarker to your Module
|
136
|
+
|
137
|
+
Can do the same with class as with module, but with module, additional work is required.
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
# lib/sample_module.rb
|
141
|
+
|
142
|
+
module SampleModule
|
143
|
+
extend Rbenchmarker::ClassMethods # for module, add this sentence before launching rbenchmarker
|
144
|
+
rbenchmarker all: __FILE__
|
145
|
+
end
|
146
|
+
```
|
147
|
+
|
148
|
+
Then add the following options in the class that is using that module.
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
# app/models/class_include_module.rb
|
152
|
+
|
153
|
+
class ClassIncludeModule
|
154
|
+
include SampleModule
|
155
|
+
rbenchmarker all: __FILE__, include: [SampleModule] # In the `include` option, specify all included modules in array format
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
# app/models/class_extend_module.rb
|
161
|
+
|
162
|
+
class ClassExtendModule
|
163
|
+
extend SampleModule
|
164
|
+
rbenchmarker all: __FILE__, extend: [SampleModule] # In the `extend` option, specify all extended modules in array format
|
165
|
+
end
|
166
|
+
```
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
# app/models/class_prepend_module.rb
|
170
|
+
|
171
|
+
class ClassPrependModule
|
172
|
+
prepend SampleModule
|
173
|
+
rbenchmarker all: __FILE__, prepend: [SampleModule] # In the `prepend` option, specify all prepended modules in array format
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
The same is true for modules that use modules.
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
# lib/module_include_module.rb
|
181
|
+
|
182
|
+
module DoIncludeModule
|
183
|
+
include SampleModule
|
184
|
+
extend Rbenchmarker::ClassMethods
|
185
|
+
rbenchmarker all: __FILE__, include: [SampleModule]
|
186
|
+
end
|
187
|
+
```
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
# lib/module_extend_module.rb
|
191
|
+
|
192
|
+
module DoExtendModule
|
193
|
+
extend SampleModule
|
194
|
+
extend Rbenchmarker::ClassMethods
|
195
|
+
rbenchmarker all: __FILE__, extend: [SampleModule]
|
196
|
+
end
|
197
|
+
```
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
# lib/module_prepend_module.rb
|
201
|
+
|
202
|
+
module DoPrependModule
|
203
|
+
prepend SampleModule
|
204
|
+
extend Rbenchmarker::ClassMethods
|
205
|
+
rbenchmarker all: __FILE__, prepend: [SampleModule]
|
206
|
+
end
|
207
|
+
```
|
208
|
+
|
209
|
+
Your Module is now a benchmarker!
|
210
|
+
|
211
|
+
## About `rbenchmarker` options
|
212
|
+
|
213
|
+
List of all possible options
|
214
|
+
|
215
|
+
| Options | Description | Exsample |
|
216
|
+
| ------------- | ------------- | ------------- |
|
217
|
+
| `all` | `all` option performs static analysis to identify the target method, so put `__FILE__` in the argument. Setting this option will measure all methods listed in the file. normally, set this option. | `rbenchmarker all: __FILE__` |
|
218
|
+
| `only` | method specified in `only` only will be benchmarked. | `rbenchmarker only: [:sample_method1, :sample_method2]` |
|
219
|
+
| `except` | method specified in `except` will not be benchmarked. | `rbenchmarker except: [:sample_method1, :sample_method2]` |
|
220
|
+
| `added` | method specified by `added` is added to the benchmark after the `only` and `except` method filtering is done. | `rbenchmarker added: [:sample_method1, :sample_method2]` |
|
221
|
+
| `label_width` | `label_width` specifies the width of the benchmark label. | `rbenchmarker label_width: 25` |
|
222
|
+
| `times` | benchmark is measured by repeatedly executing the benchmarked method for the number of times specified by `times`. (Keep in mind that code such as SQL queries will also be executed repeatedly.) | `rbenchmarker times: 5` |
|
223
|
+
| `require_hidden_method` | If `require_hidden_method` option is set to true, methods dynamically created by metaprogramming, methods added to another file, etc. are also included. | `rbenchmarker require_hidden_method: true` |
|
224
|
+
| `include` | in the `include` option, specify the module that you are including. Arrange the option array in the loading order of the modules to be included. | `rbenchmarker include: [SampleModule1, SampleModule2]` |
|
225
|
+
| `extend` | in the `extend` option, specify the module that you are extending. Arrange the option array in the loading order of the modules to be extended. | `rbenchmarker extend: [SampleModule1, SampleModule2]` |
|
226
|
+
| `prepend` | in the `prepend` option, specify the module that you are prepending. Arrange the option array in the loading order of the modules to be prepended. | `rbenchmarker prepend: [SampleModule1, SampleModule2]` |
|
227
|
+
|
228
|
+
Can set multiple options as follows.
|
229
|
+
|
230
|
+
```ruby
|
231
|
+
# app/models/sample_class.rb
|
232
|
+
|
233
|
+
class SampleClass
|
234
|
+
rbenchmarker all: __FILE__, only: [:sample_method1, :sample_method2], label_width: 25
|
235
|
+
end
|
236
|
+
```
|
237
|
+
|
238
|
+
Of course, also possible with modules.
|
239
|
+
|
240
|
+
```ruby
|
241
|
+
# lib/sample_module.rb
|
242
|
+
|
243
|
+
module SampleModule
|
244
|
+
extend Rbenchmarker::ClassMethods
|
245
|
+
rbenchmarker all: __FILE__, only: [:sample_method1, :sample_method2], label_width: 25
|
246
|
+
end
|
247
|
+
```
|
248
|
+
|
249
|
+
## About `require_hidden_method` option
|
250
|
+
|
251
|
+
Maybe, `require_hidden_method` option may often be used in Ruby on Rails projects.
|
252
|
+
|
253
|
+
For example, in the case of the following Ruby on Rails code
|
254
|
+
|
255
|
+
```ruby
|
256
|
+
# app/models/sample_class.rb
|
257
|
+
|
258
|
+
class SampleClass
|
259
|
+
rbenchmarker all: __FILE__
|
260
|
+
|
261
|
+
belongs_to :sample_parent
|
262
|
+
has_many :sample_children, dependent: :destroy
|
263
|
+
scope :has_sample_parent, -> { where.not(sample_parent_id: nil) }
|
264
|
+
|
265
|
+
def my_name_length
|
266
|
+
name.length
|
267
|
+
end
|
268
|
+
end
|
269
|
+
```
|
270
|
+
|
271
|
+
Ruby on Rails adds methods to the class with various options, but usually it's not written in a file.
|
272
|
+
However, the `all: __FILE__` option can only track methods defined directly in the file. (In this case, only `my_name_length` is tracked)
|
273
|
+
|
274
|
+
In such cases, use the `require_hidden_method` option to track invisible methods.
|
275
|
+
|
276
|
+
```ruby
|
277
|
+
# app/models/sample_class.rb
|
278
|
+
|
279
|
+
class SampleClass
|
280
|
+
rbenchmarker require_hidden_method: true # the `all` option is not required when using the `require_hidden_method` option
|
281
|
+
|
282
|
+
belongs_to :sample_parent
|
283
|
+
has_many :sample_children, dependent: :destroy
|
284
|
+
scope :has_sample_parent, -> { where.not(sample_parent_id: nil) }
|
285
|
+
|
286
|
+
def my_name_length
|
287
|
+
name.length
|
288
|
+
end
|
289
|
+
end
|
290
|
+
```
|
291
|
+
|
292
|
+
The same applies in the following cases.
|
293
|
+
|
294
|
+
```ruby
|
295
|
+
# lib/module_include_module.rb
|
296
|
+
|
297
|
+
module IncludedModule
|
298
|
+
extend ActiveSupport::Concern
|
299
|
+
included do
|
300
|
+
def sample_module_method
|
301
|
+
puts 'sample_module_method!'
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
# app/models/sample_class.rb
|
307
|
+
|
308
|
+
class SampleClass
|
309
|
+
include IncludedModule
|
310
|
+
rbenchmarker require_hidden_method: true # Requires the `require_hidden_method` option to track the IncludedModule methods
|
311
|
+
end
|
312
|
+
```
|
313
|
+
|
314
|
+
Set of `extend ActiveSupport::Concern` and `included do ~`, which is often seen when using Ruby on Rails modules, adds a method to the class itself by internally executing `class_eval` to the target class
|
315
|
+
(Rather than using module methods).
|
316
|
+
Therefore, want Rbenchmarker to track the methods added in this way, need the `require_hidden_method` option.
|
317
|
+
|
318
|
+
However, `require_hidden_method` option can be annoying as it keeps track of all hidden methods. In that case, adjust using options such as `only`, `except`, and `added`.
|
319
|
+
|
320
|
+
## About `setup` options
|
321
|
+
|
322
|
+
| Options | Description | Exsample |
|
323
|
+
| ------------- | ------------- | ------------- |
|
324
|
+
| `switch` | In case of `off` or `OFF`, the processing of rbenchmarker will not be executed. | `Rbenchmarker.setup switch: 'off'` |
|
325
|
+
| `output_log_path` | Specify the output destination of the measurement result log output by rbenchmark. | `Rbenchmarker.setup output_log_path: '/Users/daiki.shibata/xxx/workspace'` |
|
326
|
+
| `except_classes` | Specify the class that does not give rbenchmark processing. | `Rbenchmarker.setup except_classes: [Class1, Class2, Class3]` |
|
327
|
+
| `except_modules` | Specify the module that does not give rbenchmark processing. | `Rbenchmarker.setup except_modules: [Module1, Module2, Module3]` |
|
328
|
+
|
329
|
+
Can set multiple options as follows.
|
330
|
+
|
331
|
+
```ruby
|
332
|
+
# rbenchmarker_setup.rb
|
333
|
+
|
334
|
+
Rbenchmarker.setup switch: 'on',
|
335
|
+
output_log_path: '/Users/daiki.shibata/xxx/workspace',
|
336
|
+
except_classes: [Class1, Class2, Class3],
|
337
|
+
except_modules: [Module1, Module2, Module3]
|
338
|
+
```
|
339
|
+
|
340
|
+
## License
|
341
|
+
|
342
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
343
|
+
|
344
|
+
## Code of Conduct
|
345
|
+
|
346
|
+
Everyone interacting in the Rbenchmarker project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/shibatadaiki/Rbenchmarker/blob/main/CODE_OF_CONDUCT.md).
|
347
|
+
|
348
|
+
## Contributing
|
349
|
+
|
350
|
+
Everyone is encouraged to help improve this project. Here are a few ways you can help:
|
351
|
+
|
352
|
+
- [Report bugs](https://github.com/shibatadaiki/Rbenchmarker/issues)
|
353
|
+
- Fix bugs and [submit pull requests](https://github.com/shibatadaiki/Rbenchmarker/pulls)
|
354
|
+
- Write, clarify, or fix documentation
|
355
|
+
- Suggest or add new features
|
356
|
+
|
357
|
+
To get started with development:
|
358
|
+
|
359
|
+
```sh
|
360
|
+
git clone https://github.com/shibatadaiki/Rbenchmarker.git
|
361
|
+
cd Rbenchmarker
|
362
|
+
bundle install
|
363
|
+
bundle exec rake test
|
364
|
+
```
|
data/lib/rbenchmarker.rb
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'rbenchmarker/version'
|
4
|
+
require_relative 'rbenchmarker/class_methods'
|
5
|
+
require_relative 'rbenchmarker/prepend_modules'
|
6
|
+
require_relative 'rbenchmarker/rbenchmarker_log'
|
7
|
+
require_relative 'rbenchmarker/exceptions'
|
8
|
+
|
9
|
+
module Rbenchmarker
|
10
|
+
LOG_DIRECTORY = 'log'
|
11
|
+
LOG_FILE_NAME = 'rbenchmark.log'
|
12
|
+
|
13
|
+
@setup_executed = false
|
14
|
+
@rbench_classes = []
|
15
|
+
@rbench_modules = []
|
16
|
+
@object_with_has_modules = []
|
17
|
+
@module_with_has_methods_lists = {}
|
18
|
+
@bm_reports = {}
|
19
|
+
@rbenchmarker_log_file_path = if Dir.exist? "#{Dir.pwd}/#{LOG_DIRECTORY}"
|
20
|
+
"#{Dir.pwd}/#{LOG_DIRECTORY}/#{LOG_FILE_NAME}"
|
21
|
+
else
|
22
|
+
"#{Dir.pwd}/#{LOG_FILE_NAME}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.setup_executed?
|
26
|
+
@setup_executed
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.setup_executed!
|
30
|
+
@setup_executed = true
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.setup_no_executed!
|
34
|
+
@setup_executed = false
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.tracking_classes
|
38
|
+
@rbench_classes
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.add_class(value)
|
42
|
+
@rbench_classes << value
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.init_tracking_classes
|
46
|
+
@rbench_classes = []
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.tracking_modules
|
50
|
+
@rbench_modules
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.add_module(value)
|
54
|
+
@rbench_modules << value
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.init_tracking_modules
|
58
|
+
@rbench_modules = []
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.object_with_has_modules
|
62
|
+
@object_with_has_modules
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.add_object_with_modules(value)
|
66
|
+
@object_with_has_modules << value
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.init_object_with_has_modules
|
70
|
+
@object_with_has_modules = []
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.module_with_has_methods_lists
|
74
|
+
@module_with_has_methods_lists
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.add_module_with_has_methods_list(key, value)
|
78
|
+
@module_with_has_methods_lists[key] = value
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.init_module_with_has_methods_lists
|
82
|
+
@module_with_has_methods_lists = {}
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.tracking_reports
|
86
|
+
@bm_reports
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.add_report(key, value)
|
90
|
+
@bm_reports[key] = value
|
91
|
+
callbacks_self_add_report(key, value)
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.init_tracking_reports
|
95
|
+
@bm_reports = {}
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.output_log_file_path
|
99
|
+
@rbenchmarker_log_file_path
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.change_output_log_file_path(path_text)
|
103
|
+
@rbenchmarker_log_file_path = path_text
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.init_log_file_path
|
107
|
+
@rbenchmarker_log_file_path = if Dir.exist? "#{Dir.pwd}/#{LOG_DIRECTORY}"
|
108
|
+
"#{Dir.pwd}/#{LOG_DIRECTORY}/#{LOG_FILE_NAME}"
|
109
|
+
else
|
110
|
+
"#{Dir.pwd}/#{LOG_FILE_NAME}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# This method must read (execute) after reading all files except this configuration file.
|
115
|
+
def self.setup(switch: 'on', output_log_path: nil, except_classes: [], except_modules: [])
|
116
|
+
return if %w[off OFF].include?(switch)
|
117
|
+
return puts 'setup has already been executed.' if setup_executed?
|
118
|
+
|
119
|
+
setup_validation_check(except_classes, except_modules, output_log_path)
|
120
|
+
|
121
|
+
change_output_log_file_path "#{output_log_path}/#{LOG_FILE_NAME}" if output_log_path
|
122
|
+
|
123
|
+
tracking_classes.each do |benchmark_target_class, options|
|
124
|
+
next if except_classes.include?(benchmark_target_class)
|
125
|
+
|
126
|
+
benchmark_target_class.call_register_rbenchmarker_methods(options)
|
127
|
+
end
|
128
|
+
|
129
|
+
tracking_modules.each do |benchmark_target_module, options|
|
130
|
+
next if except_modules.include?(benchmark_target_module)
|
131
|
+
|
132
|
+
benchmark_target_module.call_register_rbenchmarker_methods(options)
|
133
|
+
end
|
134
|
+
|
135
|
+
Rbenchmarker::PrependModules.register_rbenchmarker_methods_to_module(
|
136
|
+
object_with_has_modules, module_with_has_methods_lists, except_modules
|
137
|
+
)
|
138
|
+
|
139
|
+
Rbenchmarker::RbenchmarkerLog.init_log
|
140
|
+
setup_executed!
|
141
|
+
end
|
142
|
+
|
143
|
+
# private
|
144
|
+
|
145
|
+
def self.setup_validation_check(except_classes, except_modules, output_log_path)
|
146
|
+
raise ExceptClassDesignationError unless except_classes.is_a?(Array)
|
147
|
+
raise ExceptClassDesignationError unless except_classes.all? { |obj| obj.class.to_s == 'Class' }
|
148
|
+
raise ExceptModuleDesignationError unless except_modules.is_a?(Array)
|
149
|
+
raise ExceptModuleDesignationError unless except_modules.all? { |obj| obj.class.to_s == 'Module' }
|
150
|
+
raise TargetDirPathError if output_log_path && !Dir.exist?(output_log_path)
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.callbacks_self_add_report(key, value)
|
154
|
+
Rbenchmarker::RbenchmarkerLog.puts_log(key, value)
|
155
|
+
end
|
156
|
+
|
157
|
+
private_class_method :setup_validation_check
|
158
|
+
private_class_method :callbacks_self_add_report
|
159
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'exceptions'
|
4
|
+
require_relative 'register_rbenchmarker_methods'
|
5
|
+
|
6
|
+
module Rbenchmarker
|
7
|
+
def self.included(klass)
|
8
|
+
klass.extend ClassMethods
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def rbenchmarker(**options)
|
13
|
+
rbenchmarker_validation_check(options)
|
14
|
+
|
15
|
+
options[:only] = options[:only].map(&:to_sym) if options[:only]
|
16
|
+
options[:except] = options[:except].map(&:to_sym) if options[:except]
|
17
|
+
options[:added] = options[:added].map(&:to_sym) if options[:added]
|
18
|
+
|
19
|
+
granted_modules = {}
|
20
|
+
granted_modules[:prepend] = options.delete(:prepend) if options[:prepend]
|
21
|
+
granted_modules[:include] = options.delete(:include) if options[:include]
|
22
|
+
granted_modules[:extend] = options.delete(:extend) if options[:extend]
|
23
|
+
|
24
|
+
if instance_of?(Module)
|
25
|
+
options[:object_type] = 'Module'
|
26
|
+
Rbenchmarker.add_module [self, options]
|
27
|
+
else
|
28
|
+
options[:object_type] = 'Class'
|
29
|
+
Rbenchmarker.add_class [self, options]
|
30
|
+
end
|
31
|
+
|
32
|
+
Rbenchmarker.add_object_with_modules([self, granted_modules])
|
33
|
+
end
|
34
|
+
|
35
|
+
def call_register_rbenchmarker_methods(options)
|
36
|
+
options[:target_obj] = self
|
37
|
+
Rbenchmarker::RegisterRbenchmarkerMethods.register_rbenchmarker_methods(**options)
|
38
|
+
end
|
39
|
+
|
40
|
+
private # not strictly concealed because can be called from unspecified object.
|
41
|
+
|
42
|
+
def rbenchmarker_validation_check(options)
|
43
|
+
raise Rbenchmarker::TargetFilePathError if options[:all] && !options[:all].is_a?(String)
|
44
|
+
raise Rbenchmarker::TargetFilePathError if options[:all] && !File.file?(options[:all])
|
45
|
+
raise Rbenchmarker::OnlyMethodDesignationError if options[:only] && !options[:only].is_a?(Array)
|
46
|
+
raise Rbenchmarker::OnlyMethodDesignationError if options[:only] && !options[:only].all? do |method_name|
|
47
|
+
method_name.is_a?(String) || method_name.is_a?(Symbol)
|
48
|
+
end
|
49
|
+
raise Rbenchmarker::ExceptMethodDesignationError if options[:except] && !options[:except].is_a?(Array)
|
50
|
+
raise Rbenchmarker::ExceptMethodDesignationError if options[:except] && !options[:except].all? do |method_name|
|
51
|
+
method_name.is_a?(String) || method_name.is_a?(Symbol)
|
52
|
+
end
|
53
|
+
raise Rbenchmarker::AddedMethodDesignationError if options[:added] && !options[:added].is_a?(Array)
|
54
|
+
raise Rbenchmarker::AddedMethodDesignationError if options[:added] && !options[:added].all? do |method_name|
|
55
|
+
method_name.is_a?(String) || method_name.is_a?(Symbol)
|
56
|
+
end
|
57
|
+
raise Rbenchmarker::PrependModuleDesignationError if options[:prepend] && !options[:prepend].is_a?(Array)
|
58
|
+
raise Rbenchmarker::PrependModuleDesignationError if options[:prepend] && !options[:prepend].all? do |obj|
|
59
|
+
obj.class.to_s == 'Module'
|
60
|
+
end
|
61
|
+
raise Rbenchmarker::IncludeModuleDesignationError if options[:include] && !options[:include].is_a?(Array)
|
62
|
+
raise Rbenchmarker::IncludeModuleDesignationError if options[:include] && !options[:include].all? do |obj|
|
63
|
+
obj.class.to_s == 'Module'
|
64
|
+
end
|
65
|
+
raise Rbenchmarker::ExtendModuleDesignationError if options[:extend] && !options[:extend].is_a?(Array)
|
66
|
+
raise Rbenchmarker::ExtendModuleDesignationError if options[:extend] && !options[:extend].all? do |obj|
|
67
|
+
obj.class.to_s == 'Module'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
Object.send :include, Rbenchmarker::ClassMethods
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rbenchmarker
|
4
|
+
class ExceptClassDesignationError < ArgumentError
|
5
|
+
def initialize
|
6
|
+
super('"except_classes" option must be an array containing only "Class".')
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class ExceptModuleDesignationError < ArgumentError
|
11
|
+
def initialize
|
12
|
+
super('"except_modules" option must be an array containing only the defined "Module".')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class TargetDirPathError < ArgumentError
|
17
|
+
def initialize
|
18
|
+
super('In the argument of "output_log_file_path", specify the path of the directory where you want to place the log.')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class OnlyMethodDesignationError < ArgumentError
|
23
|
+
def initialize
|
24
|
+
super('"Only" option must be an array containing only "String" or "Symbol".')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class ExceptMethodDesignationError < ArgumentError
|
29
|
+
def initialize
|
30
|
+
super('"Except" option must be an array containing only "String" or "Symbol".')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class AddedMethodDesignationError < ArgumentError
|
35
|
+
def initialize
|
36
|
+
super('"Added" option must be an array containing only "String" or "Symbol".')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class PrependModuleDesignationError < ArgumentError
|
41
|
+
def initialize
|
42
|
+
super('"prepend" option must be an array containing only the defined "Module".')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class IncludeModuleDesignationError < ArgumentError
|
47
|
+
def initialize
|
48
|
+
super('"include" option must be an array containing only the defined "Module".')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class ExtendModuleDesignationError < ArgumentError
|
53
|
+
def initialize
|
54
|
+
super('"extend" option must be an array containing only the defined "Module".')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class TargetFilePathError < ArgumentError
|
59
|
+
def initialize
|
60
|
+
super('Must be specify an existing file path. Unless there is a special reason, specify the return value of the "__FILE__" method in the all argument.')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rbenchmarker
|
4
|
+
module PrependModules
|
5
|
+
def self.register_rbenchmarker_methods_to_module(object_with_has_modules, module_with_has_methods_lists, except_modules)
|
6
|
+
object_with_has_modules.each do |obj, granted_modules|
|
7
|
+
if granted_modules[:prepend].is_a?(Array)
|
8
|
+
methods_setup(granted_modules, :prepend, obj, except_modules, module_with_has_methods_lists)
|
9
|
+
end
|
10
|
+
|
11
|
+
if granted_modules[:include].is_a?(Array)
|
12
|
+
methods_setup(granted_modules, :include, obj, except_modules, module_with_has_methods_lists)
|
13
|
+
end
|
14
|
+
|
15
|
+
if granted_modules[:extend].is_a?(Array)
|
16
|
+
methods_setup(granted_modules, :extend, obj, except_modules, module_with_has_methods_lists)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# private
|
22
|
+
|
23
|
+
def self.methods_setup(granted_modules, include_type, obj, except_modules, module_with_has_methods_lists)
|
24
|
+
granted_modules[include_type].each do |benchmark_module|
|
25
|
+
next if except_modules.include?(benchmark_module)
|
26
|
+
next unless module_with_has_methods_lists[benchmark_module.to_s.to_sym]
|
27
|
+
|
28
|
+
case include_type
|
29
|
+
when :prepend
|
30
|
+
obj.prepend module_with_has_methods_lists[benchmark_module.to_s.to_sym]
|
31
|
+
when :include
|
32
|
+
obj.include module_with_has_methods_lists[benchmark_module.to_s.to_sym]
|
33
|
+
when :extend
|
34
|
+
obj.extend module_with_has_methods_lists[benchmark_module.to_s.to_sym]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private_class_method :methods_setup
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module Rbenchmarker
|
6
|
+
module RbenchmarkerLog
|
7
|
+
def self.init_log
|
8
|
+
@logger = Logger.new(Rbenchmarker.output_log_file_path)
|
9
|
+
@logger.info("== Start recording Rbenchmarker == \n")
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.puts_log(label, report, number_of_digits: 8)
|
13
|
+
return unless defined? @logger
|
14
|
+
|
15
|
+
log_text = "\n"
|
16
|
+
log_text += "#{label}: current time\n"
|
17
|
+
log_text += "user: #{format("%.#{number_of_digits}f", report[:utime][-1])}, "
|
18
|
+
log_text += "system: #{format("%.#{number_of_digits}f", report[:stime][-1])}, "
|
19
|
+
log_text += "total: #{format("%.#{number_of_digits}f", report[:total][-1])}, "
|
20
|
+
log_text += "real: #{format("%.#{number_of_digits}f", report[:real][-1])}\n"
|
21
|
+
|
22
|
+
log_text += "#{label}: total time for #{report[:number_of_executions]} times called\n"
|
23
|
+
log_text += "user: #{format("%.#{number_of_digits}f", report[:utime].sum)}, "
|
24
|
+
log_text += "system: #{format("%.#{number_of_digits}f", report[:stime].sum)}, "
|
25
|
+
log_text += "total: #{format("%.#{number_of_digits}f", report[:total].sum)}, "
|
26
|
+
log_text += "real: #{format("%.#{number_of_digits}f", report[:real].sum)}\n"
|
27
|
+
|
28
|
+
log_text += "#{label}: avarage time\n"
|
29
|
+
log_text += "user: #{format("%.#{number_of_digits}f", (report[:utime].sum / report[:number_of_executions]))}, "
|
30
|
+
log_text += "system: #{format("%.#{number_of_digits}f", (report[:stime].sum / report[:number_of_executions]))}, "
|
31
|
+
log_text += "total: #{format("%.#{number_of_digits}f", (report[:total].sum / report[:number_of_executions]))}, "
|
32
|
+
log_text += "real: #{format("%.#{number_of_digits}f", (report[:real].sum / report[:number_of_executions]))}\n"
|
33
|
+
|
34
|
+
@logger.info(log_text)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
module Rbenchmarker
|
6
|
+
module RegisterRbenchmarkerMethods
|
7
|
+
DEFAULT_REPORT_LENGTH = 30
|
8
|
+
TIMES_THE_METHOD_WAS_CALLED = 1
|
9
|
+
|
10
|
+
# "rbenchmarker" needs to start processing after all the methods of the target class(file) have been read,
|
11
|
+
# Rbenchmarker uses the benchmark library (https://docs.ruby-lang.org/ja/latest/class/Benchmark.html) internally.
|
12
|
+
#
|
13
|
+
# "all" option performs static analysis to identify the target method, so put "__FILE__" in the argument. normally, set this option.
|
14
|
+
#
|
15
|
+
# method specified in "only" will not be benchmarked.
|
16
|
+
# method specified in "except" will not be benchmarked.
|
17
|
+
# method specified by "added" is added to the benchmark after the "only" and "except" method filtering is done.
|
18
|
+
#
|
19
|
+
# "label_width" specifies the width of the benchmark label.
|
20
|
+
#
|
21
|
+
# benchmark is measured by repeatedly executing the benchmarked method for the number of times specified by "times".
|
22
|
+
# Keep in mind that code such as SQL queries will also be executed repeatedly.
|
23
|
+
#
|
24
|
+
# "require_hidden_method" option is set to true, methods created dynamically by metaprogramming will also be targeted.
|
25
|
+
#
|
26
|
+
# in the "include" option, specify the module that you are including. Arrange the option array in the loading order of the modules to be included.
|
27
|
+
# in the "extend" option, specify the module that you are extending. Arrange the option array in the loading order of the modules to be extended.
|
28
|
+
# in the "prepend" option, specify the module that you are prepending. Arrange the option array in the loading order of the modules to be prepended.
|
29
|
+
def self.register_rbenchmarker_methods(
|
30
|
+
all: nil,
|
31
|
+
only: [],
|
32
|
+
except: [],
|
33
|
+
added: [],
|
34
|
+
label_width: 0,
|
35
|
+
times: 0,
|
36
|
+
require_hidden_method: false,
|
37
|
+
object_type: nil,
|
38
|
+
target_obj: nil
|
39
|
+
)
|
40
|
+
# Collect the method names to be benchmarked.
|
41
|
+
target_instance_method_names, target_class_method_names = collect_target_methods(
|
42
|
+
target_obj, all, require_hidden_method, only, except, added
|
43
|
+
)
|
44
|
+
|
45
|
+
# Add benchmark function to the method to be benchmarked.
|
46
|
+
instance_method_codes_with_benchmark_function = generate_benchmarking_codes(
|
47
|
+
target_instance_method_names, label_width, times, (object_type == 'Module' ? 'module' : 'instance')
|
48
|
+
)
|
49
|
+
class_method_codes_with_benchmark_function = generate_benchmarking_codes(
|
50
|
+
target_class_method_names, label_width, times, (object_type == 'Module' ? 'module' : 'class')
|
51
|
+
)
|
52
|
+
|
53
|
+
# Override the original method with the benchmark functionality.
|
54
|
+
prepended_instance_methods = prepend_benchmarking_methods(instance_method_codes_with_benchmark_function)
|
55
|
+
prepended_class_methods = prepend_benchmarking_methods(class_method_codes_with_benchmark_function)
|
56
|
+
|
57
|
+
case object_type
|
58
|
+
when 'Module'
|
59
|
+
Rbenchmarker.add_module_with_has_methods_list(target_obj.to_s.to_sym, prepended_instance_methods)
|
60
|
+
when 'Class'
|
61
|
+
target_obj.prepend prepended_instance_methods
|
62
|
+
end
|
63
|
+
|
64
|
+
target_obj.singleton_class.prepend prepended_class_methods
|
65
|
+
end
|
66
|
+
|
67
|
+
# private
|
68
|
+
|
69
|
+
def self.collect_target_methods(target_obj, target_file, require_hidden_method, only, except, added)
|
70
|
+
if require_hidden_method
|
71
|
+
instance_method_name_candidates = target_obj.instance_methods(false) + target_obj.private_instance_methods(false)
|
72
|
+
class_method_name_candidates = target_obj.singleton_methods(false)
|
73
|
+
elsif target_file
|
74
|
+
instance_method_name_candidates, class_method_name_candidates =
|
75
|
+
statically_analyze_and_extract_target_method_names(target_obj, target_file)
|
76
|
+
else
|
77
|
+
instance_method_name_candidates = []
|
78
|
+
class_method_name_candidates = []
|
79
|
+
end
|
80
|
+
|
81
|
+
filtered_instance_method_names =
|
82
|
+
filter_by_only_except_and_added(instance_method_name_candidates, only, except, added, target_obj, 'instance_method')
|
83
|
+
filtered_class_method_names =
|
84
|
+
filter_by_only_except_and_added(class_method_name_candidates, only, except, added, target_obj, 'class_method')
|
85
|
+
|
86
|
+
[filtered_instance_method_names, filtered_class_method_names]
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.statically_analyze_and_extract_target_method_names(target_obj, target_file)
|
90
|
+
instance_method_name_candidates = []
|
91
|
+
class_method_name_candidates = []
|
92
|
+
words = []
|
93
|
+
|
94
|
+
all_class_methods = target_obj.singleton_methods(false)
|
95
|
+
all_instance_methods = target_obj.instance_methods(false) + target_obj.private_instance_methods(false)
|
96
|
+
File.foreach(target_file) { |line| words << line[/def\ (.*?)[ ;|\(\n]/, 1] }
|
97
|
+
user_described_all_method_names = words.compact.map { |word| word.match(/^self\./) ? word.gsub(/^self\./, '') : word }
|
98
|
+
|
99
|
+
user_described_all_method_names.each do |def_name|
|
100
|
+
class_method_name_candidates << def_name.to_sym if all_class_methods.include?(def_name.to_sym)
|
101
|
+
instance_method_name_candidates << def_name.to_sym if all_instance_methods.include?(def_name.to_sym)
|
102
|
+
end
|
103
|
+
|
104
|
+
[instance_method_name_candidates.uniq, class_method_name_candidates.uniq]
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.filter_by_only_except_and_added(method_names, only, except, added, target_obj, method_type)
|
108
|
+
has_methods = case method_type
|
109
|
+
when 'instance_method'
|
110
|
+
target_obj.instance_methods(false) + target_obj.private_instance_methods(false)
|
111
|
+
when 'class_method'
|
112
|
+
target_obj.singleton_methods(false)
|
113
|
+
end
|
114
|
+
|
115
|
+
added = added.select { |method_name| has_methods.include?(method_name) }
|
116
|
+
|
117
|
+
if !only.empty?
|
118
|
+
((method_names & only) + added).uniq
|
119
|
+
elsif !except.empty?
|
120
|
+
(method_names - except + added).uniq
|
121
|
+
else
|
122
|
+
(method_names + added).uniq
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.generate_benchmarking_codes(method_names, width, times, class_type)
|
127
|
+
method_names.map { |method_name| define_benchmarking_code(method_name, width, times, class_type) }
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.define_benchmarking_code(method_name, width, times, class_type)
|
131
|
+
times_to_i = times.to_i.nonzero?
|
132
|
+
generate_report_function_code = generate_report_method_text('bm_result')
|
133
|
+
|
134
|
+
<<-RUBY_EVAL
|
135
|
+
def #{method_name}(*)
|
136
|
+
origin_return_value = nil
|
137
|
+
bm_result = # 'bm_result' is Arguments of 'generate_report_method_text'
|
138
|
+
Benchmark.bm #{width.to_i.nonzero? || method_name.length + DEFAULT_REPORT_LENGTH} do |r|
|
139
|
+
r.report "report def #{method_name}#{"(#{times_to_i}" if times_to_i}#{' loops)' if times_to_i} #{class_type} method" do
|
140
|
+
#{times_to_i ? "#{times_to_i}.times{ origin_return_value = super }" : 'origin_return_value = super'}
|
141
|
+
end
|
142
|
+
end
|
143
|
+
#{generate_report_function_code}
|
144
|
+
origin_return_value
|
145
|
+
end
|
146
|
+
RUBY_EVAL
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.generate_report_method_text(bm_report_name)
|
150
|
+
<<-RUBY_EVAL
|
151
|
+
new_report = #{bm_report_name}.first
|
152
|
+
bm_reports = Rbenchmarker.tracking_reports
|
153
|
+
if bm_reports[new_report.label.to_sym]
|
154
|
+
previous_report = bm_reports[new_report.label.to_sym]
|
155
|
+
key = new_report.label.to_sym
|
156
|
+
value = {
|
157
|
+
utime: previous_report[:utime] << new_report.utime,
|
158
|
+
stime: previous_report[:stime] << new_report.stime,
|
159
|
+
total: previous_report[:total] << new_report.total,
|
160
|
+
real: previous_report[:real] << new_report.real,
|
161
|
+
number_of_executions: previous_report[:number_of_executions] + 1
|
162
|
+
}
|
163
|
+
Rbenchmarker.add_report(key, value)
|
164
|
+
else
|
165
|
+
key = new_report.label.to_sym
|
166
|
+
value = {
|
167
|
+
utime: [new_report.utime],
|
168
|
+
stime: [new_report.stime],
|
169
|
+
total: [new_report.total],
|
170
|
+
real: [new_report.real],
|
171
|
+
number_of_executions: TIMES_THE_METHOD_WAS_CALLED
|
172
|
+
}
|
173
|
+
Rbenchmarker.add_report(key, value)
|
174
|
+
end
|
175
|
+
RUBY_EVAL
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.prepend_benchmarking_methods(benchmarking_codes)
|
179
|
+
Module.new do
|
180
|
+
benchmarking_codes.each { |code| class_eval code, __FILE__, __LINE__ + 1 }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
private_class_method :collect_target_methods
|
185
|
+
private_class_method :statically_analyze_and_extract_target_method_names
|
186
|
+
private_class_method :filter_by_only_except_and_added
|
187
|
+
private_class_method :generate_benchmarking_codes
|
188
|
+
private_class_method :define_benchmarking_code
|
189
|
+
private_class_method :generate_report_method_text
|
190
|
+
private_class_method :prepend_benchmarking_methods
|
191
|
+
end
|
192
|
+
end
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rbenchmarker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- daiki shibata
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-02-05 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |
|
14
|
+
Rbenchmarker is a gem that allows you to automatically benchmark the execution time of a method defined in a Ruby class and module. Benchmark module (https://docs.ruby-lang.org/ja/latest/class/Benchmark.html) is used inside Rbenchmarker, and bm method is automatically applied to all target methods.
|
15
|
+
However, method itself to which Rbenchmarker is applied remains unchanged, takes the same arguments as before, and returns the same return value as before.
|
16
|
+
So you don't have to change the methods yourself, and you don't have to benchmark the methods one by one. Just launch the application as before and will automatically benchmark all targeted methods!
|
17
|
+
email:
|
18
|
+
- shibatadaiki92@gmail.com
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- CHANGELOG.md
|
24
|
+
- MIT-LICENSE
|
25
|
+
- README.md
|
26
|
+
- lib/rbenchmarker.rb
|
27
|
+
- lib/rbenchmarker/class_methods.rb
|
28
|
+
- lib/rbenchmarker/exceptions.rb
|
29
|
+
- lib/rbenchmarker/prepend_modules.rb
|
30
|
+
- lib/rbenchmarker/rbenchmarker_log.rb
|
31
|
+
- lib/rbenchmarker/register_rbenchmarker_methods.rb
|
32
|
+
- lib/rbenchmarker/version.rb
|
33
|
+
homepage: https://github.com/shibatadaiki/Rbenchmarker
|
34
|
+
licenses:
|
35
|
+
- MIT
|
36
|
+
metadata:
|
37
|
+
homepage_uri: https://github.com/shibatadaiki/Rbenchmarker
|
38
|
+
source_code_uri: https://github.com/shibatadaiki/Rbenchmarker/
|
39
|
+
changelog_uri: https://github.com/shibatadaiki/Rbenchmarker/blob/master/CHANGELOG.md
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 2.3.0
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubygems_version: 3.2.3
|
56
|
+
signing_key:
|
57
|
+
specification_version: 4
|
58
|
+
summary: Automatically log benchmarks for all methods
|
59
|
+
test_files: []
|