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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.travis.yml +13 -0
  4. data/CHANGELOG.md +13 -0
  5. data/Gemfile +9 -0
  6. data/Gemfile.lock +133 -0
  7. data/README.md +415 -51
  8. data/Rakefile +27 -0
  9. data/bin/derailed +84 -0
  10. data/derailed_benchmarks.gemspec +6 -1
  11. data/lib/derailed_benchmarks.rb +32 -0
  12. data/lib/derailed_benchmarks/auth_helper.rb +32 -0
  13. data/lib/derailed_benchmarks/auth_helpers/devise.rb +36 -0
  14. data/lib/derailed_benchmarks/core_ext/kernel_require.rb +72 -0
  15. data/lib/derailed_benchmarks/require_tree.rb +45 -0
  16. data/lib/derailed_benchmarks/tasks.rb +71 -110
  17. data/lib/derailed_benchmarks/version.rb +1 -1
  18. data/test/derailed_benchmarks/core_ext/kernel_require_test.rb +29 -0
  19. data/test/derailed_benchmarks/require_tree_test.rb +54 -0
  20. data/test/derailed_test.rb +12 -0
  21. data/test/fixtures/require/child_one.rb +4 -0
  22. data/test/fixtures/require/child_two.rb +9 -0
  23. data/test/fixtures/require/parent_one.rb +6 -0
  24. data/test/fixtures/require/raise_child.rb +4 -0
  25. data/test/fixtures/require/relative_child.rb +2 -0
  26. data/test/integration/tasks_test.rb +82 -0
  27. data/test/rails_app/Rakefile +7 -0
  28. data/test/rails_app/app/assets/javascripts/authenticated.js +2 -0
  29. data/test/rails_app/app/assets/stylesheets/authenticated.css +4 -0
  30. data/test/rails_app/app/controllers/application_controller.rb +9 -0
  31. data/test/rails_app/app/controllers/authenticated_controller.rb +12 -0
  32. data/test/rails_app/app/controllers/pages_controller.rb +7 -0
  33. data/test/rails_app/app/helpers/application_helper.rb +2 -0
  34. data/test/rails_app/app/helpers/authenticated_helper.rb +2 -0
  35. data/test/rails_app/app/models/user.rb +11 -0
  36. data/test/rails_app/app/views/authenticated/index.html.erb +1 -0
  37. data/test/rails_app/app/views/layouts/application.html.erb +14 -0
  38. data/test/rails_app/app/views/pages/index.html.erb +1 -0
  39. data/test/rails_app/config.ru +4 -0
  40. data/test/rails_app/config/application.rb +48 -0
  41. data/test/rails_app/config/boot.rb +10 -0
  42. data/test/rails_app/config/database.yml +22 -0
  43. data/test/rails_app/config/environment.rb +5 -0
  44. data/test/rails_app/config/environments/development.rb +25 -0
  45. data/test/rails_app/config/environments/production.rb +49 -0
  46. data/test/rails_app/config/environments/test.rb +35 -0
  47. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  48. data/test/rails_app/config/initializers/devise.rb +256 -0
  49. data/test/rails_app/config/initializers/inflections.rb +10 -0
  50. data/test/rails_app/config/initializers/mime_types.rb +5 -0
  51. data/test/rails_app/config/initializers/secret_token.rb +7 -0
  52. data/test/rails_app/config/initializers/session_store.rb +8 -0
  53. data/test/rails_app/config/locales/devise.en.yml +59 -0
  54. data/test/rails_app/config/locales/en.yml +9 -0
  55. data/test/rails_app/config/locales/es.yml +10 -0
  56. data/test/rails_app/config/routes.rb +64 -0
  57. data/test/rails_app/db/migrate/20141210070547_devise_create_users.rb +42 -0
  58. data/test/rails_app/db/schema.rb +34 -0
  59. data/test/rails_app/perf.rake +4 -0
  60. data/test/rails_app/public/404.html +26 -0
  61. data/test/rails_app/public/422.html +26 -0
  62. data/test/rails_app/public/500.html +26 -0
  63. data/test/rails_app/public/favicon.ico +0 -0
  64. data/test/rails_app/public/javascripts/application.js +2 -0
  65. data/test/rails_app/public/javascripts/controls.js +965 -0
  66. data/test/rails_app/public/javascripts/dragdrop.js +974 -0
  67. data/test/rails_app/public/javascripts/effects.js +1123 -0
  68. data/test/rails_app/public/javascripts/prototype.js +6001 -0
  69. data/test/rails_app/public/javascripts/rails.js +202 -0
  70. data/test/rails_app/public/stylesheets/.gitkeep +0 -0
  71. data/test/rails_app/script/rails +6 -0
  72. data/test/support/integration_case.rb +5 -0
  73. data/test/test_helper.rb +53 -0
  74. metadata +198 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: de696c65bb7e1fd25ec48fed45e6c8b6d7051c3f
