derailed_benchmarks 0.0.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|