perflab 0.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/.circleci/config.yml +54 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +15 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +90 -0
- data/LICENSE.txt +22 -0
- data/README.md +92 -0
- data/lib/perflab.rb +19 -0
- data/lib/perflab/benchmark.rb +51 -0
- data/lib/perflab/config.rb +23 -0
- data/lib/perflab/operator.rb +60 -0
- data/lib/perflab/profiler.rb +15 -0
- data/lib/perflab/util.rb +17 -0
- data/perflab.gemspec +21 -0
- data/spec/perflab_spec.rb +129 -0
- data/spec/spec_helper.rb +11 -0
- metadata +171 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 07770e966ab5d03af3ba434df0cab25aadf476862dd8d465c7a686394b34fecd
|
4
|
+
data.tar.gz: 0e5cff62571e62c0f7f3ad75e5bb27980e14b306b8271335cbf71033e32eae0e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 34887275755092c94a09aea125fb946fd508e88216cdf8209b4d7791c12214d1040517eb1f1530896dc346f5439d36d9b0224f1897f7832a95b6b89dcf85958b
|
7
|
+
data.tar.gz: 839f89aeb9ba9d26d173a86ca36a1179133da63b67da621897a5b8858930b690cbbfa44effca278d4115275d93f81c19ff5efd095b79bb58a4e84baae44ff95a
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Ruby CircleCI 2.0 configuration file
|
2
|
+
#
|
3
|
+
# Check https://circleci.com/docs/2.0/language-ruby/ for more details
|
4
|
+
#
|
5
|
+
version: 2
|
6
|
+
jobs:
|
7
|
+
build:
|
8
|
+
docker:
|
9
|
+
# specify the version you desire here
|
10
|
+
- image: circleci/ruby:2.6.3-node
|
11
|
+
|
12
|
+
working_directory: ~/repo
|
13
|
+
|
14
|
+
steps:
|
15
|
+
- checkout
|
16
|
+
|
17
|
+
- run:
|
18
|
+
name: Configure Bundler
|
19
|
+
command: |
|
20
|
+
echo 'export BUNDLER_VERSION=$(cat Gemfile.lock | tail -1 | tr -d " ")' >> $BASH_ENV
|
21
|
+
source $BASH_ENV
|
22
|
+
gem install bundler
|
23
|
+
|
24
|
+
# Download and cache dependencies
|
25
|
+
- restore_cache:
|
26
|
+
keys:
|
27
|
+
- v1-dependencies-{{ checksum "Gemfile.lock" }}
|
28
|
+
# fallback to using the latest cache if no exact match is found
|
29
|
+
- v1-dependencies-
|
30
|
+
|
31
|
+
- run:
|
32
|
+
name: Install dependencies
|
33
|
+
command: |
|
34
|
+
bundle install --jobs=4 --retry=3 --path vendor/bundle
|
35
|
+
|
36
|
+
- save_cache:
|
37
|
+
paths:
|
38
|
+
- ./vendor/bundle
|
39
|
+
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
|
40
|
+
|
41
|
+
- run:
|
42
|
+
name: rubocop
|
43
|
+
command: bundle exec rubocop
|
44
|
+
|
45
|
+
- run:
|
46
|
+
name: rspec
|
47
|
+
command: bundle exec rspec
|
48
|
+
|
49
|
+
# collect reports
|
50
|
+
- store_test_results:
|
51
|
+
path: /tmp/test-results
|
52
|
+
- store_artifacts:
|
53
|
+
path: /tmp/test-results
|
54
|
+
destination: test-results
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
perflab (0.1.0)
|
5
|
+
activesupport
|
6
|
+
benchmark-ipsa
|
7
|
+
stackprof
|
8
|
+
zeitwerk
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
activesupport (5.2.3)
|
14
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
15
|
+
i18n (>= 0.7, < 2)
|
16
|
+
minitest (~> 5.1)
|
17
|
+
tzinfo (~> 1.1)
|
18
|
+
ast (2.4.0)
|
19
|
+
benchmark-ips (2.5.0)
|
20
|
+
benchmark-ipsa (0.2.0)
|
21
|
+
benchmark-ips (~> 2.5.0)
|
22
|
+
memory_profiler (~> 0.9.6)
|
23
|
+
concurrent-ruby (1.1.5)
|
24
|
+
coveralls (0.8.23)
|
25
|
+
json (>= 1.8, < 3)
|
26
|
+
simplecov (~> 0.16.1)
|
27
|
+
term-ansicolor (~> 1.3)
|
28
|
+
thor (>= 0.19.4, < 2.0)
|
29
|
+
tins (~> 1.6)
|
30
|
+
diff-lcs (1.3)
|
31
|
+
docile (1.3.1)
|
32
|
+
i18n (1.6.0)
|
33
|
+
concurrent-ruby (~> 1.0)
|
34
|
+
jaro_winkler (1.5.2)
|
35
|
+
json (2.2.0)
|
36
|
+
memory_profiler (0.9.13)
|
37
|
+
minitest (5.11.3)
|
38
|
+
parallel (1.17.0)
|
39
|
+
parser (2.6.3.0)
|
40
|
+
ast (~> 2.4.0)
|
41
|
+
rainbow (3.0.0)
|
42
|
+
rspec (3.8.0)
|
43
|
+
rspec-core (~> 3.8.0)
|
44
|
+
rspec-expectations (~> 3.8.0)
|
45
|
+
rspec-mocks (~> 3.8.0)
|
46
|
+
rspec-core (3.8.0)
|
47
|
+
rspec-support (~> 3.8.0)
|
48
|
+
rspec-expectations (3.8.3)
|
49
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
50
|
+
rspec-support (~> 3.8.0)
|
51
|
+
rspec-mocks (3.8.0)
|
52
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
53
|
+
rspec-support (~> 3.8.0)
|
54
|
+
rspec-support (3.8.0)
|
55
|
+
rubocop (0.68.1)
|
56
|
+
jaro_winkler (~> 1.5.1)
|
57
|
+
parallel (~> 1.10)
|
58
|
+
parser (>= 2.5, != 2.5.1.1)
|
59
|
+
rainbow (>= 2.2.2, < 4.0)
|
60
|
+
ruby-progressbar (~> 1.7)
|
61
|
+
unicode-display_width (>= 1.4.0, < 1.6)
|
62
|
+
ruby-progressbar (1.10.0)
|
63
|
+
simplecov (0.16.1)
|
64
|
+
docile (~> 1.1)
|
65
|
+
json (>= 1.8, < 3)
|
66
|
+
simplecov-html (~> 0.10.0)
|
67
|
+
simplecov-html (0.10.2)
|
68
|
+
stackprof (0.2.12)
|
69
|
+
term-ansicolor (1.7.1)
|
70
|
+
tins (~> 1.0)
|
71
|
+
thor (0.20.3)
|
72
|
+
thread_safe (0.3.6)
|
73
|
+
tins (1.20.2)
|
74
|
+
tzinfo (1.2.5)
|
75
|
+
thread_safe (~> 0.1)
|
76
|
+
unicode-display_width (1.5.0)
|
77
|
+
zeitwerk (2.1.4)
|
78
|
+
|
79
|
+
PLATFORMS
|
80
|
+
ruby
|
81
|
+
|
82
|
+
DEPENDENCIES
|
83
|
+
coveralls
|
84
|
+
perflab!
|
85
|
+
rspec
|
86
|
+
rubocop
|
87
|
+
simplecov
|
88
|
+
|
89
|
+
BUNDLED WITH
|
90
|
+
2.0.1
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright © 2019 Mert Guldur
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# PerfLab
|
2
|
+
|
3
|
+
[](https://circleci.com/gh/mertguldur/perflab)
|
4
|
+
|
5
|
+
## Introduction
|
6
|
+
|
7
|
+
PerfLab is a unified interface for [stackprof](https://github.com/tmm1/stackprof), [benchmark](https://ruby-doc.org/stdlib-2.6.3/libdoc/benchmark/rdoc/Benchmark.html), [benchmark-ips](https://github.com/evanphx/benchmark-ips/) and [benchmark-ipsa](https://github.com/jondot/benchmark-ipsa) libraries. It allows performance testers to define code snippets they want to improve and benchmark against only once. It is designed to streamline the Profile -> Benchmark -> Iterate process.
|
8
|
+
|
9
|
+
## Setup
|
10
|
+
|
11
|
+
Install [graphviz](https://www.graphviz.org/) to create dot graphs from profiler output files.
|
12
|
+
|
13
|
+
In your Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'perflab'
|
17
|
+
```
|
18
|
+
|
19
|
+
Specify the code snippet you already have and the one you want to improve.
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
lab = PerfLab.configure do |config|
|
23
|
+
config.existing -> { MyService.slow_code }
|
24
|
+
config.improved -> { MyService.fast_code }
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
## Profiling
|
29
|
+
|
30
|
+
PerfLab uses `stackprof` to profile the given improved code snippet.
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
lab.profile # profiles the 'improved' snippet and writes to tmp/perflab/profiler.dump
|
34
|
+
|
35
|
+
lab.profile_existing # same as .profile except that it profiles the 'existing' snippet
|
36
|
+
```
|
37
|
+
|
38
|
+
It is recommended to convert the dump file to a dot graph to easily interpret the report.
|
39
|
+
|
40
|
+
Bash example:
|
41
|
+
|
42
|
+
```bash
|
43
|
+
function prof {
|
44
|
+
stackprof tmp/perflab/profiler.dump --graphviz > tmp/perflab/profiler.dot
|
45
|
+
dot -Tpng tmp/perflab/profiler.dot > tmp/perflab/profiler.png
|
46
|
+
open -a 'Google Chrome' tmp/perflab/profiler.png
|
47
|
+
}
|
48
|
+
```
|
49
|
+
|
50
|
+
For other ways of examining the profiler report please refer to [stackprof](https://github.com/tmm1/stackprof).
|
51
|
+
|
52
|
+
## Benchmarking
|
53
|
+
|
54
|
+
PerfLab provides wrappers for [Benchmark.bmbm](https://ruby-doc.org/stdlib-2.6.3/libdoc/benchmark/rdoc/Benchmark.html#method-c-bmbm), [Benchmark.ips](https://github.com/evanphx/benchmark-ips/) and [Benchmark.ipsa](https://github.com/jondot/benchmark-ipsa) methods.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
lab.bmbm # calls Benchmark.bmbm with both
|
58
|
+
lab.bmbm_improved # calls Benchmark.bmbm with only improved
|
59
|
+
|
60
|
+
lab.ips # calls Benchmark.ips with both
|
61
|
+
lab.ips_improved # calls Benchmark.ips with only improved
|
62
|
+
|
63
|
+
lab.ipsa # calls Benchmark.ipsa with both
|
64
|
+
lab.ipsa_improved # calls Benchmark.ipsa with only improved
|
65
|
+
```
|
66
|
+
|
67
|
+
It sets up `ips` and `ipsa` with one rehearsal round and favors `bmbm` over `bm` to minimize memory allocation and GC side effects.
|
68
|
+
|
69
|
+
## Verifying correctness
|
70
|
+
|
71
|
+
PerfLab has a `correct?` method to verify that the `improved` snippet behaves exactly like `existing`.
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
lab.correct?
|
75
|
+
=> # true or false
|
76
|
+
```
|
77
|
+
|
78
|
+
By default it compares the return values of the snippets but one can pass in an `equality` lambda for additional checks.
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
lab = PerfLab.configure do |config|
|
82
|
+
config.existing -> { MyService.slow_code }
|
83
|
+
config.improved -> { MyService.fast_code }
|
84
|
+
config.equality ->(existing_result, improved_result) {
|
85
|
+
existing_result == improved_result && MyModel.count == 500
|
86
|
+
} # optional
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
## Thanks
|
91
|
+
|
92
|
+
This was written on [tastyworks'](https://tastyworks.com/) time.
|
data/lib/perflab.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'active_support/all'
|
2
|
+
require 'benchmark'
|
3
|
+
require 'benchmark/ipsa'
|
4
|
+
require 'stackprof'
|
5
|
+
require 'zeitwerk'
|
6
|
+
|
7
|
+
loader = Zeitwerk::Loader.for_gem
|
8
|
+
loader.setup
|
9
|
+
loader.eager_load
|
10
|
+
|
11
|
+
module PerfLab
|
12
|
+
module_function
|
13
|
+
|
14
|
+
def configure
|
15
|
+
config = Config.new
|
16
|
+
yield config
|
17
|
+
Operator.new(config.lambdas)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module PerfLab
|
2
|
+
class Benchmark
|
3
|
+
EXISTING = 'existing'.freeze
|
4
|
+
IMPROVED = 'improved'.freeze
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def bmbm(improved, existing = nil)
|
8
|
+
results = ::Benchmark.bmbm do |x|
|
9
|
+
x.report(EXISTING) { existing.call } if existing.present?
|
10
|
+
x.report(IMPROVED) { improved.call }
|
11
|
+
end
|
12
|
+
|
13
|
+
return results unless existing.present?
|
14
|
+
|
15
|
+
print_bmbm_improvement(results)
|
16
|
+
results
|
17
|
+
end
|
18
|
+
|
19
|
+
def ips(improved, existing = nil)
|
20
|
+
::Benchmark.ips do |x|
|
21
|
+
x.config(warmup: 1, time: 1)
|
22
|
+
|
23
|
+
x.report(EXISTING) { existing.call } if existing.present?
|
24
|
+
x.report(IMPROVED) { improved.call }
|
25
|
+
|
26
|
+
x.compare!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def ipsa(improved, existing = nil)
|
31
|
+
::Benchmark.ipsa do |x|
|
32
|
+
x.config(warmup: 1, time: 1)
|
33
|
+
|
34
|
+
x.report(EXISTING) { existing.call } if existing.present?
|
35
|
+
x.report(IMPROVED) { improved.call }
|
36
|
+
|
37
|
+
x.compare!
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def print_bmbm_improvement(results)
|
44
|
+
existing_wall_time = results.find { |result| result.label == EXISTING }.real
|
45
|
+
improved_wall_time = results.find { |result| result.label == IMPROVED }.real
|
46
|
+
|
47
|
+
Util.print_performance_improvement(existing_wall_time, improved_wall_time)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module PerfLab
|
2
|
+
class Config
|
3
|
+
def existing(existing)
|
4
|
+
@existing = existing
|
5
|
+
end
|
6
|
+
|
7
|
+
def improved(improved)
|
8
|
+
@improved = improved
|
9
|
+
end
|
10
|
+
|
11
|
+
def equality(equality)
|
12
|
+
@equality = equality
|
13
|
+
end
|
14
|
+
|
15
|
+
def lambdas
|
16
|
+
{
|
17
|
+
existing: @existing,
|
18
|
+
improved: @improved,
|
19
|
+
equality: @equality
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module PerfLab
|
2
|
+
class Operator
|
3
|
+
def initialize(existing:, improved:, equality:)
|
4
|
+
@existing = existing
|
5
|
+
@improved = improved
|
6
|
+
@equality = equality
|
7
|
+
end
|
8
|
+
|
9
|
+
def profile
|
10
|
+
puts 'Rehearsal...'
|
11
|
+
@improved.call
|
12
|
+
|
13
|
+
puts 'Profiling...'
|
14
|
+
Profiler.profile(@improved)
|
15
|
+
end
|
16
|
+
|
17
|
+
def profile_existing
|
18
|
+
puts 'Rehearsal...'
|
19
|
+
@existing.call
|
20
|
+
|
21
|
+
puts 'Profiling...'
|
22
|
+
Profiler.profile(@existing)
|
23
|
+
end
|
24
|
+
|
25
|
+
def bmbm
|
26
|
+
Benchmark.bmbm(@improved, @existing)
|
27
|
+
end
|
28
|
+
|
29
|
+
def bmbm_improved
|
30
|
+
Benchmark.bmbm(@improved)
|
31
|
+
end
|
32
|
+
|
33
|
+
def ips
|
34
|
+
Benchmark.ips(@improved, @existing)
|
35
|
+
end
|
36
|
+
|
37
|
+
def ips_improved
|
38
|
+
Benchmark.ipsa(@improved)
|
39
|
+
end
|
40
|
+
|
41
|
+
def ipsa
|
42
|
+
Benchmark.ipsa(@improved, @existing)
|
43
|
+
end
|
44
|
+
|
45
|
+
def ipsa_improved
|
46
|
+
Benchmark.ipsa(@improved)
|
47
|
+
end
|
48
|
+
|
49
|
+
def correct?
|
50
|
+
if @equality.present?
|
51
|
+
result = @equality.call(@existing.call, @improved.call)
|
52
|
+
raise ArgumentError, 'Equality must return a boolean' unless [TrueClass, FalseClass].include?(result.class)
|
53
|
+
|
54
|
+
result
|
55
|
+
else
|
56
|
+
@existing.call == @improved.call
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module PerfLab
|
2
|
+
class Profiler
|
3
|
+
DIRECTORY = 'tmp/perflab'.freeze
|
4
|
+
FILENAME = 'profiler.dump'.freeze
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def profile(lambda)
|
8
|
+
FileUtils.mkdir_p(DIRECTORY)
|
9
|
+
path = "#{DIRECTORY}/#{FILENAME}"
|
10
|
+
|
11
|
+
StackProf.run(mode: :wall, out: path, raw: true) { lambda.call }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/perflab/util.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module PerfLab
|
2
|
+
class Util
|
3
|
+
class << self
|
4
|
+
def print_performance_improvement(existing_result, improved_result)
|
5
|
+
percentage = ((improved_result.to_f - existing_result.to_f) / existing_result.to_f) * 100
|
6
|
+
|
7
|
+
if percentage < 0
|
8
|
+
puts "Improved is #{percentage.abs.round(2)}% faster"
|
9
|
+
elsif percentage > 0
|
10
|
+
puts "Improved is #{percentage.abs.round(2)}% slower"
|
11
|
+
else
|
12
|
+
puts 'No difference in speed'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/perflab.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'perflab'
|
3
|
+
s.version = '0.1.0'
|
4
|
+
s.date = '2019-05-08'
|
5
|
+
s.summary = 'Ruby performance lab'
|
6
|
+
s.description = 'Library to profile and benchmark Ruby code'
|
7
|
+
s.authors = ['Mert Guldur']
|
8
|
+
s.email = 'mertguldur@gmail.com'
|
9
|
+
s.files = `git ls-files`.split("\n")
|
10
|
+
s.license = 'MIT'
|
11
|
+
|
12
|
+
s.add_dependency 'activesupport'
|
13
|
+
s.add_dependency 'benchmark-ipsa'
|
14
|
+
s.add_dependency 'stackprof'
|
15
|
+
s.add_dependency 'zeitwerk'
|
16
|
+
|
17
|
+
s.add_development_dependency 'coveralls'
|
18
|
+
s.add_development_dependency 'rspec'
|
19
|
+
s.add_development_dependency 'rubocop'
|
20
|
+
s.add_development_dependency 'simplecov'
|
21
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PerfLab do
|
4
|
+
let(:lab) do
|
5
|
+
described_class.configure do |config|
|
6
|
+
config.existing -> { 100.times; 100 }
|
7
|
+
config.improved -> { 10.times; 10 }
|
8
|
+
config.equality ->(_existing_result, _improved_result) { true }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'profiling' do
|
13
|
+
let(:directory) { 'spec' }
|
14
|
+
let(:filename) { 'perflab-profiler-test.dump' }
|
15
|
+
let(:path) { "#{directory}/#{filename}" }
|
16
|
+
|
17
|
+
before do
|
18
|
+
stub_const('PerfLab::Profiler::DIRECTORY', directory)
|
19
|
+
stub_const('PerfLab::Profiler::FILENAME', filename)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '.profile' do
|
23
|
+
it 'profiles the improved lambda and creates an output file' do
|
24
|
+
lab.profile
|
25
|
+
|
26
|
+
expect(File.exist?(path)).to eq(true)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '.profile_existing' do
|
31
|
+
it 'profiles the existing lambda and creates an output file' do
|
32
|
+
lab.profile_existing
|
33
|
+
|
34
|
+
expect(File.exist?(path)).to eq(true)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
after do
|
39
|
+
FileUtils.rm(path)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '.bmbm' do
|
44
|
+
it 'creates a benchmark with existing and improved lambdas' do
|
45
|
+
results = lab.bmbm
|
46
|
+
|
47
|
+
expect(results.size).to eq(2)
|
48
|
+
results.each do |result|
|
49
|
+
expect(result).to be_a(Benchmark::Tms)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '.bmbm_improved' do
|
55
|
+
it 'creates a benchmark only with the improved lambda' do
|
56
|
+
results = lab.bmbm_improved
|
57
|
+
|
58
|
+
expect(results.size).to eq(1)
|
59
|
+
expect(results.first).to be_a(Benchmark::Tms)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe '.ips' do
|
64
|
+
it 'creates a benchmark with existing and improved lambdas' do
|
65
|
+
result = lab.ips
|
66
|
+
expect(result).to be_a(Benchmark::IPS::Report)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '.ips_improved' do
|
71
|
+
it 'creates a benchmark only with improved lambda' do
|
72
|
+
result = lab.ips_improved
|
73
|
+
expect(result).to be_a(Benchmark::IPS::Report)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '.ipsa' do
|
78
|
+
it 'creates a benchmark with existing and improved lambdas' do
|
79
|
+
result = lab.ipsa
|
80
|
+
expect(result).to be_a(Benchmark::IPS::Report)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe '.ipsa_improved' do
|
85
|
+
it 'creates a benchmark only with improved lambda' do
|
86
|
+
result = lab.ipsa_improved
|
87
|
+
expect(result).to be_a(Benchmark::IPS::Report)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'equality' do
|
92
|
+
context 'when the equality is provided' do
|
93
|
+
it 'runs the lambda to verify correctness' do
|
94
|
+
expect(lab).to be_correct
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'when the equality is not provided' do
|
99
|
+
it 'compares the return values of the existing and improved lambdas to verify correctness' do
|
100
|
+
lab = described_class.configure do |config|
|
101
|
+
config.existing -> { 100.times; 100 }
|
102
|
+
config.improved -> { 10.times; 10 }
|
103
|
+
end
|
104
|
+
|
105
|
+
expect(lab).to_not be_correct
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe 'performance improvement calculation' do
|
111
|
+
context 'when improved is faster' do
|
112
|
+
it 'prints a message saying improved is faster' do
|
113
|
+
described_class::Util.print_performance_improvement(10, 5)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'when improved is slower' do
|
118
|
+
it 'prints a message saying improved is slower' do
|
119
|
+
described_class::Util.print_performance_improvement(10, 15)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context 'when there is not difference' do
|
124
|
+
it 'prints a message saying there is no difference' do
|
125
|
+
described_class::Util.print_performance_improvement(10, 10)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: perflab
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mert Guldur
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-05-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: benchmark-ipsa
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: stackprof
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: zeitwerk
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: coveralls
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Library to profile and benchmark Ruby code
|
126
|
+
email: mertguldur@gmail.com
|
127
|
+
executables: []
|
128
|
+
extensions: []
|
129
|
+
extra_rdoc_files: []
|
130
|
+
files:
|
131
|
+
- ".circleci/config.yml"
|
132
|
+
- ".gitignore"
|
133
|
+
- ".rubocop.yml"
|
134
|
+
- Gemfile
|
135
|
+
- Gemfile.lock
|
136
|
+
- LICENSE.txt
|
137
|
+
- README.md
|
138
|
+
- lib/perflab.rb
|
139
|
+
- lib/perflab/benchmark.rb
|
140
|
+
- lib/perflab/config.rb
|
141
|
+
- lib/perflab/operator.rb
|
142
|
+
- lib/perflab/profiler.rb
|
143
|
+
- lib/perflab/util.rb
|
144
|
+
- perflab.gemspec
|
145
|
+
- spec/perflab_spec.rb
|
146
|
+
- spec/spec_helper.rb
|
147
|
+
homepage:
|
148
|
+
licenses:
|
149
|
+
- MIT
|
150
|
+
metadata: {}
|
151
|
+
post_install_message:
|
152
|
+
rdoc_options: []
|
153
|
+
require_paths:
|
154
|
+
- lib
|
155
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
161
|
+
requirements:
|
162
|
+
- - ">="
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: '0'
|
165
|
+
requirements: []
|
166
|
+
rubyforge_project:
|
167
|
+
rubygems_version: 2.7.6
|
168
|
+
signing_key:
|
169
|
+
specification_version: 4
|
170
|
+
summary: Ruby performance lab
|
171
|
+
test_files: []
|