4
- data.tar.gz: 60f83ef96828857be4e501da08d95a449ab71a00
3
+ metadata.gz: e444f3e6c00f95138b3df5c86995b4126fd89121
4
+ data.tar.gz: cae797098b186ef45c23fb1cdc525c98ecc4cd87
5
5
  SHA512:
6
- metadata.gz: 0852320d09632e4bb7a4e47dad84128708fa25b01c5ccd007d1ff66a4e05d1ed93097442759cee7d043dd3240d04f618a7b3e320b4e6eea0ac24fe1a5334bdc2
7
- data.tar.gz: 085ea3fde49d10c5cc2c23d77a8c2891cb6888b33c176f573d027268260e6e8d14aee0f91b9d5eccc5993696c27ebe4b3683e3eef43e75b878d5bf665aea79af
6
+ metadata.gz: 54796bcc3efc7773af0705cf38fa21f93c1602c5661626a1643b0b425be14eed042bfd5455ea25dcecde47ce7d95837c17549f85dd0026cbb19365efec89a9ba
7
+ data.tar.gz: 398783116407326acf4622869abc85420be8ddb7468e33d7267d1695de63f87ca21f577627d0145fb9ec3f82ec2dc0bc0fda32b1eedb8fee909d04551df86622
data/.gitignore CHANGED
@@ -1 +1,5 @@
1
1
  .DS_Store
