derailed_benchmarks 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: de696c65bb7e1fd25ec48fed45e6c8b6d7051c3f
4
+ data.tar.gz: 60f83ef96828857be4e501da08d95a449ab71a00
5
+ SHA512:
6
+ metadata.gz: 0852320d09632e4bb7a4e47dad84128708fa25b01c5ccd007d1ff66a4e05d1ed93097442759cee7d043dd3240d04f618a7b3e320b4e6eea0ac24fe1a5334bdc2
7
+ data.tar.gz: 085ea3fde49d10c5cc2c23d77a8c2891cb6888b33c176f573d027268260e6e8d14aee0f91b9d5eccc5993696c27ebe4b3683e3eef43e75b878d5bf665aea79af
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .DS_Store
data/README.md ADDED
@@ -0,0 +1,134 @@
1
+ ## Derailed Benchmarks
2
+
3
+ A series of things you can use to benchmark a Rails app
4
+
5
+ ## Install
6
+
7
+ Put this in your gemfile:
8
+
9
+ ```
10
+ gem 'derailed_benchmarks' group: :development
11
+ ```
12
+
13
+ Then run `$ bundle install`.
14
+
15
+ This part is **important** run this command to create a `perf.rake`
16
+
17
+ ```
18
+ $ cat << EOF > perf.rake
19
+ require 'bundler'
20
+ Bundler.setup
21
+
22
+ require 'derailed_benchmarks'
23
+ require 'derailed_benchmarks/tasks'
24
+ EOF
25
+ ```
26
+
27
+ The file should look like this:
28
+
29
+ ```
30
+ $ cat perf.rake
31
+ require 'bundler'
32
+ Bundler.setup
33
+
34
+ require 'derailed_benchmarks'
35
+ require 'derailed_benchmarks/tasks'
36
+ ```
37
+
38
+ This is done so the benchmarks will be loaded before your application, this is important for some benchmarks and less for others. This also prevents you from accidentally loading these benchmarks when you don't need them.
39
+
40
+ ## Run
41
+
42
+ To find out the tasks available you can use `$ rake -f perf.rake -T` which essentially says use the file `perf.rake` and list all the tasks.
43
+
44
+ ```
45
+ $ rake -f perf.rake -T
46
+ rake perf:allocated_objects # outputs allocated object diff after app is called TEST_COUNT times
47
+ rake perf:ips # ips
48
+ rake perf:mem # profiles ruby allocation
49
+ rake perf:ram_over_time # outputs ram usage over time
50
+ rake perf:require_bench # show memory usage caused by invoking require per gem
51
+ rake perf:stackprof # sampling stack time
52
+ rake perf:test # hits the url TEST_COUNT times
53
+ ```
54
+
55
+ All the rake tasks accept configuration in the form of environment variables. For example, this command will measure the time it takes to hit your site `100,000` times:
56
+
57
+ ```
58
+ $ rake -f perf.rake perf:test TEST_COUNT=100_000
59
+ ```
60
+
61
+
62
+ ## Config
63
+
64
+ Here are the common environment variables.
65
+
66
+ ### PATH_TO_HIT
67
+
68
+ By default tasks will hit your homepage `/`. If you want to hit a different url use `PATH_TO_HIT` for example if you wanted to go to `users/new` you can execute:
69
+
70
+ ```
71
+ PATH_TO_HIT=/users/new
72
+ ```
73
+
74
+ ### USE_SERVER
75
+
76
+ All tests are run without a webserver by default, if you want to use a webserver set `USE_SERVER` to a Rack::Server compliant server, such as `webrick`.
77
+
78
+ ```
79
+ USE_SERVER=webrick
80
+ ```
81
+
82
+ ### TEST_COUNT
83
+
84
+ If the test contains an interation (most of them do), control how many times the test will loop before exiting with `TEST_COUNT`. To run `1` time you can execute
85
+
86
+ ```
87
+ TEST_COUNT=1
88
+ ```
89
+
90
+ Note some tasks have different defaults.
91
+
92
+
93
+ ## Task Specific notes
94
+
95
+
96
+ ### perf:mem
97
+
98
+ This task uses `memory_profiler` to see where memory is allocated while it is running. By default it will iterate once
99
+
100
+
101
+ ### perf:ips
102
+
103
+ Determines the number of times your app can serve a web request each second (iterations per second). Higher number is better. Note this will be much larger if you do not use a server.
104
+
105
+
106
+ ### perf:ram_over_time
107
+
108
+ Your app will use memory differently over time, this task records RSS memory usage every 5 seconds and outputs the value to STDOUT and to a file in `tmp/`. You can use this to build graphs (https://drive.google.com).
109
+
110
+
111
+ ### perf:require_bench
112
+
113
+ Shows the amount of memory (RAM) each library takes up when it is required.
114
+
115
+
116
+ ```
117
+ action_controller/railtie: 1.06 mb
118
+ action_controller: 0.72 mb
119
+ action_controller/metal/live: 0.38 mb
120
+ action_dispatch/http/response: 0.17 mb
121
+ rack/request: 0.05 mb
122
+ ```
123
+
124
+
125
+ ## License
126
+
127
+ MIT
128
+
129
+
130
+ ## Acknowledgements
131
+
132
+ Most of the commands are wrappers around other libraries, go check them out. Also thanks to [@tenderlove](https://twitter.com/tenderlove) as I cribbed some of the Rails init code in `$ rake perf:setup` from one of his projects.
133
+
134
+ kthksbye [@schneems](https://twitter.com/schneems)
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'derailed_benchmarks/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "derailed_benchmarks"
8
+ gem.version = DerailedBenchmarks::VERSION
9
+ gem.authors = ["Richard Schneeman"]
10
+ gem.email = ["richard.schneeman+rubygems@gmail.com"]
11
+ gem.description = %q{ Go faster, off the Rails }
12
+ gem.summary = %q{ Benchmarks designed to performance test your ENTIRE site }
13
+ gem.homepage = "https://github.com/schneems/derailed_benchmarks"
14
+ gem.license = "MIT"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_dependency "memory_profiler", "~> 0"
22
+ gem.add_dependency "get_process_mem", "~> 0"
23
+ gem.add_dependency "benchmark-ips", "~> 2"
24
+ end
25
+
@@ -0,0 +1,2 @@
1
+ module DerailedBenchmarks
2
+ end
@@ -0,0 +1,291 @@
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
+
12
+ ENV["RAILS_ENV"] ||= "production"
13
+ ENV['RACK_ENV'] = ENV["RAILS_ENV"]
14
+ ENV["DISABLE_SPRING"] = "true"
15
+
16
+ ENV["SECRET_KEY_BASE"] ||= "foofoofoo"
17
+
18
+ ENV['LOG_LEVEL'] = "FATAL"
19
+
20
+ '.:lib:test:config'.split(':').each { |x| $: << x }
21
+
22
+ require 'application'
23
+
24
+ Rails.env = ENV["RAILS_ENV"]
25
+
26
+ APP = Rails.application
27
+ # puts APP.method(:initialize!).source_location
28
+
29
+ APP.initialize! unless APP.initialized?
30
+ ActiveRecord::Migrator.migrations_paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths
31
+ ActiveRecord::Migration.verbose = true
32
+ ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, nil)
33
+
34
+ APP.config.consider_all_requests_local = true
35
+
36
+
37
+ @app = Rack::MockRequest.new(APP)
38
+
39
+ puts "Booting: #{Rails.env}"
40
+
41
+ PATH_TO_HIT = ENV["PATH_TO_HIT"] || "/"
42
+ if server = ENV["USE_SERVER"]
43
+ @port = (3000..3900).to_a.sample
44
+ thread = Thread.new do
45
+ Rack::Server.start(app: APP, :Port => @port, environment: "none", server: server)
46
+ end
47
+ sleep 1
48
+
49
+ def call_app
50
+ `curl http://localhost:#{@port}#{PATH_TO_HIT} -s`
51
+ raise "Bad request: #{response.body}" unless $?.success?
52
+ end
53
+ else
54
+ def call_app
55
+ response = @app.get(PATH_TO_HIT)
56
+ raise "Bad request: #{response.body}" unless response.status == 200
57
+ response
58
+ end
59
+ end
60
+ end
61
+
62
+
63
+ desc "hits the url TEST_COUNT times"
64
+ task :test => [:setup] do
65
+ Benchmark.bm { |x|
66
+ x.report("#{TEST_COUNT} requests") {
67
+ TEST_COUNT.times {
68
+ call_app
69
+ }
70
+ }
71
+ }
72
+ end
73
+
74
+ task :stackprof => [:setup] do
75
+ # [:wall, :cpu, :object]
76
+ require 'stackprof'
77
+ file = "tmp/#{Time.now.iso8601}-stackprof-cpu-myapp.dump"
78
+ StackProf.run(mode: :cpu, out: file) do
79
+ Rake::Task["perf:test"].invoke
80
+ end
81
+ cmd = "stackprof #{file}"
82
+ puts "Running `#{cmd}`. Execute `stackprof --help` for more info"
83
+ puts `#{cmd}`
84
+ end
85
+
86
+ task :kernel_require_patch do
87
+ require 'get_process_mem'
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
162
+ end
163
+
164
+ desc "show memory usage caused by invoking require per gem"
165
+ task :require_bench => [:kernel_require_patch , :setup] do
166
+ ENV['CUT_OFF'] ||= "0.3"
167
+ puts "## Impact of `require <file>` on RAM"
168
+ puts
169
+ puts "Showing all `require <file>` calls that consume #{ENV['CUT_OFF']} mb or more of RSS"
170
+ puts "Configure with `CUT_OFF=0` for all entries or `CUT_OFF=5` for few entries"
171
+
172
+ puts "Note: Files only count against RAM on their first load."
173
+ puts " If multiple libraries require the same file, then"
174
+ puts " the 'cost' only shows up under the first library"
175
+ puts
176
+
177
+ call_app
178
+
179
+ TOP_REQUIRE.sorted_children.each do |child|
180
+ child.print_sorted_children
181
+ end
182
+ end
183
+
184
+ desc "outputs ram usage over time"
185
+ task :ram_over_time => [:setup] do
186
+ puts "PID: #{Process.pid}"
187
+ ram = GetProcessMem.new
188
+ @keep_going = true
189
+ begin
190
+ unless ENV["SKIP_FILE_WRITE"]
191
+ ruby = `ruby -v`
192
+ FileUtils.mkdir_p("tmp")
193
+ file = File.open("tmp/#{Time.now.iso8601}-#{ruby}-memory-#{TEST_COUNT}-times.txt", 'w')
194
+ file.sync = true
195
+ end
196
+
197
+ ram_thread = Thread.new do
198
+ while @keep_going
199
+ mb = ram.mb
200
+ STDOUT.puts mb
201
+ file.puts mb unless ENV["SKIP_FILE_WRITE"]
202
+ sleep 5
203
+ end
204
+ end
205
+
206
+ TEST_COUNT.times {
207
+ call_app
208
+ }
209
+ ensure
210
+ @keep_going = false
211
+ ram_thread.join
212
+ file.close unless ENV["SKIP_FILE_WRITE"]
213
+ end
214
+ end
215
+
216
+ desc "iterations per second"
217
+ task :ips => [:setup] do
218
+ Benchmark.ips do |x|
219
+ x.report("ips") { call_app }
220
+ end
221
+ end
222
+
223
+ desc "outputs GC::Profiler.report data while app is called TEST_COUNT times"
224
+ task :gc => [:setup] do
225
+ GC::Profiler.enable
226
+ TEST_COUNT.times { call_app }
227
+ GC::Profiler.report
228
+ GC::Profiler.disable
229
+ end
230
+
231
+ task :foo => [:setup] do
232
+ require 'objspace'
233
+ call_app
234
+
235
+ before = Hash.new { 0 }
236
+ after = Hash.new { 0 }
237
+ after_size = Hash.new { 0 }
238
+ GC.start
239
+ GC.disable
240
+
241
+ TEST_COUNT.times { call_app }
242
+
243
+ rvalue_size = GC::INTERNAL_CONSTANTS[:RVALUE_SIZE]
244
+ ObjectSpace.each_object do |obj|
245
+ after[obj.class] += 1
246
+ memsize = ObjectSpace.memsize_of(obj) + rvalue_size
247
+ # compensate for API bug
248
+ memsize = rvalue_size if memsize > 100_000_000_000
249
+ after_size[obj.class] += memsize
250
+ end
251
+
252
+ require 'pp'
253
+ pp after.sort {|(k,v), (k2, v2)| v2 <=> v }
254
+ puts "========="
255
+ puts
256
+ puts
257
+ pp after_size.sort {|(k,v), (k2, v2)| v2 <=> v }
258
+ end
259
+
260
+ desc "outputs allocated object diff after app is called TEST_COUNT times"
261
+ task :allocated_objects => [:setup] do
262
+ call_app
263
+ GC.start
264
+ GC.disable
265
+ start = ObjectSpace.count_objects
266
+ TEST_COUNT.times { call_app }
267
+ finish = ObjectSpace.count_objects
268
+ GC.enable
269
+ finish.each do |k,v|
270
+ puts k => (v - start[k]) / TEST_COUNT.to_f
271
+ end
272
+ end
273
+
274
+
275
+ desc "profiles ruby allocation"
276
+ task :mem => [:setup] do
277
+ require 'memory_profiler'
278
+ call_app
279
+ GC.start
280
+
281
+ num = Integer(ENV["TEST_COUNT"] || 1)
282
+ opts = {}
283
+ opts[:ignore_files] = /#{ENV['IGNORE_FILES_REGEXP']}/ if ENV['IGNORE_FILES_REGEXP']
284
+ puts "Running #{num} times"
285
+ report = MemoryProfiler.report(opts) do
286
+ num.times { call_app }
287
+ end
288
+ report.pretty_print
289
+ end
290
+
291
+ end
@@ -0,0 +1,3 @@
1
+ module DerailedBenchmarks
2
+ VERSION = "0.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: derailed_benchmarks
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Richard Schneeman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: memory_profiler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: get_process_mem
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: benchmark-ips
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2'
55
+ description: " Go faster, off the Rails "
56
+ email:
57
+ - richard.schneeman+rubygems@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - README.md
64
+ - derailed_benchmarks.gemspec
65
+ - lib/derailed_benchmarks.rb
66
+ - lib/derailed_benchmarks/tasks.rb
67
+ - lib/derailed_benchmarks/version.rb
68
+ homepage: https://github.com/schneems/derailed_benchmarks
69
+ licenses:
70
+ - MIT
71
+ metadata: {}
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubyforge_project:
88
+ rubygems_version: 2.2.2
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: Benchmarks designed to performance test your ENTIRE site
92
+ test_files: []
93
+ has_rdoc: