derailed_benchmarks 0.0.0 → 1.0.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/.gitignore +4 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +133 -0
- data/README.md +415 -51
- data/Rakefile +27 -0
- data/bin/derailed +84 -0
- data/derailed_benchmarks.gemspec +6 -1
- data/lib/derailed_benchmarks.rb +32 -0
- data/lib/derailed_benchmarks/auth_helper.rb +32 -0
- data/lib/derailed_benchmarks/auth_helpers/devise.rb +36 -0
- data/lib/derailed_benchmarks/core_ext/kernel_require.rb +72 -0
- data/lib/derailed_benchmarks/require_tree.rb +45 -0
- data/lib/derailed_benchmarks/tasks.rb +71 -110
- data/lib/derailed_benchmarks/version.rb +1 -1
- data/test/derailed_benchmarks/core_ext/kernel_require_test.rb +29 -0
- data/test/derailed_benchmarks/require_tree_test.rb +54 -0
- data/test/derailed_test.rb +12 -0
- data/test/fixtures/require/child_one.rb +4 -0
- data/test/fixtures/require/child_two.rb +9 -0
- data/test/fixtures/require/parent_one.rb +6 -0
- data/test/fixtures/require/raise_child.rb +4 -0
- data/test/fixtures/require/relative_child.rb +2 -0
- data/test/integration/tasks_test.rb +82 -0
- data/test/rails_app/Rakefile +7 -0
- data/test/rails_app/app/assets/javascripts/authenticated.js +2 -0
- data/test/rails_app/app/assets/stylesheets/authenticated.css +4 -0
- data/test/rails_app/app/controllers/application_controller.rb +9 -0
- data/test/rails_app/app/controllers/authenticated_controller.rb +12 -0
- data/test/rails_app/app/controllers/pages_controller.rb +7 -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 +11 -0
- data/test/rails_app/app/views/authenticated/index.html.erb +1 -0
- data/test/rails_app/app/views/layouts/application.html.erb +14 -0
- data/test/rails_app/app/views/pages/index.html.erb +1 -0
- data/test/rails_app/config.ru +4 -0
- data/test/rails_app/config/application.rb +48 -0
- data/test/rails_app/config/boot.rb +10 -0
- data/test/rails_app/config/database.yml +22 -0
- data/test/rails_app/config/environment.rb +5 -0
- data/test/rails_app/config/environments/development.rb +25 -0
- data/test/rails_app/config/environments/production.rb +49 -0
- data/test/rails_app/config/environments/test.rb +35 -0
- data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/test/rails_app/config/initializers/devise.rb +256 -0
- data/test/rails_app/config/initializers/inflections.rb +10 -0
- data/test/rails_app/config/initializers/mime_types.rb +5 -0
- data/test/rails_app/config/initializers/secret_token.rb +7 -0
- data/test/rails_app/config/initializers/session_store.rb +8 -0
- data/test/rails_app/config/locales/devise.en.yml +59 -0
- data/test/rails_app/config/locales/en.yml +9 -0
- data/test/rails_app/config/locales/es.yml +10 -0
- data/test/rails_app/config/routes.rb +64 -0
- data/test/rails_app/db/migrate/20141210070547_devise_create_users.rb +42 -0
- data/test/rails_app/db/schema.rb +34 -0
- data/test/rails_app/perf.rake +4 -0
- data/test/rails_app/public/404.html +26 -0
- data/test/rails_app/public/422.html +26 -0
- data/test/rails_app/public/500.html +26 -0
- data/test/rails_app/public/favicon.ico +0 -0
- data/test/rails_app/public/javascripts/application.js +2 -0
- data/test/rails_app/public/javascripts/controls.js +965 -0
- data/test/rails_app/public/javascripts/dragdrop.js +974 -0
- data/test/rails_app/public/javascripts/effects.js +1123 -0
- data/test/rails_app/public/javascripts/prototype.js +6001 -0
- data/test/rails_app/public/javascripts/rails.js +202 -0
- data/test/rails_app/public/stylesheets/.gitkeep +0 -0
- data/test/rails_app/script/rails +6 -0
- data/test/support/integration_case.rb +5 -0
- data/test/test_helper.rb +53 -0
- metadata +198 -6
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler'
|
4
|
+
require "bundler/gem_tasks"
|
5
|
+
|
6
|
+
begin
|
7
|
+
Bundler.setup(:default, :development, :test)
|
8
|
+
rescue Bundler::BundlerError => e
|
9
|
+
$stderr.puts e.message
|
10
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
11
|
+
exit e.status_code
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'rake'
|
15
|
+
|
16
|
+
require 'rake/testtask'
|
17
|
+
|
18
|
+
Rake::TestTask.new(:test) do |t|
|
19
|
+
t.libs << 'lib'
|
20
|
+
t.libs << 'test'
|
21
|
+
t.pattern = 'test/**/*_test.rb'
|
22
|
+
t.verbose = false
|
23
|
+
end
|
24
|
+
|
25
|
+
task default: :test
|
26
|
+
|
27
|
+
|
data/bin/derailed
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
unless File.respond_to? :realpath
|
4
|
+
class File #:nodoc:
|
5
|
+
def self.realpath path
|
6
|
+
return realpath(File.readlink(path)) if symlink?(path)
|
7
|
+
path
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
lib = File.expand_path(File.dirname(File.realpath(__FILE__)) + '/../lib')
|
12
|
+
$: << lib
|
13
|
+
|
14
|
+
|
15
|
+
require File.join(lib, 'derailed_benchmarks.rb')
|
16
|
+
require 'bundler'
|
17
|
+
Bundler.setup
|
18
|
+
|
19
|
+
require 'thor'
|
20
|
+
|
21
|
+
class DerailedBenchmarkCLI < Thor
|
22
|
+
|
23
|
+
desc "exec", "executes given derailed benchmark"
|
24
|
+
def exec(task = nil)
|
25
|
+
require 'derailed_benchmarks'
|
26
|
+
require 'rake'
|
27
|
+
Rake::TaskManager.record_task_metadata = true
|
28
|
+
require 'derailed_benchmarks/tasks'
|
29
|
+
|
30
|
+
perf_rakefile = File.expand_path(".", "perf.rake")
|
31
|
+
load perf_rakefile if File.exist?(perf_rakefile)
|
32
|
+
|
33
|
+
if task.nil? || task == "--help"
|
34
|
+
Rake.application.tasks.map do |task, n|
|
35
|
+
next unless task.comment
|
36
|
+
puts " $ derailed exec #{task.name} # #{task.comment}"
|
37
|
+
end
|
38
|
+
else
|
39
|
+
task = "perf:#{task}" unless Rake::Task.task_defined?(task)
|
40
|
+
Rake::Task[task].invoke
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "bundle:objects", "measures objects created by gems"
|
45
|
+
define_method(:"bundle:objects") do |env = "production"|
|
46
|
+
setup_bundler!
|
47
|
+
env = [:default] + env.split(",")
|
48
|
+
puts "Measuring objects created by gems in groups #{ env.inspect }"
|
49
|
+
require 'memory_profiler'
|
50
|
+
report = MemoryProfiler.report do
|
51
|
+
Bundler.require(*env)
|
52
|
+
end
|
53
|
+
report.pretty_print
|
54
|
+
end
|
55
|
+
|
56
|
+
map :"bundler:objects" => :"bundle:objects"
|
57
|
+
|
58
|
+
desc "bundle:mem", "measures memory used by gems at boot time"
|
59
|
+
define_method(:"bundle:mem") do |env = "production"|
|
60
|
+
env = [:default] + env.split(",")
|
61
|
+
require 'get_process_mem'
|
62
|
+
mem = GetProcessMem.new
|
63
|
+
require 'derailed_benchmarks/core_ext/kernel_require'
|
64
|
+
before = mem.mb
|
65
|
+
setup_bundler!
|
66
|
+
Bundler.require(*env)
|
67
|
+
after = mem.mb
|
68
|
+
TOP_REQUIRE.print_sorted_children
|
69
|
+
end
|
70
|
+
map :"bundler:mem" => :"bundle:mem"
|
71
|
+
|
72
|
+
private
|
73
|
+
def setup_bundler!
|
74
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
75
|
+
require 'bundler/setup'
|
76
|
+
|
77
|
+
begin
|
78
|
+
require 'rails/all'
|
79
|
+
rescue LoadError
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
DerailedBenchmarkCLI.start(ARGV)
|
data/derailed_benchmarks.gemspec
CHANGED
@@ -21,5 +21,10 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.add_dependency "memory_profiler", "~> 0"
|
22
22
|
gem.add_dependency "get_process_mem", "~> 0"
|
23
23
|
gem.add_dependency "benchmark-ips", "~> 2"
|
24
|
-
|
24
|
+
gem.add_dependency "rack", "~> 1"
|
25
|
+
gem.add_dependency "rake", "~> 10.4"
|
25
26
|
|
27
|
+
gem.add_development_dependency "capybara", "~> 2"
|
28
|
+
gem.add_development_dependency "rails", "~> 3"
|
29
|
+
gem.add_development_dependency "devise", "~> 3"
|
30
|
+
end
|
data/lib/derailed_benchmarks.rb
CHANGED
@@ -1,2 +1,34 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
require 'rack/test'
|
4
|
+
require 'rack/file'
|
5
|
+
require 'benchmark/ips'
|
6
|
+
require 'get_process_mem'
|
7
|
+
require 'bundler'
|
8
|
+
|
1
9
|
module DerailedBenchmarks
|
10
|
+
def self.gem_is_bundled?(name)
|
11
|
+
specs = ::Bundler.locked_gems.specs.each_with_object({}) {|spec, hash| hash[spec.name] = spec }
|
12
|
+
specs[name]
|
13
|
+
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
attr_accessor :auth
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.add_auth(app)
|
20
|
+
if use_auth = ENV['USE_AUTH']
|
21
|
+
puts "Auth: #{use_auth}"
|
22
|
+
auth.add_app(app)
|
23
|
+
else
|
24
|
+
app
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
require 'derailed_benchmarks/require_tree'
|
30
|
+
require 'derailed_benchmarks/auth_helper'
|
31
|
+
|
32
|
+
if DerailedBenchmarks.gem_is_bundled?("devise")
|
33
|
+
DerailedBenchmarks.auth = DerailedBenchmarks::AuthHelpers::Devise.new
|
2
34
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module DerailedBenchmarks
|
4
|
+
# Base helper class. Can be used to authenticate different strategies
|
5
|
+
# The root app will be wrapped by an authentication action
|
6
|
+
class AuthHelper
|
7
|
+
attr_reader :app
|
8
|
+
|
9
|
+
# Put any coded needed to set up or initialize your authentication module here
|
10
|
+
def setup
|
11
|
+
raise "Must subclass"
|
12
|
+
end
|
13
|
+
|
14
|
+
# Gets called for every request. Place all auth logic here.
|
15
|
+
# Return value is expected to be an valid Rack response array.
|
16
|
+
# If you do not manually `app.call(env)` here, the client app
|
17
|
+
# will never be called.
|
18
|
+
def call(env)
|
19
|
+
raise "Must subclass"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns self and sets the target app
|
23
|
+
def add_app(app)
|
24
|
+
raise "App is required argument" unless app
|
25
|
+
@app = app
|
26
|
+
setup
|
27
|
+
self
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
require 'derailed_benchmarks/auth_helpers/devise'
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module DerailedBenchmarks
|
2
|
+
class AuthHelpers
|
3
|
+
# Devise helper for authenticating requests
|
4
|
+
# Setup adds necessarry test methods, user provides a sample user.
|
5
|
+
# The authenticate method is called on every request when authentication is enabled
|
6
|
+
class Devise < AuthHelper
|
7
|
+
attr_accessor :user
|
8
|
+
|
9
|
+
# Include devise test helpers and turn on test mode
|
10
|
+
# We need to do this on the class level
|
11
|
+
def setup
|
12
|
+
# self.class.instance_eval do
|
13
|
+
require 'devise'
|
14
|
+
require 'warden'
|
15
|
+
extend ::Warden::Test::Helpers
|
16
|
+
extend ::Devise::TestHelpers
|
17
|
+
Warden.test_mode!
|
18
|
+
# end
|
19
|
+
end
|
20
|
+
|
21
|
+
def user
|
22
|
+
@user ||= begin
|
23
|
+
password = SecureRandom.hex
|
24
|
+
User.first_or_create!(email: "#{SecureRandom.hex}@example.com", password: password, password_confirmation: password)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Logs the user in, then call the parent app
|
29
|
+
def call(env)
|
30
|
+
login_as(user)
|
31
|
+
app.call(env)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'get_process_mem'
|
2
|
+
require 'derailed_benchmarks/require_tree'
|
3
|
+
|
4
|
+
ENV['CUT_OFF'] ||= "0.3"
|
5
|
+
|
6
|
+
# This file contains classes and monkey patches to measure the amount of memory
|
7
|
+
# useage requiring an individual file adds.
|
8
|
+
|
9
|
+
# Monkey patch kernel to ensure that all `require` calls call the same
|
10
|
+
# method
|
11
|
+
module Kernel
|
12
|
+
alias :original_require :require
|
13
|
+
REQUIRE_STACK = []
|
14
|
+
|
15
|
+
def require(file)
|
16
|
+
Kernel.require(file)
|
17
|
+
end
|
18
|
+
|
19
|
+
# This breaks things, not sure how to fix
|
20
|
+
# def require_relative(file)
|
21
|
+
# Kernel.require_relative(file)
|
22
|
+
# end
|
23
|
+
class << self
|
24
|
+
alias :original_require :require
|
25
|
+
# alias :original_require_relative :require_relative
|
26
|
+
end
|
27
|
+
|
28
|
+
# The core extension we use to measure require time of all requires
|
29
|
+
# When a file is required we create a tree node with its file name.
|
30
|
+
# We then push it onto a stack, this is because requiring a file can
|
31
|
+
# require other files before it is finished.
|
32
|
+
#
|
33
|
+
# When a child file is required, a tree node is created and the child file
|
34
|
+
# is pushed onto the parents tree. We then repeat the process as child
|
35
|
+
# files may require additional files.
|
36
|
+
#
|
37
|
+
# When a require returns we remove it from the require stack so we don't
|
38
|
+
# accidentally push additional children nodes to it. We then store the
|
39
|
+
# memory cost of the require in the tree node.
|
40
|
+
def self.measure_memory_impact(file, &block)
|
41
|
+
mem = GetProcessMem.new
|
42
|
+
node = DerailedBenchmarks::RequireTree.new(file)
|
43
|
+
|
44
|
+
parent = REQUIRE_STACK.last
|
45
|
+
parent << node
|
46
|
+
REQUIRE_STACK.push(node)
|
47
|
+
begin
|
48
|
+
before = mem.mb
|
49
|
+
block.call file
|
50
|
+
ensure
|
51
|
+
REQUIRE_STACK.pop # node
|
52
|
+
after = mem.mb
|
53
|
+
end
|
54
|
+
node.cost = after - before
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Top level node that will store all require information for the entire app
|
59
|
+
TOP_REQUIRE = DerailedBenchmarks::RequireTree.new("TOP")
|
60
|
+
REQUIRE_STACK.push(TOP_REQUIRE)
|
61
|
+
|
62
|
+
Kernel.define_singleton_method(:require) do |file|
|
63
|
+
measure_memory_impact(file) { |file| original_require(file) }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Don't forget to assign a cost to the top level
|
67
|
+
cost_before_requiring_anything = GetProcessMem.new.mb
|
68
|
+
TOP_REQUIRE.cost = cost_before_requiring_anything
|
69
|
+
def TOP_REQUIRE.print_sorted_children(*args)
|
70
|
+
self.cost = GetProcessMem.new.mb - self.cost
|
71
|
+
super
|
72
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Tree structure used to store and sort require memory costs
|
2
|
+
# RequireTree.new('get_process_mem')
|
3
|
+
module DerailedBenchmarks
|
4
|
+
class RequireTree
|
5
|
+
attr_reader :name
|
6
|
+
attr_accessor :cost
|
7
|
+
|
8
|
+
def initialize(name)
|
9
|
+
@name = name
|
10
|
+
@children = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def <<(tree)
|
14
|
+
@children[tree.name.to_s] = tree
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](name)
|
18
|
+
@children[name.to_s]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns array of child nodes
|
22
|
+
def children
|
23
|
+
@children.values
|
24
|
+
end
|
25
|
+
|
26
|
+
def cost
|
27
|
+
@cost || 0
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns sorted array of child nodes from Largest to Smallest
|
31
|
+
def sorted_children
|
32
|
+
children.sort { |c1, c2| c2.cost <=> c1.cost }
|
33
|
+
end
|
34
|
+
|
35
|
+
# Recursively prints all child nodes
|
36
|
+
def print_sorted_children(level = 0, out = STDOUT)
|
37
|
+
return if cost < ENV['CUT_OFF'].to_f
|
38
|
+
out.puts " " * level + "#{name}: #{cost.round(4)} mb"
|
39
|
+
level += 1
|
40
|
+
sorted_children.each do |child|
|
41
|
+
child.print_sorted_children(level, out)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -1,14 +1,6 @@
|
|
1
|
-
namespace :perf do
|
2
|
-
task :setup do
|
3
|
-
require 'benchmark/ips'
|
4
|
-
require 'rack/file'
|
5
|
-
require 'time'
|
6
|
-
require 'rack/test'
|
7
|
-
|
8
|
-
require 'get_process_mem'
|
9
|
-
|
10
|
-
TEST_COUNT = (ENV['TEST_COUNT'] || ENV['CNT'] || 1_000).to_i
|
11
1
|
|
2
|
+
namespace :perf do
|
3
|
+
task :rails_load do
|
12
4
|
ENV["RAILS_ENV"] ||= "production"
|
13
5
|
ENV['RACK_ENV'] = ENV["RAILS_ENV"]
|
14
6
|
ENV["DISABLE_SPRING"] = "true"
|
@@ -17,49 +9,90 @@ namespace :perf do
|
|
17
9
|
|
18
10
|
ENV['LOG_LEVEL'] = "FATAL"
|
19
11
|
|
20
|
-
'
|
12
|
+
require 'rails'
|
13
|
+
|
14
|
+
puts "Booting: #{Rails.env}"
|
15
|
+
|
16
|
+
%W{ . lib test config }.each do |file|
|
17
|
+
$LOAD_PATH << file
|
18
|
+
end
|
21
19
|
|
22
20
|
require 'application'
|
23
21
|
|
24
22
|
Rails.env = ENV["RAILS_ENV"]
|
25
23
|
|
26
|
-
|
27
|
-
# puts APP.method(:initialize!).source_location
|
24
|
+
DERAILED_APP = Rails.application
|
28
25
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
26
|
+
if DERAILED_APP.respond_to?(:initialized?)
|
27
|
+
DERAILED_APP.initialize! unless DERAILED_APP.initialized?
|
28
|
+
else
|
29
|
+
DERAILED_APP.initialize! unless DERAILED_APP.instance_variable_get(:@initialized)
|
30
|
+
end
|
33
31
|
|
34
|
-
|
32
|
+
if defined? ActiveRecord
|
33
|
+
if defined? ActiveRecord::Tasks::DatabaseTasks
|
34
|
+
ActiveRecord::Tasks::DatabaseTasks.create_current
|
35
|
+
else # Rails 3.2
|
36
|
+
raise "No valid database for #{ENV['RAILS_ENV']}, please create one" unless ActiveRecord::Base.connection.active?.inspect
|
37
|
+
end
|
35
38
|
|
39
|
+
ActiveRecord::Migrator.migrations_paths = DERAILED_APP.paths['db/migrate'].to_a
|
40
|
+
ActiveRecord::Migration.verbose = true
|
41
|
+
ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, nil)
|
42
|
+
end
|
36
43
|
|
37
|
-
|
44
|
+
DERAILED_APP.config.consider_all_requests_local = true
|
45
|
+
end
|
38
46
|
|
39
|
-
|
47
|
+
task :rack_load do
|
48
|
+
puts "You're not using Rails"
|
49
|
+
puts "You need to tell derailed how to boot your app"
|
50
|
+
puts "In your perf.rake add:"
|
51
|
+
puts
|
52
|
+
puts "namespace :perf do"
|
53
|
+
puts " task :rack_load do"
|
54
|
+
puts " # DERAILED_APP = your code here"
|
55
|
+
puts " end"
|
56
|
+
puts "end"
|
57
|
+
end
|
58
|
+
|
59
|
+
task :setup do
|
60
|
+
if DerailedBenchmarks.gem_is_bundled?("railties")
|
61
|
+
Rake::Task["perf:rails_load"].invoke
|
62
|
+
else
|
63
|
+
Rake::Task["perf:rack_load"].invoke
|
64
|
+
end
|
65
|
+
|
66
|
+
TEST_COUNT = (ENV['TEST_COUNT'] || ENV['CNT'] || 1_000).to_i
|
67
|
+
PATH_TO_HIT = ENV["PATH_TO_HIT"] || ENV['ENDPOINT'] || "/"
|
68
|
+
puts "Endpoint: #{ PATH_TO_HIT.inspect }"
|
40
69
|
|
41
|
-
|
70
|
+
DERAILED_APP = DerailedBenchmarks.add_auth(DERAILED_APP)
|
42
71
|
if server = ENV["USE_SERVER"]
|
43
72
|
@port = (3000..3900).to_a.sample
|
73
|
+
puts "Port: #{ @port.inspect }"
|
74
|
+
puts "Server: #{ server.inspect }"
|
44
75
|
thread = Thread.new do
|
45
|
-
Rack::Server.start(app:
|
76
|
+
Rack::Server.start(app: DERAILED_APP, :Port => @port, environment: "none", server: server)
|
46
77
|
end
|
47
78
|
sleep 1
|
48
79
|
|
49
|
-
def call_app
|
50
|
-
|
51
|
-
|
80
|
+
def call_app(path = File.join("/", PATH_TO_HIT))
|
81
|
+
cmd = "curl http://localhost:#{@port}#{path} -s --fail 2>&1"
|
82
|
+
response = `#{cmd}`
|
83
|
+
raise "Bad request to #{cmd.inspect} Response:\n#{ response.inspect }" unless $?.success?
|
52
84
|
end
|
53
85
|
else
|
86
|
+
@app = Rack::MockRequest.new(DERAILED_APP)
|
87
|
+
|
54
88
|
def call_app
|
55
89
|
response = @app.get(PATH_TO_HIT)
|
56
|
-
raise "Bad request: #{response.body}" unless response.status == 200
|
90
|
+
raise "Bad request: #{ response.body }" unless response.status == 200
|
57
91
|
response
|
58
92
|
end
|
59
93
|
end
|
60
94
|
end
|
61
95
|
|
62
|
-
|
63
96
|
desc "hits the url TEST_COUNT times"
|
64
97
|
task :test => [:setup] do
|
65
98
|
Benchmark.bm { |x|
|
@@ -71,9 +104,15 @@ namespace :perf do
|
|
71
104
|
}
|
72
105
|
end
|
73
106
|
|
107
|
+
desc "stackprof"
|
74
108
|
task :stackprof => [:setup] do
|
75
109
|
# [:wall, :cpu, :object]
|
76
|
-
|
110
|
+
begin
|
111
|
+
require 'stackprof'
|
112
|
+
rescue LoadError
|
113
|
+
raise "Add stackprof to your gemfile to continue `gem 'stackprof', group: :development`"
|
114
|
+
end
|
115
|
+
TEST_COUNT = (ENV["TEST_COUNT"] ||= "100").to_i
|
77
116
|
file = "tmp/#{Time.now.iso8601}-stackprof-cpu-myapp.dump"
|
78
117
|
StackProf.run(mode: :cpu, out: file) do
|
79
118
|
Rake::Task["perf:test"].invoke
|
@@ -84,86 +123,11 @@ namespace :perf do
|
|
84
123
|
end
|
85
124
|
|
86
125
|
task :kernel_require_patch do
|
87
|
-
require '
|
88
|
-
|
89
|
-
class RequireTree
|
90
|
-
attr_reader :name
|
91
|
-
attr_accessor :cost
|
92
|
-
|
93
|
-
def initialize(name)
|
94
|
-
@name = name
|
95
|
-
@children = {}
|
96
|
-
end
|
97
|
-
|
98
|
-
def <<(tree)
|
99
|
-
@children[tree.name] = tree
|
100
|
-
end
|
101
|
-
|
102
|
-
def [](name)
|
103
|
-
@children[name]
|
104
|
-
end
|
105
|
-
|
106
|
-
def children
|
107
|
-
@children.values
|
108
|
-
end
|
109
|
-
|
110
|
-
def cost
|
111
|
-
@cost || 0
|
112
|
-
end
|
113
|
-
|
114
|
-
def sorted_children
|
115
|
-
children.sort { |c1, c2| c2.cost <=> c1.cost }
|
116
|
-
end
|
117
|
-
|
118
|
-
def print_sorted_children(level = 0)
|
119
|
-
return if cost < ENV['CUT_OFF'].to_f
|
120
|
-
puts " " * level + "#{name}: #{cost.round(4)} mb"
|
121
|
-
level += 1
|
122
|
-
sorted_children.each do |child|
|
123
|
-
child.print_sorted_children(level)
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
module Kernel
|
131
|
-
alias :original_require :require
|
132
|
-
REQUIRE_STACK = []
|
133
|
-
|
134
|
-
def require file
|
135
|
-
Kernel.require(file)
|
136
|
-
end
|
137
|
-
|
138
|
-
class << self
|
139
|
-
alias :original_require :require
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
TOP_REQUIRE = RequireTree.new("TOP")
|
144
|
-
REQUIRE_STACK.push(TOP_REQUIRE)
|
145
|
-
|
146
|
-
Kernel.define_singleton_method(:require) do |file|
|
147
|
-
mem = GetProcessMem.new
|
148
|
-
node = RequireTree.new(file)
|
149
|
-
|
150
|
-
parent = REQUIRE_STACK.last
|
151
|
-
parent << node
|
152
|
-
REQUIRE_STACK.push(node)
|
153
|
-
begin
|
154
|
-
before = mem.mb
|
155
|
-
original_require file
|
156
|
-
ensure
|
157
|
-
REQUIRE_STACK.pop # node
|
158
|
-
after = mem.mb
|
159
|
-
end
|
160
|
-
node.cost = after - before
|
161
|
-
end
|
126
|
+
require 'derailed_benchmarks/core_ext/kernel_require.rb'
|
162
127
|
end
|
163
128
|
|
164
129
|
desc "show memory usage caused by invoking require per gem"
|
165
|
-
task :
|
166
|
-
ENV['CUT_OFF'] ||= "0.3"
|
130
|
+
task :mem => [:kernel_require_patch, :setup] do
|
167
131
|
puts "## Impact of `require <file>` on RAM"
|
168
132
|
puts
|
169
133
|
puts "Showing all `require <file>` calls that consume #{ENV['CUT_OFF']} mb or more of RSS"
|
@@ -176,9 +140,7 @@ namespace :perf do
|
|
176
140
|
|
177
141
|
call_app
|
178
142
|
|
179
|
-
TOP_REQUIRE.
|
180
|
-
child.print_sorted_children
|
181
|
-
end
|
143
|
+
TOP_REQUIRE.print_sorted_children
|
182
144
|
end
|
183
145
|
|
184
146
|
desc "outputs ram usage over time"
|
@@ -273,7 +235,7 @@ namespace :perf do
|
|
273
235
|
|
274
236
|
|
275
237
|
desc "profiles ruby allocation"
|
276
|
-
task :
|
238
|
+
task :objects => [:setup] do
|
277
239
|
require 'memory_profiler'
|
278
240
|
call_app
|
279
241
|
GC.start
|
@@ -287,5 +249,4 @@ namespace :perf do
|
|
287
249
|
end
|
288
250
|
report.pretty_print
|
289
251
|
end
|
290
|
-
|
291
252
|
end
|