awfy 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +153 -10
  3. data/lib/awfy/cli.rb +12 -6
  4. data/lib/awfy/version.rb +1 -1
  5. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f90af1a6ce12d7b811d3fb6353f91d46e1637607b7890d3ed0837fbeda7fdd96
4
- data.tar.gz: e2a17a61c3ecf4d5795fc636407b49fa1aeaf150308f67215acf8d648d8f5df5
3
+ metadata.gz: 9b3e0b8df98fcef36e265d46ab9416c1b7cd529116fe57a4b9470a95d04427d0
4
+ data.tar.gz: a7d9962369da0320d5d97cfb32db82ff06b52d72425e22e4134759c8a549a140
5
5
  SHA512:
6
- metadata.gz: 22291837342e69a9da6c55e7a4a6869a80b2dfa47d67e2ad3847a870335999560109de557f73b58d1b73d564fb1f9f016654fcc542ecada0238b16276a74ca23
7
- data.tar.gz: 4c8dfa85d87b246512f7a478bfdf844c4af7361e1b6d87a418bf1d96aaa260a592194f3c7114f79cdfb38afc3a473da871f52f93c2f20811739bdbe05ba2bf77
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
- TODO: Delete this and the text below, and describe your gem
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
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/awfy`. To experiment with that code, run `bin/console` for an interactive prompt.
5
+ The benchmarks are written using a simple DSL in your target project.
6
6
 
7
- ## Installation
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
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
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 UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
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 UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
27
+ gem install awfy
21
28
  ```
22
29
 
23
30
  ## Usage
24
31
 
25
- TODO: Write usage instructions here
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/[USERNAME]/awfy.
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 "> Report (#{runtime} - branch '#{git_current_branch_name}'): #{report[:name]}", :magenta
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]} (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: "Summary for #{requested_tests(group_data[:group], group_data[:report])}",
386
- headings: ["Branch", "Runtime", "Name", "IPS", "Diff v baseline (times)"],
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Awfy
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
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.2.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-28 00:00:00.000000000 Z
11
+ date: 2024-10-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor