derailed_benchmarks 1.3.6 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +7 -1
- data/README.md +70 -2
- data/Rakefile +2 -0
- data/bin/derailed +1 -0
- data/derailed_benchmarks.gemspec +4 -1
- data/gemfiles/rails_5_1.gemfile +2 -0
- data/gemfiles/rails_5_2.gemfile +2 -0
- data/gemfiles/rails_6_0.gemfile +2 -0
- data/gemfiles/rails_git.gemfile +19 -0
- data/lib/derailed_benchmarks.rb +20 -0
- data/lib/derailed_benchmarks/auth_helper.rb +2 -0
- data/lib/derailed_benchmarks/auth_helpers/devise.rb +2 -0
- data/lib/derailed_benchmarks/core_ext/kernel_require.rb +7 -2
- data/lib/derailed_benchmarks/load_tasks.rb +138 -0
- data/lib/derailed_benchmarks/require_tree.rb +3 -1
- data/lib/derailed_benchmarks/stats_from_dir.rb +99 -0
- data/lib/derailed_benchmarks/stats_in_file.rb +53 -0
- data/lib/derailed_benchmarks/tasks.rb +70 -115
- data/lib/derailed_benchmarks/version.rb +3 -1
- data/test/derailed_benchmarks/core_ext/kernel_require_test.rb +3 -0
- data/test/derailed_benchmarks/require_tree_test.rb +2 -0
- data/test/derailed_benchmarks/stats_from_dir_test.rb +101 -0
- data/test/derailed_test.rb +2 -0
- data/test/fixtures/require/child_one.rb +1 -1
- data/test/fixtures/require/child_two.rb +1 -1
- data/test/fixtures/require/parent_one.rb +2 -1
- data/test/fixtures/require/raise_child.rb +2 -0
- data/test/fixtures/require/relative_child.rb +2 -0
- data/test/fixtures/require/relative_child_two.rb +4 -0
- data/test/fixtures/stats/significant/loser.bench.txt +100 -0
- data/test/fixtures/stats/significant/winner.bench.txt +100 -0
- data/test/integration/tasks_test.rb +9 -0
- data/test/rails_app/Rakefile +2 -0
- data/test/rails_app/app/controllers/application_controller.rb +2 -0
- data/test/rails_app/app/controllers/authenticated_controller.rb +2 -0
- data/test/rails_app/app/controllers/pages_controller.rb +2 -0
- data/test/rails_app/app/helpers/application_helper.rb +2 -0
- data/test/rails_app/app/helpers/authenticated_helper.rb +2 -0
- data/test/rails_app/app/models/user.rb +2 -0
- data/test/rails_app/config.ru +2 -0
- data/test/rails_app/config/application.rb +2 -0
- data/test/rails_app/config/boot.rb +3 -1
- data/test/rails_app/config/environment.rb +4 -0
- data/test/rails_app/config/environments/development.rb +2 -0
- data/test/rails_app/config/environments/production.rb +2 -0
- data/test/rails_app/config/environments/test.rb +2 -0
- data/test/rails_app/config/initializers/backtrace_silencers.rb +2 -0
- data/test/rails_app/config/initializers/devise.rb +2 -0
- data/test/rails_app/config/initializers/inflections.rb +2 -0
- data/test/rails_app/config/initializers/mime_types.rb +2 -0
- data/test/rails_app/config/initializers/secret_token.rb +2 -0
- data/test/rails_app/config/initializers/session_store.rb +2 -0
- data/test/rails_app/config/routes.rb +2 -0
- data/test/rails_app/db/migrate/20141210070547_devise_create_users.rb +2 -0
- data/test/rails_app/db/schema.rb +2 -0
- data/test/rails_app/perf.rake +2 -0
- data/test/rails_app/script/rails +2 -0
- data/test/support/integration_case.rb +2 -0
- data/test/test_helper.rb +6 -2
- metadata +30 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cefb45902f24e4e4b0d8083b59ad9733005943db3df8a8b97b30552a27ff400b
|
4
|
+
data.tar.gz: 9d5921ac19df4db40e78ec2ca6396593f1f5235c3caaae84c332163ed3c5e4ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3335a72cc6b965b76f6804c3dd71ae5cf61d56f5203447870f0ad31a30c6cef5c47b28cf569311d7c033fe2898f2653ab041d7c7dceefe85e5501c4b79ab06b4
|
7
|
+
data.tar.gz: f591582236f863286b65d9a96b33b481e0a2c9cb05a6985ecb4123d121452ebda81a1b5d123f1d0a77c297939e8f6f2de16ca14dad892436109d819ff2b7ee91
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# A Log of Changes!
|
2
2
|
|
3
|
+
## 1.4.0
|
4
|
+
|
5
|
+
- Allow configuration of `perf:ips` benchmark.
|
6
|
+
- Fix bug with `require_relative` [#142](https://github.com/schneems/derailed_benchmarks/pull/142)
|
7
|
+
- Introduce `perf:library` to profile patches to libraries (like Rails) [#135](https://github.com/schneems/derailed_benchmarks/pull/135), [#139](https://github.com/schneems/derailed_benchmarks/pull/139), [#140](https://github.com/schneems/derailed_benchmarks/pull/140), [#141](https://github.com/schneems/derailed_benchmarks/pull/141)
|
8
|
+
|
3
9
|
## 1.3.6
|
4
10
|
|
5
11
|
- `require_relative` is now measured [commit](https://github.com/schneems/derailed_benchmarks/commit/af11bcc46a4fa24f79e4897a51034927a56e077e)
|
@@ -67,4 +73,4 @@
|
|
67
73
|
|
68
74
|
## [0.0.0] - 2014-08-15
|
69
75
|
|
70
|
-
- Initial release
|
76
|
+
- Initial release
|
data/README.md
CHANGED
@@ -317,7 +317,7 @@ TOP: 54.1836 MiB
|
|
317
317
|
action_view/base: 0.4336 MiB
|
318
318
|
```
|
319
319
|
|
320
|
-
You can use `CUT_OFF=0.3` to only show files that have above a certain memory
|
320
|
+
You can use `CUT_OFF=0.3` to only show files that have above a certain memory usage, this can be used to help eliminate noise.
|
321
321
|
|
322
322
|
If your application code is extremely large at boot consider using `$ derailed exec perf:objects` to debug low level object creation.
|
323
323
|
|
@@ -388,12 +388,80 @@ $ bundle exec derailed exec perf:test
|
|
388
388
|
|
389
389
|
But I wouldn't, benchmark-ips is a better measure.
|
390
390
|
|
391
|
+
### Configuring `benchmark-ips`
|
392
|
+
|
393
|
+
The `benchmark-ips` gem allows for a number of test run customizations, and `derailed_benchmarks` exposes a few of them via environment variables.
|
394
|
+
|
395
|
+
- `IPS_WARMUP`: number of seconds spent warming up the app, defaullt is `2`
|
396
|
+
- `IPS_TIME`: number of seconds to run ips benchmark for after warm up, defaullt is `5`
|
397
|
+
- `IPS_SUITE`: custom suite to use to run test
|
398
|
+
- `IPS_ITERATIONS`: number of times to run the test, displaying that last result, defaullt is `1`
|
399
|
+
|
400
|
+
## I made a patch to to Rails how can I tell if it made my Rails app faster and test for statistical significance
|
401
|
+
|
402
|
+
When you're trying to submit a performance patch to rails/rails then they'll likely ask you for a benchmark. While you can sometimes provide a microbenchmark, a real world full stack request/response test is the gold standard.
|
403
|
+
|
404
|
+
That's what this section is about. You'll need a rails app, ideally one you can open source (see [example apps](http://codetriage.com/example_app) if you need inspiration for extracting your private code into something external).
|
405
|
+
|
406
|
+
Then you'll need to fork rails and make a branch. Then point your rails app to your branch in your gemfile
|
407
|
+
|
408
|
+
```
|
409
|
+
gem 'rails', github: "<github username>/rails", branch: "<your branch name>"
|
410
|
+
```
|
411
|
+
|
412
|
+
or point it at your local copy:
|
413
|
+
|
414
|
+
```
|
415
|
+
gem 'rails', path: "<path/to/your/local/copy/rails>"
|
416
|
+
```
|
417
|
+
|
418
|
+
To run your test:
|
419
|
+
|
420
|
+
```
|
421
|
+
$ bundle exec derailed exec perf:library
|
422
|
+
```
|
423
|
+
|
424
|
+
This will automatically test the two latest commits of Rails (or the library you've specified). If you would like to compare against different SHAs you can manually specify them:
|
425
|
+
|
426
|
+
```
|
427
|
+
$ SHAS_TO_TEST="7b4d80cb373e,13d6aa3a7b70" bundle exec derailed exec perf:library
|
428
|
+
```
|
429
|
+
|
430
|
+
Use a comma to seperate your branch names with the `SHAS_TO_TEST` env var, or omit the env var to use the last 2 git commits.
|
431
|
+
|
432
|
+
If you only include one SHA, then derailed will grab the latest commit and compare it to that SHA.
|
433
|
+
|
434
|
+
These tests might take a along time to run so the output is stored on disk incase you want to see them in the future, they're at `tmp/library_branches/<timestamp>` and labeled with the same names as your commits.
|
435
|
+
|
436
|
+
When the test is done it will output which commit "won" and by how much:
|
437
|
+
|
438
|
+
```
|
439
|
+
❤️ ❤️ ❤️ (Statistically Significant) ❤️ ❤️ ❤️
|
440
|
+
|
441
|
+
[7b4d80cb37] "1.8x Faster Partial Caching - Faster Cache Keys" - (10.9711965 seconds)
|
442
|
+
FASTER by:
|
443
|
+
1.0870x [older/newer]
|
444
|
+
8.0026% [(older - newer) / older * 100]
|
445
|
+
[13d6aa3a7b] "Merge pull request #36284 from kamipo/fix_eager_loading_with_string_joins" - (11.9255485 seconds)
|
446
|
+
|
447
|
+
P-value: 4.635595463712749e-05
|
448
|
+
Is significant? (P-value < 0.05): true
|
449
|
+
```
|
450
|
+
|
451
|
+
You can provide this to the Rails team along with the example app you used to benchmark (so they can independently verify if needed).
|
452
|
+
|
453
|
+
Generally performance patches have to be weighted in terms of how much they help versus how large/difficult/gnarly the patch is. If the above example was a really tiny patch and it was in a common component, then half a percent might be a justafiable increase. If it was a huge re-write then it's likely going to be closed. In general I tend to not submit patches unless I'm seeing `>= 1%` performance increases.
|
454
|
+
|
455
|
+
You can use this to test changes in other libraries that aren't rails, you just have to tell it the path to the library you want to test against with the `DERAILED_PATH_TO_LIBRARY` env var.
|
456
|
+
|
457
|
+
> To get the best results before running tests you should close all programs on your laptop, turn on a program to prevent your laptop from going to sleep (or increase your sleep timer). Make sure it's plugged into a power outlet and go grab a cup of coffee. If you do anything on your laptop while this test is running you risk the chance of skewing your results.
|
458
|
+
|
459
|
+
By default derailed will stop once statistical signficance has been detected, you can tune this behavior by setting `DERAILED_STOP_VALID_COUNT` env var. Setting this to a positive number, will increase the number of iterations required that are detected to be statistically significant. For example setting it to 100 might result in 120 runs if it takes 20 runs to detect significance. Generally the more runs you have, the more accurate your averages will be. You can disable this all together by setting `DERAILED_STOP_VALID_COUNT=0` which will force derailed to run all iterations.
|
391
460
|
|
392
461
|
## Environment Variables
|
393
462
|
|
394
463
|
All the tasks accept configuration in the form of environment variables.
|
395
464
|
|
396
|
-
|
397
465
|
### Increasing or decreasing test count `TEST_COUNT`
|
398
466
|
|
399
467
|
For tasks that are run a number of times you can set the number using `TEST_COUNT` for example:
|
data/Rakefile
CHANGED
data/bin/derailed
CHANGED
data/derailed_benchmarks.gemspec
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
lib = File.expand_path('../lib', __FILE__)
|
3
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
6
|
require 'derailed_benchmarks/version'
|
@@ -27,10 +29,11 @@ Gem::Specification.new do |gem|
|
|
27
29
|
gem.add_dependency "rack", ">= 1"
|
28
30
|
gem.add_dependency "rake", "> 10", "< 13"
|
29
31
|
gem.add_dependency "thor", "~> 0.19"
|
32
|
+
gem.add_dependency "ruby-statistics", ">= 2.1"
|
30
33
|
|
31
34
|
gem.add_development_dependency "capybara", "~> 2"
|
32
35
|
gem.add_development_dependency "m"
|
33
36
|
gem.add_development_dependency "rails", "> 3", "<= 6"
|
34
|
-
gem.add_development_dependency "devise", "> 3", "<
|
37
|
+
gem.add_development_dependency "devise", "> 3", "< 6"
|
35
38
|
gem.add_development_dependency "appraisal", "2.2.0"
|
36
39
|
end
|
data/gemfiles/rails_5_1.gemfile
CHANGED
data/gemfiles/rails_5_2.gemfile
CHANGED
data/gemfiles/rails_6_0.gemfile
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# $ BUNDLE_GEMFILE="$(pwd)/gemfiles/rails_git.gemfile" bundle exec m test/integration/tasks_test.rb:30
|
4
|
+
|
5
|
+
source "https://rubygems.org"
|
6
|
+
|
7
|
+
gem "rails", github: "rails/rails", ref: "3054e1d584e7eca110c69a1f8423f2e0866abbf9"
|
8
|
+
|
9
|
+
gem 'devise', github: "plataformatec/devise"
|
10
|
+
|
11
|
+
group :development, :test do
|
12
|
+
gem "sqlite3", platform: [:ruby, :mswin, :mingw]
|
13
|
+
gem "activerecord-jdbcsqlite3-adapter", "~> 1.3.13", platform: :jruby
|
14
|
+
gem "test-unit", "~> 3.0"
|
15
|
+
end
|
16
|
+
|
17
|
+
gemspec path: "../"
|
18
|
+
|
19
|
+
ENV['USING_RAILS_GIT'] = "1"
|
data/lib/derailed_benchmarks.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'time'
|
2
4
|
require 'bundler'
|
3
5
|
|
@@ -13,6 +15,21 @@ module DerailedBenchmarks
|
|
13
15
|
attr_accessor :auth
|
14
16
|
end
|
15
17
|
|
18
|
+
def self.rails_path_on_disk
|
19
|
+
require 'rails/version'
|
20
|
+
rails_version_file = Rails.method(:version).source_location[0]
|
21
|
+
path = Pathname.new(rails_version_file).expand_path.parent.parent
|
22
|
+
|
23
|
+
while path != Pathname.new("/")
|
24
|
+
basename = path.expand_path.basename.to_s
|
25
|
+
|
26
|
+
break if basename.start_with?("rails") && basename != "railties"
|
27
|
+
path = path.parent
|
28
|
+
end
|
29
|
+
raise "Could not find rails folder on a folder in #{rails_version_file}" if path == Pathname.new("/")
|
30
|
+
path.expand_path
|
31
|
+
end
|
32
|
+
|
16
33
|
def self.add_auth(app)
|
17
34
|
if use_auth = ENV['USE_AUTH']
|
18
35
|
puts "Auth: #{use_auth}"
|
@@ -26,6 +43,9 @@ end
|
|
26
43
|
require 'derailed_benchmarks/require_tree'
|
27
44
|
require 'derailed_benchmarks/auth_helper'
|
28
45
|
|
46
|
+
require 'derailed_benchmarks/stats_in_file'
|
47
|
+
require 'derailed_benchmarks/stats_from_dir'
|
48
|
+
|
29
49
|
if DerailedBenchmarks.gem_is_bundled?("devise")
|
30
50
|
DerailedBenchmarks.auth = DerailedBenchmarks::AuthHelpers::Devise.new
|
31
51
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'get_process_mem'
|
2
4
|
require 'derailed_benchmarks/require_tree'
|
3
5
|
|
@@ -20,8 +22,11 @@ module Kernel
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def require_relative(file)
|
23
|
-
|
24
|
-
|
25
|
+
if Pathname.new(file).absolute?
|
26
|
+
require file
|
27
|
+
else
|
28
|
+
require File.expand_path("../#{file}", caller_locations(1, 1)[0].absolute_path)
|
29
|
+
end
|
25
30
|
end
|
26
31
|
|
27
32
|
class << self
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :perf do
|
4
|
+
task :rails_load do
|
5
|
+
ENV["RAILS_ENV"] ||= "production"
|
6
|
+
ENV['RACK_ENV'] = ENV["RAILS_ENV"]
|
7
|
+
ENV["DISABLE_SPRING"] = "true"
|
8
|
+
|
9
|
+
ENV["SECRET_KEY_BASE"] ||= "foofoofoo"
|
10
|
+
|
11
|
+
ENV['LOG_LEVEL'] ||= "FATAL"
|
12
|
+
|
13
|
+
require 'rails'
|
14
|
+
|
15
|
+
puts "Booting: #{Rails.env}"
|
16
|
+
|
17
|
+
%W{ . lib test config }.each do |file|
|
18
|
+
$LOAD_PATH << File.expand_path(file)
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'application'
|
22
|
+
|
23
|
+
Rails.env = ENV["RAILS_ENV"]
|
24
|
+
|
25
|
+
DERAILED_APP = Rails.application
|
26
|
+
|
27
|
+
if DERAILED_APP.respond_to?(:initialized?)
|
28
|
+
DERAILED_APP.initialize! unless DERAILED_APP.initialized?
|
29
|
+
else
|
30
|
+
DERAILED_APP.initialize! unless DERAILED_APP.instance_variable_get(:@initialized)
|
31
|
+
end
|
32
|
+
|
33
|
+
if ENV["DERAILED_SKIP_ACTIVE_RECORD"] && defined? ActiveRecord
|
34
|
+
if defined? ActiveRecord::Tasks::DatabaseTasks
|
35
|
+
ActiveRecord::Tasks::DatabaseTasks.create_current
|
36
|
+
else # Rails 3.2
|
37
|
+
raise "No valid database for #{ENV['RAILS_ENV']}, please create one" unless ActiveRecord::Base.connection.active?.inspect
|
38
|
+
end
|
39
|
+
|
40
|
+
ActiveRecord::Migrator.migrations_paths = DERAILED_APP.paths['db/migrate'].to_a
|
41
|
+
ActiveRecord::Migration.verbose = true
|
42
|
+
ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, nil)
|
43
|
+
end
|
44
|
+
|
45
|
+
DERAILED_APP.config.consider_all_requests_local = true
|
46
|
+
end
|
47
|
+
|
48
|
+
task :rack_load do
|
49
|
+
puts "You're not using Rails"
|
50
|
+
puts "You need to tell derailed how to boot your app"
|
51
|
+
puts "In your perf.rake add:"
|
52
|
+
puts
|
53
|
+
puts "namespace :perf do"
|
54
|
+
puts " task :rack_load do"
|
55
|
+
puts " # DERAILED_APP = your code here"
|
56
|
+
puts " end"
|
57
|
+
puts "end"
|
58
|
+
end
|
59
|
+
|
60
|
+
task :setup do
|
61
|
+
if DerailedBenchmarks.gem_is_bundled?("railties")
|
62
|
+
Rake::Task["perf:rails_load"].invoke
|
63
|
+
else
|
64
|
+
Rake::Task["perf:rack_load"].invoke
|
65
|
+
end
|
66
|
+
|
67
|
+
WARM_COUNT = (ENV['WARM_COUNT'] || 0).to_i
|
68
|
+
TEST_COUNT = (ENV['TEST_COUNT'] || ENV['CNT'] || 1_000).to_i
|
69
|
+
PATH_TO_HIT = ENV["PATH_TO_HIT"] || ENV['ENDPOINT'] || "/"
|
70
|
+
puts "Endpoint: #{ PATH_TO_HIT.inspect }"
|
71
|
+
|
72
|
+
HTTP_HEADER_PREFIX = "HTTP_".freeze
|
73
|
+
RACK_HTTP_HEADERS = ENV.select { |key| key.start_with?(HTTP_HEADER_PREFIX) }
|
74
|
+
|
75
|
+
HTTP_HEADERS = RACK_HTTP_HEADERS.keys.inject({}) do |hash, rack_header_name|
|
76
|
+
# e.g. "HTTP_ACCEPT_CHARSET" -> "Accept-Charset"
|
77
|
+
header_name = rack_header_name[HTTP_HEADER_PREFIX.size..-1].split("_").map(&:downcase).map(&:capitalize).join("-")
|
78
|
+
hash[header_name] = RACK_HTTP_HEADERS[rack_header_name]
|
79
|
+
hash
|
80
|
+
end
|
81
|
+
puts "HTTP headers: #{HTTP_HEADERS}" unless HTTP_HEADERS.empty?
|
82
|
+
|
83
|
+
CURL_HTTP_HEADER_ARGS = HTTP_HEADERS.map { |http_header_name, value| "-H \"#{http_header_name}: #{value}\"" }.join(" ")
|
84
|
+
|
85
|
+
require 'rack/test'
|
86
|
+
require 'rack/file'
|
87
|
+
|
88
|
+
DERAILED_APP = DerailedBenchmarks.add_auth(Object.class_eval { remove_const(:DERAILED_APP) })
|
89
|
+
if server = ENV["USE_SERVER"]
|
90
|
+
@port = (3000..3900).to_a.sample
|
91
|
+
puts "Port: #{ @port.inspect }"
|
92
|
+
puts "Server: #{ server.inspect }"
|
93
|
+
thread = Thread.new do
|
94
|
+
Rack::Server.start(app: DERAILED_APP, :Port => @port, environment: "none", server: server)
|
95
|
+
end
|
96
|
+
sleep 1
|
97
|
+
|
98
|
+
def call_app(path = File.join("/", PATH_TO_HIT))
|
99
|
+
cmd = "curl #{CURL_HTTP_HEADER_ARGS} 'http://localhost:#{@port}#{path}' -s --fail 2>&1"
|
100
|
+
response = `#{cmd}`
|
101
|
+
unless $?.success?
|
102
|
+
STDERR.puts "Couldn't call app."
|
103
|
+
STDERR.puts "Bad request to #{cmd.inspect} \n\n***RESPONSE***:\n\n#{ response.inspect }"
|
104
|
+
|
105
|
+
FileUtils.mkdir_p("tmp")
|
106
|
+
File.open("tmp/fail.html", "w+") {|f| f.write response.body }
|
107
|
+
|
108
|
+
`open #{File.expand_path("tmp/fail.html")}` if ENV["DERAILED_DEBUG"]
|
109
|
+
|
110
|
+
exit(1)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
else
|
114
|
+
@app = Rack::MockRequest.new(DERAILED_APP)
|
115
|
+
|
116
|
+
def call_app
|
117
|
+
response = @app.get(PATH_TO_HIT, RACK_HTTP_HEADERS)
|
118
|
+
if response.status != 200
|
119
|
+
STDERR.puts "Couldn't call app. Bad request to #{PATH_TO_HIT}! Resulted in #{response.status} status."
|
120
|
+
STDERR.puts "\n\n***RESPONSE BODY***\n\n"
|
121
|
+
STDERR.puts response.body
|
122
|
+
|
123
|
+
FileUtils.mkdir_p("tmp")
|
124
|
+
File.open("tmp/fail.html", "w+") {|f| f.write response.body }
|
125
|
+
|
126
|
+
`open #{File.expand_path("tmp/fail.html")}` if ENV["DERAILED_DEBUG"]
|
127
|
+
|
128
|
+
exit(1)
|
129
|
+
end
|
130
|
+
response
|
131
|
+
end
|
132
|
+
end
|
133
|
+
if WARM_COUNT > 0
|
134
|
+
puts "Warming up app: #{WARM_COUNT} times"
|
135
|
+
WARM_COUNT.times { call_app }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Tree structure used to store and sort require memory costs
|
2
4
|
# RequireTree.new('get_process_mem')
|
3
5
|
module DerailedBenchmarks
|
@@ -38,7 +40,7 @@ module DerailedBenchmarks
|
|
38
40
|
end
|
39
41
|
|
40
42
|
def to_string
|
41
|
-
str = "#{name}: #{cost.round(4)} MiB"
|
43
|
+
str = +"#{name}: #{cost.round(4)} MiB"
|
42
44
|
if parent && REQUIRED_BY[self.name.to_s]
|
43
45
|
names = REQUIRED_BY[self.name.to_s].uniq - [parent.name.to_s]
|
44
46
|
if names.any?
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bigdecimal'
|
4
|
+
require 'statistics'
|
5
|
+
|
6
|
+
module DerailedBenchmarks
|
7
|
+
# A class used to read several benchmark files
|
8
|
+
# it will parse each file, then sort by average
|
9
|
+
# time of benchmarks. It can be used to find
|
10
|
+
# the fastest and slowest examples and give information
|
11
|
+
# about them such as what the percent difference is
|
12
|
+
# and if the results are statistically significant
|
13
|
+
#
|
14
|
+
# Example:
|
15
|
+
#
|
16
|
+
# branch_info = {}
|
17
|
+
# branch_info["loser"] = { desc: "Old commit", time: Time.now, file: dir.join("loser.bench.txt"), name: "loser" }
|
18
|
+
# branch_info["winner"] = { desc: "I am the new commit", time: Time.now + 1, file: dir.join("winner.bench.txt"), name: "winner" }
|
19
|
+
# stats = DerailedBenchmarks::StatsFromDir.new(branch_info)
|
20
|
+
#
|
21
|
+
# stats.newest.average # => 10.5
|
22
|
+
# stats.oldest.average # => 11.0
|
23
|
+
# stats.significant? # => true
|
24
|
+
# stats.x_faster # => "1.0476"
|
25
|
+
class StatsFromDir
|
26
|
+
FORMAT = "%0.4f"
|
27
|
+
attr_reader :stats, :oldest, :newest
|
28
|
+
|
29
|
+
def initialize(hash)
|
30
|
+
@files = []
|
31
|
+
|
32
|
+
hash.each do |branch, info_hash|
|
33
|
+
file = info_hash.fetch(:file)
|
34
|
+
desc = info_hash.fetch(:desc)
|
35
|
+
time = info_hash.fetch(:time)
|
36
|
+
@files << StatsForFile.new(file: file, desc: desc, time: time, name: branch)
|
37
|
+
end
|
38
|
+
@files.sort_by! { |f| f.time }
|
39
|
+
@oldest = @files.first
|
40
|
+
@newest = @files.last
|
41
|
+
end
|
42
|
+
|
43
|
+
def call
|
44
|
+
@files.each(&:call)
|
45
|
+
@stats = students_t_test
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def students_t_test(series_1=oldest.values, series_2=newest.values)
|
50
|
+
StatisticalTest::TTest.perform(
|
51
|
+
alpha = 0.05,
|
52
|
+
:two_tail,
|
53
|
+
series_1,
|
54
|
+
series_2
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
def significant?
|
59
|
+
@stats[:alternative]
|
60
|
+
end
|
61
|
+
|
62
|
+
def p_value
|
63
|
+
@stats[:p_value].to_f
|
64
|
+
end
|
65
|
+
|
66
|
+
def x_faster
|
67
|
+
FORMAT % (oldest.average/newest.average).to_f
|
68
|
+
end
|
69
|
+
|
70
|
+
def percent_faster
|
71
|
+
FORMAT % (((oldest.average - newest.average) / oldest.average).to_f * 100)
|
72
|
+
end
|
73
|
+
|
74
|
+
def change_direction
|
75
|
+
newest.average < oldest.average ? "FASTER" : "SLOWER"
|
76
|
+
end
|
77
|
+
|
78
|
+
def banner(io = Kernel)
|
79
|
+
io.puts
|
80
|
+
if significant?
|
81
|
+
io.puts "❤️ ❤️ ❤️ (Statistically Significant) ❤️ ❤️ ❤️"
|
82
|
+
else
|
83
|
+
io.puts "👎👎👎(NOT Statistically Significant) 👎👎👎"
|
84
|
+
end
|
85
|
+
io.puts
|
86
|
+
io.puts "[#{newest.name}] #{newest.desc.inspect} - (#{newest.average} seconds)"
|
87
|
+
io.puts " #{change_direction} by:"
|
88
|
+
io.puts " #{x_faster}x [older/newer]"
|
89
|
+
io.puts " #{percent_faster}\% [(older - newer) / older * 100]"
|
90
|
+
io.puts "[#{oldest.name}] #{oldest.desc.inspect} - (#{oldest.average} seconds)"
|
91
|
+
io.puts
|
92
|
+
io.puts "Iterations per sample: #{ENV["TEST_COUNT"]}"
|
93
|
+
io.puts "Samples: #{newest.values.length}"
|
94
|
+
io.puts "P-value: #{p_value}"
|
95
|
+
io.puts "Is significant? (P-value < 0.05): #{significant?}"
|
96
|
+
io.puts
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|