2
+ *.gem
3
+ /test/rails_app/tmp/*
4
+ /test/rails_app/log/*
5
+ *.sqlite3
data/.travis.yml ADDED
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0
5
+ - 2.1
6
+ - 2.2
7
+ - ruby-head
8
+
9
+ matrix:
10
+ allow_failures:
11
+ - rvm: 1.9.3
12
+ - rvm: 2.0
13
+ - rvm: ruby-head
data/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # A Log of Changes!
2
+
3
+
4
+ ## [1.0.0] - 2015-15-05
5
+
6
+ - Added `derailed` command line utility. Can be used with just a Gemfile using command `$ derailed bundle:mem` and `$ derailed bundle:objects`. All existing Rake tasks can now be called with `$ derailed exec` such as `$ derailed exec perf:mem`.
7
+ - Changed memory_profiler task to be `perf:objects` instead of `perf:mem`.
8
+ - Changed boot time memory measurement to `perf:mem` instead of `perf:require_bench`
9
+ - Released seperate [derailed](https://github.com/schneems/derailed) gem that is a wrapper for this gem. I.e. installing that gem installs this one. Easier to remember, less words to type. Also means there's no colision using the `derailed` namespace for executables inside of the `derailed_benchmarks`.
10
+
11
+ ## [0.0.0] - 2014-15-08
12
+
13
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development, :test do
6
+ gem "sqlite3", :platform => [:ruby, :mswin, :mingw]
7
+ gem "activerecord-jdbcsqlite3-adapter", '~> 1.3.13', :platform => :jruby
8
+ gem "test-unit", "~> 3.0"
9
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,133 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ derailed_benchmarks (1.0.0)
5
+ benchmark-ips (~> 2)
6
+ get_process_mem (~> 0)
7
+ memory_profiler (~> 0)
8
+ rack (~> 1)
9
+ rake (~> 10.4)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ actionmailer (3.2.21)
15
+ actionpack (= 3.2.21)
16
+ mail (~> 2.5.4)
17
+ actionpack (3.2.21)
18
+ activemodel (= 3.2.21)
19
+ activesupport (= 3.2.21)
20
+ builder (~> 3.0.0)
21
+ erubis (~> 2.7.0)
22
+ journey (~> 1.0.4)
23
+ rack (~> 1.4.5)
24
+ rack-cache (~> 1.2)
25
+ rack-test (~> 0.6.1)
26
+ sprockets (~> 2.2.1)
27
+ activemodel (3.2.21)
28
+ activesupport (= 3.2.21)
29
+ builder (~> 3.0.0)
30
+ activerecord (3.2.21)
31
+ activemodel (= 3.2.21)
32
+ activesupport (= 3.2.21)
33
+ arel (~> 3.0.2)
34
+ tzinfo (~> 0.3.29)
35
+ activeresource (3.2.21)
36
+ activemodel (= 3.2.21)
37
+ activesupport (= 3.2.21)
38
+ activesupport (3.2.21)
39
+ i18n (~> 0.6, >= 0.6.4)
40
+ multi_json (~> 1.0)
41
+ arel (3.0.3)
42
+ bcrypt (3.1.10)
43
+ benchmark-ips (2.1.1)
44
+ builder (3.0.4)
45
+ capybara (2.4.4)
46
+ mime-types (>= 1.16)
47
+ nokogiri (>= 1.3.3)
48
+ rack (>= 1.0.0)
49
+ rack-test (>= 0.5.4)
50
+ xpath (~> 2.0)
51
+ devise (3.4.1)
52
+ bcrypt (~> 3.0)
53
+ orm_adapter (~> 0.1)
54
+ railties (>= 3.2.6, < 5)
55
+ responders
56
+ thread_safe (~> 0.1)
57
+ warden (~> 1.2.3)
58
+ erubis (2.7.0)
59
+ get_process_mem (0.2.0)
60
+ hike (1.2.3)
61
+ i18n (0.7.0)
62
+ journey (1.0.4)
63
+ json (1.8.2)
64
+ mail (2.5.4)
65
+ mime-types (~> 1.16)
66
+ treetop (~> 1.4.8)
67
+ memory_profiler (0.9.0)
68
+ mime-types (1.25.1)
69
+ mini_portile (0.6.2)
70
+ multi_json (1.11.0)
71
+ nokogiri (1.6.6.2)
72
+ mini_portile (~> 0.6.0)
73
+ orm_adapter (0.5.0)
74
+ polyglot (0.3.5)
75
+ power_assert (0.2.3)
76
+ rack (1.4.5)
77
+ rack-cache (1.2)
78
+ rack (>= 0.4)
79
+ rack-ssl (1.3.4)
80
+ rack
81
+ rack-test (0.6.3)
82
+ rack (>= 1.0)
83
+ rails (3.2.21)
84
+ actionmailer (= 3.2.21)
85
+ actionpack (= 3.2.21)
86
+ activerecord (= 3.2.21)
87
+ activeresource (= 3.2.21)
88
+ activesupport (= 3.2.21)
89
+ bundler (~> 1.0)
90
+ railties (= 3.2.21)
91
+ railties (3.2.21)
92
+ actionpack (= 3.2.21)
93
+ activesupport (= 3.2.21)
94
+ rack-ssl (~> 1.3.2)
95
+ rake (>= 0.8.7)
96
+ rdoc (~> 3.4)
97
+ thor (>= 0.14.6, < 2.0)
98
+ rake (10.4.2)
99
+ rdoc (3.12.2)
100
+ json (~> 1.4)
101
+ responders (1.1.0)
102
+ railties (>= 3.2, < 5)
103
+ sprockets (2.2.3)
104
+ hike (~> 1.2)
105
+ multi_json (~> 1.0)
106
+ rack (~> 1.0)
107
+ tilt (~> 1.1, != 1.3.0)
108
+ sqlite3 (1.3.10)
109
+ test-unit (3.0.9)
110
+ power_assert
111
+ thor (0.19.1)
112
+ thread_safe (0.3.5)
113
+ tilt (1.4.1)
114
+ treetop (1.4.15)
115
+ polyglot
116
+ polyglot (>= 0.3.1)
117
+ tzinfo (0.3.44)
118
+ warden (1.2.3)
119
+ rack (>= 1.0)
120
+ xpath (2.0.0)
121
+ nokogiri (~> 1.3)
122
+
123
+ PLATFORMS
124
+ ruby
125
+
126
+ DEPENDENCIES
127
+ activerecord-jdbcsqlite3-adapter (~> 1.3.13)
128
+ capybara (~> 2)
129
+ derailed_benchmarks!
130
+ devise (~> 3)
131
+ rails (~> 3)
132
+ sqlite3
133
+ test-unit (~> 3.0)
data/README.md CHANGED
@@ -1,18 +1,387 @@
1
1
  ## Derailed Benchmarks
2
2
 
3
- A series of things you can use to benchmark a Rails app
3
+ A series of things you can use to benchmark a Rails or Ruby app.
4
+
5
+ ![](http://media.giphy.com/media/lfbxexWy71b6U/giphy.gif)
6
+
7
+ ## Compatibility/Requirements
8
+
9
+ This gem has been tested and is known to work with Rails 3.2+ using Ruby
10
+ 2.1+. Some commands __may__ work on older versions of Ruby, but not all commands are supported.
11
+
12
+ For some benchmarks, not all, you'll need to verify you have a working version of curl on your OS:
13
+
14
+ ```
15
+ $ which curl
16
+ /usr/bin/curl
17
+ $ curl -V
18
+ curl 7.37.1 #...
19
+ ```
4
20
 
5
21
  ## Install
6
22
 
7
23
  Put this in your gemfile:
8
24
 
9
25
  ```
10
- gem 'derailed_benchmarks' group: :development
26
+ gem 'derailed', group: :development
11
27
  ```
12
28
 
13
29
  Then run `$ bundle install`.
14
30
 
15
- This part is **important** run this command to create a `perf.rake`
31
+ While executing your commands you may need to use `bundle exec` before typing the command.
32
+
33
+ To use all profiling methods available also add:
34
+
35
+ ```
36
+ gem "stackprof", group: :development
37
+ ```
38
+
39
+ You must be using Ruby 2.1+ to install these libraries. If you're on an older version of Ruby, what are you waiting for?
40
+
41
+ ## Use
42
+
43
+ There are two ways to use benchmark an app. Derailed can either try to boot your web app and run requests against it while benchmarking, or it can also staically give you more information about your dependencies that are in your your Gemfile. Booting your app will always be more accurate, but if you cannot get your app to run in production locally, you'll still find the static information useful.
44
+
45
+ ## Static Benchmarking
46
+
47
+ This section covers how to get memory information from your Gemfile without having to boot your app.
48
+
49
+ All commands in this section will begin with `$ derailed bundle:`
50
+
51
+ For more information on the relationship between memory and performance please read/watch [How Ruby Uses Memory](http://www.schneems.com/2015/05/11/how-ruby-uses-memory.html).
52
+
53
+ ### Memory used at Require time
54
+
55
+ Each gem you add to your project can increase your memory at boot. You can get visibility into the total memory used by each gem in your Gemfile by running:
56
+
57
+ ```
58
+ $ derailed bundle:mem
59
+ ```
60
+
61
+ This will load each of your gems in your Gemfile and see how much memory they consume when they are required. For example if you're using the `mail` gem. The output might look like this
62
+
63
+ ```
64
+ $ derailed bundle:mem
65
+ TOP: 54.1836 mb
66
+ mail: 18.9688 mb
67
+ mime/types: 17.4453 mb
68
+ mail/field: 0.4023 mb
69
+ mail/message: 0.3906 mb
70
+ action_view/view_paths: 0.4453 mb
71
+ action_view/base: 0.4336 mb
72
+ ```
73
+
74
+ Here we can see that `mail` uses 18mb, with the majority coming from `mime/types`. You can use this information to prune out large dependencies you don't need. Also if you see a large memory use by a gem that you do need, please open up an issue with that library to let them know (be sure to include reproduction instructions). Hopefully as a community we can identify memory hotspots and reduce their impact. Before we can fix performance problems, we need to know where those problems exist.
75
+
76
+ By default this task will only return results from the `:default` and `"production"` groups. If you want a different group you can run with.
77
+
78
+ ```
79
+ $ derailed bundle:mem development
80
+ ```
81
+
82
+ You can use `CUT_OFF=0.3` to only show files that have above a certain memory useage, this can be used to help eliminate noise.
83
+
84
+ Note: This method won't include files in your own app, only items in your Gemfile. For that you'll need to use `derailed exec mem`. See below for more info.
85
+
86
+ ### Objects created at Require time
87
+
88
+ To get more info about the objects, using [memory_profiler](https://github.com/SamSaffron/memory_profiler), created when your dependencies are required you can run:
89
+
90
+ ```
91
+ $ derailed bundle:objects
92
+ ```
93
+
94
+ This will output detailed information about objects created while your dependencies are loaded
95
+
96
+ ```
97
+ Measuring objects created by gems in groups [:default, "production"]
98
+ Total allocated 433895
99
+ Total retained 100556
100
+
101
+ allocated memory by gem
102
+ -----------------------------------
103
+ 24369241 activesupport-4.2.1
104
+ 15560550 mime-types-2.4.3
105
+ 8103432 json-1.8.2
106
+ ```
107
+
108
+ Once you identify a gem that creates a large amout of memory using `$ derailed bundle:mem` you can pull that gem into it's own Gemfile and run `$ derailed bundle:objects` to get detailed information about it. This information can be used by contributors and library authors to identify and eliminate object creation hotspots.
109
+
110
+
111
+ By default this task will only return results from the `:default` and `"production"` groups. If you want a different group you can run with.
112
+
113
+ ```
114
+ $ derailed bundle:objects development
115
+ ```
116
+
117
+ Note: This method won't include files in your own app, only items in your Gemfile. For that you'll need to use `derailed exec objects`. See below for more info.
118
+
119
+
120
+ ## Dynamic app Benchmarking
121
+
122
+ This benchmarking will attempt to boot your Rails app and run benchmarks against it. Unlike the static benchmarking with `$ derailed bundle:*` these will include information about your specific app. The pro is you'll get more information and potentially identify problems in your app code, the con is that it requires you to be able to boot and run your application in a `production` environment locally, which for some apps is non-trivial.
123
+
124
+ You may want to check out [mini-profiler](https://github.com/MiniProfiler/rack-mini-profiler), here's a [mini-profiler walkthrough](http://www.justinweiss.com/blog/2015/05/11/a-new-way-to-understand-your-rails-apps-performance/?utm_source=rubyweekly&utm_medium=email). It's great and does slightly different benchmarking than what you'll find here.
125
+
126
+ ### Running in Production Locally.
127
+
128
+ Before you want to attempt any dynamic benchmarks, you'll need to boot your app in `production` mode. We benchmark using `production` because it is close to your deployed performance. This section is more a collection of tips rather than a de-facto tutorial.
129
+
130
+ For starters try booting into a console:
131
+
132
+ ```
133
+ $ RAILS_ENV=production rails console
134
+ ```
135
+
136
+ You may get errors, complaining about not being able to connect to the `production` database. For this, you can either create a local database with the name of your production database, or you can copy the info from your `development` group to your `production` groupu in your `database.yml`.
137
+
138
+ You may be missing environment variables expected in `production` such as `SECRET_KEY_BASE`. For those you can either commit them to your `.env` file (if you're using one). Or add them directly to the command:
139
+
140
+ ```
141
+ $ SECRET_KEY_BASE=foo RAILS_ENV=production rails console
142
+ ```
143
+
144
+ Once you can boot a console in production, you'll need to be able to boot a server in production
145
+
146
+ ```
147
+ $ RAILS_ENV=production rails server
148
+ ```
149
+
150
+ You may need to disable enforcing SSL or other domain restrictions temporarially. If you do these, don't forget to add them back in before deploying any code (eek!).
151
+
152
+ You can get information from STDOUT if you're using `rails_12factor` gem, or from `logs/production.log` by running
153
+
154
+ ```
155
+ $ tail -f logs/production.log
156
+ ```
157
+
158
+ Once you've fixed all errors and you can run a server in production, you're almost there.
159
+
160
+ ### Running Derailed Exec
161
+
162
+ You can run commands against your app by running `$ derailed exec`. There are sections on setting up Rack and using authenticated requests below. You can see what commands are available by running:
163
+
164
+ ```
165
+ $ derailed exec --help
166
+ $ derailed exec perf:allocated_objects # outputs allocated object diff after app is called TEST_COUNT times
167
+ $ derailed exec perf:gc # outputs GC::Profiler.report data while app is called TEST_COUNT times
168
+ $ derailed exec perf:ips # iterations per second
169
+ $ derailed exec perf:mem # show memory usage caused by invoking require per gem
170
+ $ derailed exec perf:objects # profiles ruby allocation
171
+ $ derailed exec perf:ram_over_time # outputs ram usage over time
172
+ $ derailed exec perf:test # hits the url TEST_COUNT times
173
+ ```
174
+
175
+ Instead of going over each command we'll look at common problems and which commands are best used to diagnose them. Later on we'll cover all of the environment variables you can use to configure derailed benchmarks in it's own section.
176
+
177
+
178
+ ### Is my app leaking memory?
179
+
180
+ If your app appears to be leaking ever increasing amounts of memory, you'll want to first verify if it's an actual unbound "leak" or if it's just using more memory than you want. A true memory leak will increase memory use forever, most apps will increase memory use until they hit a "plateau". To diagnose this you can run:
181
+
182
+ ```
183
+ $ derailed exec perf:ram_over_time
184
+ ```
185
+
186
+ This will boot your app and hit it with requests and output the memory to stdout (and a file under ./tmp). It may look like this:
187
+
188
+ ```
189
+ $ derailed exec perf:ram_over_time
190
+ Booting: production
191
+ Endpoint: "/"
192
+ PID: 78675
193
+ 103.55078125
194
+ 178.45703125
195
+ 179.140625
196
+ 180.3671875
197
+ 182.1875
198
+ 182.55859375
199
+ # ...
200
+ 183.65234375
201
+ 183.26171875
202
+ 183.62109375
203
+ ```
204
+
205
+ Here we can see that while the memory use is increasing, it levels off around 183 MB. You'll want to run this task using ever increasing values of `TEST_COUNT=` for example
206
+
207
+ ```
208
+ $ TEST_COUNT=5000 derailed exec perf:ram_over_time
209
+ $ TEST_COUNT=10_000 derailed exec perf:ram_over_time
210
+ $ TEST_COUNT=20_000 derailed exec perf:ram_over_time
211
+ ```
212
+
213
+ Adjust your counts appropriately so you can get results in a reasonable amount of time. If your memory never levels off, congrats! You've got a memory leak! I recommend copying and pasting values from the file generated into google docs and graphing it so you can get a better sense of the slope of your line.
214
+
215
+ If you don't want it to generate a tmp file with results run with `SKIP_FILE_WRITE=1`.
216
+
217
+ If you're pretty sure that there's a memory leak, but you can't confirm it using this method. Look at the environment variable options below, you can try hitting a different endpoint etc.
218
+
219
+ ## Dissecting a Memory Leak
220
+
221
+ If you've identified a memory leak, or you simply want to see where your memory use is coming from you'll want to use
222
+
223
+ ```
224
+ $ derailed exec perf:objects
225
+ ```
226
+
227
+ This task hits your app and uses memory_profiler to see where objects are created. You'll likely want to run once, then run it with a higher `TEST_COUNT` so that you can see hotspots where objects are created on __EVERY__ request versus just maybe on the first.
228
+
229
+
230
+ ```
231
+ $ TEST_COUNT=10 derailed exec perf:objects
232
+ ```
233
+
234
+ This is an expensive operation, so you likely want to keep the count lowish. Once you've identified a hotspot read [how ruby uses memory](http://www.sitepoint.com/ruby-uses-memory/) for some tips on reducing object allocations.
235
+
236
+ This is is similar to `$ derailed bundle:objects` however it includes objects created at runtime. It's much more useful for actual production performance debugging, the other is more useful for library authors to debug.
237
+
238
+
239
+ ### Memory Is large at boot.
240
+
241
+ Ruby memory typically goes in one direction, up. If your memory is large when you boot the application it will likely only increase. In addition to debugging memory retained from dependencies obtained while running `$ derailed bundle:mem` you'll likely want to see how your own files contribute to memory use.
242
+
243
+ This task does essentially the same thing, however it hits your app with one request to ensure that any last minute `require`-s have been called. To execute you can run:
244
+
245
+
246
+ ```
247
+ $ derailed exec perf:mem
248
+
249
+ TOP: 54.1836 mb
250
+ mail: 18.9688 mb
251
+ mime/types: 17.4453 mb
252
+ mail/field: 0.4023 mb
253
+ mail/message: 0.3906 mb
254
+ action_view/view_paths: 0.4453 mb
255
+ action_view/base: 0.4336 mb
256
+ ```
257
+
258
+ You can use `CUT_OFF=0.3` to only show files that have above a certain memory useage, this can be used to help eliminate noise.
259
+
260
+ If your application code is exremely large at boot consider using `$ derailed exec perf:objects` to debug low level object creation.
261
+
262
+ ## My app is Slow
263
+
264
+ Well...aren't they all. If you've already looked into decreasing object allocations, you'll want to look at where your app is spending the most amount of code. Once you know that, you'll know where to spend your time optimising.
265
+
266
+ One technique is to use a "sampling" stack profiler. This type of profiling looks at what method is being executed at a given interval and records it. At the end of execution it counts all the times a given method was being called and shows you the percent of time spent in each method. This is a very low overhead method to looking into execution time. Ruby 2.1+ has this available in gem form it's called [stackprof](https://github.com/tmm1/stackprof). As you guessed you can run this with derailed benchmarks, first add it to your gemfile `gem "stackprof", group: :development` then execute:
267
+
268
+ ```
269
+ $ derailed exec perf:stackprof
270
+ ==================================
271
+ Mode: cpu(1000)
272
+ Samples: 16067 (1.07% miss rate)
273
+ GC: 2651 (16.50%)
274
+ ==================================
275
+ TOTAL (pct) SAMPLES (pct) FRAME
276
+ 1293 (8.0%) 1293 (8.0%) block in ActionDispatch::Journey::Formatter#missing_keys
277
+ 872 (5.4%) 872 (5.4%) block in ActiveSupport::Inflector#apply_inflections
278
+ 935 (5.8%) 802 (5.0%) ActiveSupport::SafeBuffer#safe_concat
279
+ 688 (4.3%) 688 (4.3%) Temple::Utils#escape_html
280
+ 578 (3.6%) 578 (3.6%) ActiveRecord::Attribute#initialize
281
+ 3541 (22.0%) 401 (2.5%) ActionDispatch::Routing::RouteSet#url_for
282
+ 346 (2.2%) 346 (2.2%) ActiveSupport::SafeBuffer#initialize
283
+ 298 (1.9%) 298 (1.9%) ThreadSafe::NonConcurrentCacheBackend#[]
284
+ 227 (1.4%) 227 (1.4%) block in ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#exec_no_cache
285
+ 218 (1.4%) 218 (1.4%) NewRelic::Agent::Instrumentation::Event#initialize
286
+ 1102 (6.9%) 213 (1.3%) ActiveSupport::Inflector#apply_inflections
287
+ 193 (1.2%) 193 (1.2%) ActionDispatch::Routing::RouteSet::NamedRouteCollection::UrlHelper#deprecate_string_options
288
+ 173 (1.1%) 173 (1.1%) ActiveSupport::SafeBuffer#html_safe?
289
+ 308 (1.9%) 171 (1.1%) NewRelic::Agent::Instrumentation::ActionViewSubscriber::RenderEvent#metric_name
290
+ 159 (1.0%) 159 (1.0%) block in ActiveRecord::Result#hash_rows
291
+ 358 (2.2%) 153 (1.0%) ActionDispatch::Routing::RouteSet::Generator#initialize
292
+ 153 (1.0%) 153 (1.0%) ActiveRecord::Type::String#cast_value
293
+ 192 (1.2%) 143 (0.9%) ActionController::UrlFor#url_options
294
+ 808 (5.0%) 127 (0.8%) ActiveRecord::LazyAttributeHash#[]
295
+ 121 (0.8%) 121 (0.8%) PG::Result#values
296
+ 120 (0.7%) 120 (0.7%) ActionDispatch::Journey::Router::Utils::UriEncoder#escape
297
+ 2478 (15.4%) 117 (0.7%) ActionDispatch::Journey::Formatter#generate
298
+ 115 (0.7%) 115 (0.7%) NewRelic::Agent::Instrumentation::EventedSubscriber#event_stack
299
+ 114 (0.7%) 114 (0.7%) ActiveRecord::Core#init_internals
300
+ 263 (1.6%) 110 (0.7%) ActiveRecord::Type::Value#type_cast
301
+ 8520 (53.0%) 102 (0.6%) ActionView::CompiledTemplates#_app_views_repos__repo_html_slim__2939326833298152184_70365772737940
302
+ ```
303
+
304
+ From here you can dig into individual methods.
305
+
306
+ ## Is this perf change faster?
307
+
308
+ Micro benchmarks might tell you at the code level how much faster something is, but what about the overall application speed. If you're trying to figure out how effective a performance change is to your application, it is useful to compare it to your existing app performance. To help you with that you can use:
309
+
310
+ ```
311
+ $ derailed exec perf:ips
312
+ Endpoint: "/"
313
+ Calculating -------------------------------------
314
+ ips 1.000 i/100ms
315
+ -------------------------------------------------
316
+ ips 3.306 (± 0.0%) i/s - 17.000
317
+ ```
318
+
319
+ This will hit an endpoint in your application using [benchmark-ips](https://github.com/evanphx/benchmark-ips). In "iterations per second" a higher value is always better. You can run your code change several times using this method, and then run your "baseline" codebase (without your changes) to see how it affects your overall performance. You'll want to run and record the results several times (including the std deviation) so you can help eliminate noise. Benchmarking is hard, this technique isn't perfect but it's definetly better than nothing.
320
+
321
+ If you care you can also run pure benchmark (without ips):
322
+
323
+ ```
324
+ $ derailed exec perf:test
325
+ ```
326
+
327
+ But I wouldn't, benchmark-ips is a better measure.
328
+
329
+
330
+ ## Environment Variables
331
+
332
+ All the tasks accept configuration in the form of environment variables.
333
+
334
+
335
+ ### Increasing or decreasing test count `TEST_COUNT`
336
+
337
+ For tasks that are run a number of times you can set the number using `TEST_COUNT` for example:
338
+
339
+ ```
340
+ $ derailed exec perf:test TEST_COUNT=100_000
341
+ ```
342
+
343
+ ## Hitting a different endpoint with `PATH_TO_HIT`
344
+
345
+ 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:
346
+
347
+ ```
348
+ $ PATH_TO_HIT=/users/new derailed exec perf:mem
349
+ ```
350
+
351
+ ### Using a real web server with USE_SERVER
352
+
353
+ All tests are run without a webserver (directly using `Rack::Mock` by default), if you want to use a webserver set `USE_SERVER` to a Rack::Server compliant server, such as `webrick`.
354
+
355
+ ```
356
+ $ USE_SERVER=webrick derailed exec perf:mem
357
+ ```
358
+
359
+ Or
360
+
361
+ ```
362
+ $ USE_SERVER=puma derailed exec perf:mem
363
+ ```
364
+
365
+ This boots a webserver and hits it using `curl` instead of in memory. This is useful if you think the performance issue is related to your webserver.
366
+
367
+ Note: this plugs in the given webserver directly into rack, it doesn't use any `puma.config` file etc. that you have set-up. If you want to do this, i'm open to suggestions on how (and pull requests)
368
+
369
+
370
+ ### Running in a different environment
371
+
372
+ Tests run against the production environment by default, but it's easy to
373
+ change this if your app doesn't run locally with `RAILS_ENV` set to
374
+ `production`. For example:
375
+
376
+ ```
377
+ $ derailed exec perf:mem RAILS_ENV=development
378
+ ```
379
+
380
+ ## perf.rake
381
+
382
+ If you want to customize derailed, you'll need to create a `perf.rake` file at the root of the directory you're trying to benchmark.
383
+
384
+ It is possible to run benchmarks directly using rake
16
385
 
17
386
  ```
18
387
  $ cat << EOF > perf.rake
@@ -37,96 +406,91 @@ $ cat perf.rake
37
406
 
38
407
  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
408
 
40
- ## Run
409
+ Then you can execute your commands via rake.
41
410
 
42
411
  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
412
 
44
413
  ```
45
414
  $ 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
415
  ```
54
416
 
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:
417
+ ## Rack Setup
418
+
419
+ Using Rails? You don't need to do anything special. If you're using Rack, you need to tell us how to boot your app. In your `perf.rake` file add a task:
56
420
 
57
421
  ```
58
- $ rake -f perf.rake perf:test TEST_COUNT=100_000
422
+ namespace :perf do
423
+ task :rack_load do
424
+ DERAILED_APP = # your code here
425
+ end
426
+ end
59
427
  ```
60
428
 
429
+ Set the constant `DERAILED_APP` to your Rack app. See [schneems/derailed_benchmarks#1](https://github.com/schneems/derailed_benchmarks/pull/1) for more info.
61
430
 
62
- ## Config
431
+ ## Authentication
63
432
 
64
- Here are the common environment variables.
433
+ If you're trying to test an endpoint that has authentication you'll need to tell your task how to bypass that authentication. Authentication is controlled by the `DerailedBenchmarks.auth` object. There is a built in support for Devise. If you're using some other authentication method, you can write your own authentication strategy.
65
434
 
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:
435
+ To enable authentication in a test run with:
69
436
 
70
437
  ```
71
- PATH_TO_HIT=/users/new
438
+ $ USE_AUTH=true derailed exec perf:mem
72
439
  ```
73
440
 
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`.
441
+ See below how to customize authentication.
77
442
 
78
- ```
79
- USE_SERVER=webrick
80
- ```
443
+ ### Authentication with Devise
81
444
 
82
- ### TEST_COUNT
445
+ If you're using devise, there is a built in auth helper that will detect the presence of the devise gem and load automatically.
83
446
 
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
447
+ Create a `perf.rake` file at your root.
85
448
 
86
449
  ```
87
- TEST_COUNT=1
450
+ $ cat perf.rake
88
451
  ```
89
452
 
90
- Note some tasks have different defaults.
453
+ If you want you can customize the user that is logged in by setting that value in your `perf.rake` file.
91
454
 
455
+ ```
456
+ DerailedBenchmarks.auth.user = User.find_or_create!(twitter: "schneems")
457
+ ```
92
458
 
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.
459
+ You will need to provide a valid user, so depending on the validations you have in your `user.rb`, you may need to provide different parameters.
104
460
 
461
+ If you're trying to authenticate a non-user model, you'll need to write your own custom auth strategy.
105
462
 
106
- ### perf:ram_over_time
463
+ ### Custom Authentication Strategy
107
464
 
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).
465
+ To implement your own authentication strategy You will need to create a class that [inherits from auth_helper.rb](lib/derailed_benchmarks/auth_helper.rb). You will need to implement a `setup` and a `call` method. You can see an example of [how the devise auth helper was written](lib/derailed_benchmarks/auth_helpers/devise.rb). You can put this code in your `perf.rake` file.
109
466
 
467
+ ```ruby
468
+ class MyCustomAuth < DerailedBenchmarks::AuthHelper
469
+ def setup
470
+ # initialize code here
471
+ end
110
472
 
111
- ### perf:require_bench
473
+ def call(env)
474
+ # log something in on each request
475
+ app.call(env)
476
+ end
477
+ end
478
+ ```
112
479
 
113
- Shows the amount of memory (RAM) each library takes up when it is required.
480
+ The devise strategy works by enabling test mode inside of the Rack request and inserting a stub user. You'll need to duplicate that logic for your own authentication scheme if you're not using devise.
114
481
 
482
+ Once you have your class, you'll need to set `DerailedBenchmarks.auth` to a new instance of your class. In your `perf.rake` file add:
115
483
 
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
484
+ ```ruby
485
+ DerailedBenchmarks.auth = MyCustomAuth.new
122
486
  ```
123
487
 
488
+ Now on every request that is made with the `USE_AUTH` environment variable set, the `MyCustomAuth#call` method will be invoked.
124
489
 
125
490
  ## License
126
491
 
127
492
  MIT
128
493
 
129
-
130
494
  ## Acknowledgements
131
495
 
132
496
  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.