awfy 0.2.0 → 0.3.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 +4 -4
- data/README.md +153 -10
- data/lib/awfy/cli.rb +12 -6
- data/lib/awfy/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b3e0b8df98fcef36e265d46ab9416c1b7cd529116fe57a4b9470a95d04427d0
|
4
|
+
data.tar.gz: a7d9962369da0320d5d97cfb32db82ff06b52d72425e22e4134759c8a549a140
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89e5d7f45b6375e790ea7387af082d603d3464caa0944084970c858c526e4bccb95673a4beddb61ebfb0b3b3bb64b7b285b613870b41d1baa3ed7d114d9c9ee8
|
7
|
+
data.tar.gz: beed2c0d694ffb1da281c9ac0344ca52e11cb981c2933d34f6a26ef5b73ba16c4d2e525bd40dd9a9fe68fc2a51bf10a3870253330eaa540aa05936ef7d211bb6
|
data/README.md
CHANGED
@@ -1,38 +1,181 @@
|
|
1
1
|
# Awfy (Are We Fast Yet)
|
2
2
|
|
3
|
-
|
3
|
+
CLI tool to help run suites of benchmarks and compare results between control implementations, across branches and with or without YJIT.
|
4
4
|
|
5
|
-
|
5
|
+
The benchmarks are written using a simple DSL in your target project.
|
6
6
|
|
7
|
-
|
7
|
+
Supports running:
|
8
|
+
|
9
|
+
- IPS benchmarks (with [benchmark-ips](https://rubygems.org/gems/benchmark-ips))
|
10
|
+
- Memory profiling (with [memory_profiler](https://rubygems.org/gems/memory_profiler))
|
11
|
+
- CPU profiling (with [stackprof](https://rubygems.org/gems/stackprof))
|
12
|
+
- Flamegraph profiling (with [singed](https://rubygems.org/gems/singed))
|
8
13
|
|
9
|
-
|
14
|
+
Awfy can also create summary reports of the results which can be useful for comparing the performance of different implementations **(currently only supported for IPS benchmarks)**.
|
15
|
+
|
16
|
+
## Installation
|
10
17
|
|
11
18
|
Install the gem and add to the application's Gemfile by executing:
|
12
19
|
|
13
20
|
```bash
|
14
|
-
bundle add
|
21
|
+
bundle add awfy
|
15
22
|
```
|
16
23
|
|
17
24
|
If bundler is not being used to manage dependencies, install the gem by executing:
|
18
25
|
|
19
26
|
```bash
|
20
|
-
gem install
|
27
|
+
gem install awfy
|
21
28
|
```
|
22
29
|
|
23
30
|
## Usage
|
24
31
|
|
25
|
-
|
32
|
+
Imagine we have a custom implementation of a Struct class called `MyStruct`. We want to compare the performance of our implementation with the built-in `Struct` class
|
33
|
+
and other similar implementations.
|
34
|
+
|
35
|
+
First, we need to create a setup file in the `benchmarks/setup.rb` directory. For example:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
# setup.rb
|
39
|
+
|
40
|
+
require "dry-struct"
|
41
|
+
require "active_model"
|
42
|
+
|
43
|
+
class DryStruct < Dry::Struct
|
44
|
+
attribute :name, Types::String
|
45
|
+
attribute :age, Types::Integer
|
46
|
+
end
|
47
|
+
|
48
|
+
class ActiveModelAttributes
|
49
|
+
include ActiveModel::API
|
50
|
+
include ActiveModel::Attributes
|
51
|
+
|
52
|
+
attribute :name, :string
|
53
|
+
attribute :age, :integer
|
54
|
+
end
|
55
|
+
|
56
|
+
# ... etc
|
57
|
+
```
|
58
|
+
|
59
|
+
Then we write benchmarks in files in the `benchmarks/tests` directory. For example:
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
# benchmarks/tests/struct.rb
|
63
|
+
|
64
|
+
# A group is a collection of related reports
|
65
|
+
Awfy.group "Struct" do
|
66
|
+
# A report is a collection of tests related to one method or feature we want to benchmark
|
67
|
+
report "#some_method" do
|
68
|
+
# We do not want to the benchmark to include the creation of the object
|
69
|
+
my_struct = MyStruct.new(name: "John", age: 30)
|
70
|
+
ruby_struct = Struct.new(:name, :age).new("John", 30)
|
71
|
+
dry_struct = DryStruct.new(name: "John", age: 30)
|
72
|
+
active_model_attributes = ActiveModelAttributes.new(name: "John", age: 30)
|
73
|
+
|
74
|
+
# "control" blocks are used to compare the performance to other implementations
|
75
|
+
control "Ruby Struct" do
|
76
|
+
ruby_struct.some_method
|
77
|
+
end
|
78
|
+
|
79
|
+
control "Dry::Struct" do
|
80
|
+
dry_struct.some_method
|
81
|
+
end
|
82
|
+
|
83
|
+
control "ActiveModel::Attributes" do
|
84
|
+
active_model_attributes.some_method
|
85
|
+
end
|
86
|
+
|
87
|
+
# This is our implementation under test
|
88
|
+
test "MyStruct" do
|
89
|
+
my_struct.some_method
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
96
|
+
### IPS Benchmarks & Summary Reports
|
97
|
+
|
98
|
+
Say you are working on performance improvements in a branch called `perf`.
|
99
|
+
|
100
|
+
```bash
|
101
|
+
git checkout perf
|
102
|
+
|
103
|
+
# ... make some changes ... then run the benchmarks
|
104
|
+
|
105
|
+
bundle exec awfy ips Struct "#some_method" --compare-with=main --runtime=both
|
106
|
+
```
|
107
|
+
|
108
|
+
Note the comparison here is with the "baseline" which is the "test" block running on MRI without YJIT enabled, on the
|
109
|
+
current branch.
|
110
|
+
|
111
|
+
```
|
112
|
+
Running IPS for:
|
113
|
+
> Struct/#some_method...
|
114
|
+
> [mri - branch 'perf'] Struct / #some_method
|
115
|
+
> [mri - branch 'main'] Struct / #some_method
|
116
|
+
> [yjit - branch 'perf'] Struct / #some_method
|
117
|
+
> [yjit - branch 'main'] Struct / #some_method
|
118
|
+
+---------------------------------------------------------------------------+
|
119
|
+
| Struct/#some_method |
|
120
|
+
+--------+---------+----------------------------+-------------+-------------+
|
121
|
+
| Branch | Runtime | Name | IPS | Vs baseline |
|
122
|
+
+--------+---------+----------------------------+-------------+-------------+
|
123
|
+
| perf | mri | Ruby Struct | 3.288M | 2.26 x |
|
124
|
+
| perf | yjit | Ruby Struct | 3.238M | 2.22 x |
|
125
|
+
| perf | yjit | MyStruct | 2.364M | 1.62 x |
|
126
|
+
| main | yjit | MyStruct | 2.255M | 1.55 x |
|
127
|
+
| perf | mri | (baseline) MyStruct | 1.455M | - |
|
128
|
+
+--------+---------+----------------------------+-------------+-------------+
|
129
|
+
| main | mri | MyStruct | 1.248M | -1.1 x |
|
130
|
+
| perf | yjit | Dry::Struct | 1.213M | -1.2 x |
|
131
|
+
| perf | mri | Dry::Struct | 639.178k | -2.28 x |
|
132
|
+
| perf | yjit | ActiveModel::Attributes | 487.398k | -2.99 x |
|
133
|
+
| perf | mri | ActiveModel::Attributes | 310.554k | -4.69 x |
|
134
|
+
+--------+---------+----------------------------+-------------+-------------+
|
135
|
+
```
|
136
|
+
|
137
|
+
## CLI Options
|
138
|
+
|
139
|
+
```
|
140
|
+
bundle exec awfy -h
|
141
|
+
Commands:
|
142
|
+
awfy flamegraph GROUP REPORT TEST # Run flamegraph profiling
|
143
|
+
awfy help [COMMAND] # Describe available commands or one specific command
|
144
|
+
awfy ips [GROUP] [REPORT] [TEST] # Run IPS benchmarks
|
145
|
+
awfy list [GROUP] # List all tests in a group
|
146
|
+
awfy memory [GROUP] [REPORT] [TEST] # Run memory profiling
|
147
|
+
awfy profile [GROUP] [REPORT] [TEST] # Run CPU profiling
|
148
|
+
|
149
|
+
Options:
|
150
|
+
[--runtime=RUNTIME] # Run with and/or without YJIT enabled
|
151
|
+
# Default: both
|
152
|
+
# Possible values: both, yjit, mri
|
153
|
+
[--compare-with=COMPARE_WITH] # Name of branch to compare with results on current branch
|
154
|
+
[--compare-control], [--no-compare-control], [--skip-compare-control] # When comparing branches, also re-run all control blocks too
|
155
|
+
# Default: false
|
156
|
+
[--summary], [--no-summary], [--skip-summary] # Generate a summary of the results
|
157
|
+
# Default: true
|
158
|
+
[--verbose], [--no-verbose], [--skip-verbose] # Verbose output
|
159
|
+
# Default: false
|
160
|
+
[--ips-warmup=N] # Number of seconds to warmup the benchmark
|
161
|
+
# Default: 1
|
162
|
+
[--ips-time=N] # Number of seconds to run the benchmark
|
163
|
+
# Default: 3
|
164
|
+
[--temp-output-directory=TEMP_OUTPUT_DIRECTORY] # Directory to store temporary output files
|
165
|
+
# Default: ./benchmarks/tmp
|
166
|
+
[--setup-file-path=SETUP_FILE_PATH] # Path to the setup file
|
167
|
+
# Default: ./benchmarks/setup
|
168
|
+
[--tests-path=TESTS_PATH] # Path to the tests files
|
169
|
+
# Default: ./benchmarks/tests
|
170
|
+
```
|
26
171
|
|
27
172
|
## Development
|
28
173
|
|
29
174
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
175
|
|
31
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
-
|
33
176
|
## Contributing
|
34
177
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
178
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/stevegeek/awfy.
|
36
179
|
|
37
180
|
## License
|
38
181
|
|
data/lib/awfy/cli.rb
CHANGED
@@ -248,7 +248,7 @@ module Awfy
|
|
248
248
|
|
249
249
|
say if verbose?
|
250
250
|
say "> --------------------------" if verbose?
|
251
|
-
say ">
|
251
|
+
say "> [#{runtime} - branch '#{git_current_branch_name}'] #{group[:name]} / #{report[:name]}", :magenta
|
252
252
|
say "> --------------------------" if verbose?
|
253
253
|
say if verbose?
|
254
254
|
yield run_report, runtime
|
@@ -375,22 +375,28 @@ module Awfy
|
|
375
375
|
else
|
376
376
|
"?"
|
377
377
|
end
|
378
|
-
test_name = result[:is_baseline] ? "#{result[:test_name]}
|
378
|
+
test_name = result[:is_baseline] ? "(baseline) #{result[:test_name]}" : result[:test_name]
|
379
379
|
|
380
|
-
[result[:branch], result[:runtime], test_name, result[:stats].central_tendency.round, diff_message]
|
380
|
+
[result[:branch], result[:runtime], test_name, Benchmark::IPS::Helpers.scale(result[:stats].central_tendency.round), diff_message]
|
381
381
|
end
|
382
382
|
|
383
383
|
group_data = report.first
|
384
384
|
table = ::Terminal::Table.new(
|
385
|
-
title:
|
386
|
-
headings: ["Branch", "Runtime", "Name", "IPS", "
|
387
|
-
rows: rows
|
385
|
+
title: requested_tests(group_data[:group], group_data[:report]),
|
386
|
+
headings: ["Branch", "Runtime", "Name", "IPS", "Vs baseline"]
|
388
387
|
)
|
389
388
|
|
390
389
|
table.align_column(2, :right)
|
391
390
|
table.align_column(3, :right)
|
392
391
|
table.align_column(4, :right)
|
393
392
|
|
393
|
+
rows.each do |row|
|
394
|
+
table.add_row(row)
|
395
|
+
if row[4] == "-"
|
396
|
+
table.add_separator(border_type: :dot3)
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
394
400
|
say table
|
395
401
|
end
|
396
402
|
end
|
data/lib/awfy/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: awfy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Ierodiaconou
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-10-
|
11
|
+
date: 2024-10-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|