derailed_benchmarks 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e444f3e6c00f95138b3df5c86995b4126fd89121
4
- data.tar.gz: cae797098b186ef45c23fb1cdc525c98ecc4cd87
3
+ metadata.gz: f00460799a696f7a1779ea6bbb4a6d0bbb4e0612
4
+ data.tar.gz: 96d81116a29b5211015581af27c3b69ede4fd125
5
5
  SHA512:
6
- metadata.gz: 54796bcc3efc7773af0705cf38fa21f93c1602c5661626a1643b0b425be14eed042bfd5455ea25dcecde47ce7d95837c17549f85dd0026cbb19365efec89a9ba
7
- data.tar.gz: 398783116407326acf4622869abc85420be8ddb7468e33d7267d1695de63f87ca21f577627d0145fb9ec3f82ec2dc0bc0fda32b1eedb8fee909d04551df86622
6
+ metadata.gz: b8d8d3c5a56696af7327a6ef4fbb927c04956a38dfced8d07ee229209eeb68beb00c047dbc317e0c6cff7a2685170305b65895e8a3d3ddb1c53ee93e4cf1a221
7
+ data.tar.gz: fdd8db66a4a0f2937b32d31a1ec3dca308c76d80ef337e6a0e817666bf0e127849083e0b594974324627a09dd86fec93e4c0443f89051018a2d02e6047a0e3ca
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # A Log of Changes!
2
2
 
3
+ ## [1.0.1] - 2015-20-06
4
+
5
+ - `bundle:mem` and similar tasks now keep track of duplicate requires and display them along side of memory requirements. This makes it easier to identify where components are used by multiple libraries
6
+ - Add rake to gemspec which gets rid of `Unresolved specs during Gem::Specification.reset:` warning
7
+ - Outputs of memory are now done in [mebibytes](https://en.wikipedia.org/wiki/Mebibyte), a more accurate unit for the value we're measuring (hint: it's what you think MB is).
3
8
 
4
9
  ## [1.0.0] - 2015-15-05
5
10
 
data/Gemfile.lock CHANGED
@@ -1,12 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- derailed_benchmarks (1.0.0)
4
+ derailed_benchmarks (1.0.1)
5
5
  benchmark-ips (~> 2)
6
6
  get_process_mem (~> 0)
7
7
  memory_profiler (~> 0)
8
8
  rack (~> 1)
9
- rake (~> 10.4)
9
+ rake (~> 10)
10
10
 
11
11
  GEM
12
12
  remote: https://rubygems.org/
@@ -40,7 +40,7 @@ GEM
40
40
  multi_json (~> 1.0)
41
41
  arel (3.0.3)
42
42
  bcrypt (3.1.10)
43
- benchmark-ips (2.1.1)
43
+ benchmark-ips (2.2.0)
44
44
  builder (3.0.4)
45
45
  capybara (2.4.4)
46
46
  mime-types (>= 1.16)
@@ -64,7 +64,7 @@ GEM
64
64
  mail (2.5.4)
65
65
  mime-types (~> 1.16)
66
66
  treetop (~> 1.4.8)
67
- memory_profiler (0.9.0)
67
+ memory_profiler (0.9.1)
68
68
  mime-types (1.25.1)
69
69
  mini_portile (0.6.2)
70
70
  multi_json (1.11.0)
data/README.md CHANGED
@@ -40,7 +40,7 @@ You must be using Ruby 2.1+ to install these libraries. If you're on an older ve
40
40
 
41
41
  ## Use
42
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.
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 statically give you more information about the dependencies that are in 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
44
 
45
45
  ## Static Benchmarking
46
46
 
@@ -62,16 +62,21 @@ This will load each of your gems in your Gemfile and see how much memory they co
62
62
 
63
63
  ```
64
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
65
+ TOP: 54.1836 MiB
66
+ mail: 18.9688 MiB
67
+ mime/types: 17.4453 MiB
68
+ mail/field: 0.4023 MiB
69
+ mail/message: 0.3906 MiB
70
+ action_view/view_paths: 0.4453 MiB
71
+ action_view/base: 0.4336 MiB
72
72
  ```
73
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.
74
+ _Aside: A "MiB", which is the [IEEE] and [IEC] symbol for Mebibyte, is 2<sup>20</sup> bytes / 1024 Kibibytes (which are in turn 1024 bytes)._
75
+
76
+ [IEEE]: http://en.wikipedia.org/wiki/IEEE_1541-2002
77
+ [IEC]: http://en.wikipedia.org/wiki/IEC_80000-13
78
+
79
+ Here we can see that `mail` uses 18MiB, 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
80
 
76
81
  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
82
 
@@ -83,6 +88,35 @@ You can use `CUT_OFF=0.3` to only show files that have above a certain memory us
83
88
 
84
89
  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
90
 
91
+ The same file may be required by several libraries, since Ruby only requires files once, the cost is only associated with the first library to require a file. To make this more visible duplicate entries will list all the parents they belong to. For example both `mail` and `fog` require `mime/types. So it may show up something like this in your app:
92
+
93
+ ```
94
+ $ derailed bundle:mem
95
+ TOP: 54.1836 MiB
96
+ mail: 18.9688 MiB
97
+ mime/types: 17.4453 MiB (Also required by: fog/storage)
98
+ mail/field: 0.4023 MiB
99
+ mail/message: 0.3906 MiB
100
+ ```
101
+
102
+ That way you'll know that simply removing the top level library (mail) would not result in a memory reduction. The output is trucated after the first two entries:
103
+
104
+
105
+ ```
106
+ fog/core: 0.9844 MiB (Also required by: fog/xml, fog/json, and 48 others)
107
+ fog/rackspace: 0.957 MiB
108
+ fog/joyent: 0.7227 MiB
109
+ fog/joyent/compute: 0.7227 MiB
110
+ ```
111
+
112
+ If you want to see everything that requires `fog/core` you can run `CUT_OFF=0 bundle exec derailed bundle:mem` to get the full output that you can then grep through manually.
113
+
114
+ Update: While `mime/types` looks horible in these examples, it's been fixed. You can add this to the top of your gemfile for free memory:
115
+
116
+ ```ruby
117
+ gem 'mime-types', '~> 2.4.3', require: 'mime/types/columnar'
118
+ ```
119
+
86
120
  ### Objects created at Require time
87
121
 
88
122
  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:
@@ -105,7 +139,7 @@ allocated memory by gem
105
139
  8103432 json-1.8.2
106
140
  ```
107
141
 
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.
142
+ Once you identify a gem that creates a large amount 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
143
 
110
144
 
111
145
  By default this task will only return results from the `:default` and `"production"` groups. If you want a different group you can run with.
@@ -202,7 +236,7 @@ PID: 78675
202
236
  183.62109375
203
237
  ```
204
238
 
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
239
+ Here we can see that while the memory use is increasing, it levels off around 183 MiB. You'll want to run this task using ever increasing values of `TEST_COUNT=` for example
206
240
 
207
241
  ```
208
242
  $ TEST_COUNT=5000 derailed exec perf:ram_over_time
@@ -246,13 +280,13 @@ This task does essentially the same thing, however it hits your app with one req
246
280
  ```
247
281
  $ derailed exec perf:mem
248
282
 
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
283
+ TOP: 54.1836 MiB
284
+ mail: 18.9688 MiB
285
+ mime/types: 17.4453 MiB
286
+ mail/field: 0.4023 MiB
287
+ mail/message: 0.3906 MiB
288
+ action_view/view_paths: 0.4453 MiB
289
+ action_view/base: 0.4336 MiB
256
290
  ```
257
291
 
258
292
  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.
data/bin/derailed CHANGED
@@ -13,7 +13,7 @@ $: << lib
13
13
 
14
14
 
15
15
  require File.join(lib, 'derailed_benchmarks.rb')
16
- require 'bundler'
16
+
17
17
  Bundler.setup
18
18
 
19
19
  require 'thor'
@@ -22,6 +22,7 @@ class DerailedBenchmarkCLI < Thor
22
22
 
23
23
  desc "exec", "executes given derailed benchmark"
24
24
  def exec(task = nil)
25
+ setup_bundler!
25
26
  require 'derailed_benchmarks'
26
27
  require 'rake'
27
28
  Rake::TaskManager.record_task_metadata = true
@@ -22,7 +22,7 @@ Gem::Specification.new do |gem|
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
+ gem.add_dependency "rake", "~> 10"
26
26
 
27
27
  gem.add_development_dependency "capybara", "~> 2"
28
28
  gem.add_development_dependency "rails", "~> 3"
@@ -1,10 +1,7 @@
1
1
  require 'time'
2
+ require 'bundler'
2
3
 
3
- require 'rack/test'
4
- require 'rack/file'
5
- require 'benchmark/ips'
6
4
  require 'get_process_mem'
7
- require 'bundler'
8
5
 
9
6
  module DerailedBenchmarks
10
7
  def self.gem_is_bundled?(name)
@@ -2,8 +2,11 @@
2
2
  # RequireTree.new('get_process_mem')
3
3
  module DerailedBenchmarks
4
4
  class RequireTree
5
+ REQUIRED_BY = {}
6
+
5
7
  attr_reader :name
6
8
  attr_accessor :cost
9
+ attr_accessor :parent
7
10
 
8
11
  def initialize(name)
9
12
  @name = name
@@ -12,6 +15,8 @@ module DerailedBenchmarks
12
15
 
13
16
  def <<(tree)
14
17
  @children[tree.name.to_s] = tree
18
+ tree.parent = self
19
+ (REQUIRED_BY[tree.name.to_s] ||= []) << self.name
15
20
  end
16
21
 
17
22
  def [](name)
@@ -32,10 +37,23 @@ module DerailedBenchmarks
32
37
  children.sort { |c1, c2| c2.cost <=> c1.cost }
33
38
  end
34
39
 
40
+ def to_string
41
+ str = "#{name}: #{cost.round(4)} MiB"
42
+ if parent && REQUIRED_BY[self.name.to_s]
43
+ names = REQUIRED_BY[self.name.to_s].uniq - [parent.name.to_s]
44
+ if names.any?
45
+ str << " (Also required by: #{ names.first(2).join(", ") }"
46
+ str << ", and #{names.count - 2} others" if names.count > 3
47
+ str << ")"
48
+ end
49
+ end
50
+ str
51
+ end
52
+
35
53
  # Recursively prints all child nodes
36
54
  def print_sorted_children(level = 0, out = STDOUT)
37
55
  return if cost < ENV['CUT_OFF'].to_f
38
- out.puts " " * level + "#{name}: #{cost.round(4)} mb"
56
+ out.puts " " * level + self.to_string
39
57
  level += 1
40
58
  sorted_children.each do |child|
41
59
  child.print_sorted_children(level, out)
@@ -1,4 +1,3 @@
1
-
2
1
  namespace :perf do
3
2
  task :rails_load do
4
3
  ENV["RAILS_ENV"] ||= "production"
@@ -67,6 +66,9 @@ namespace :perf do
67
66
  PATH_TO_HIT = ENV["PATH_TO_HIT"] || ENV['ENDPOINT'] || "/"
68
67
  puts "Endpoint: #{ PATH_TO_HIT.inspect }"
69
68
 
69
+ require 'rack/test'
70
+ require 'rack/file'
71
+
70
72
  DERAILED_APP = DerailedBenchmarks.add_auth(DERAILED_APP)
71
73
  if server = ENV["USE_SERVER"]
72
74
  @port = (3000..3900).to_a.sample
@@ -130,7 +132,7 @@ namespace :perf do
130
132
  task :mem => [:kernel_require_patch, :setup] do
131
133
  puts "## Impact of `require <file>` on RAM"
132
134
  puts
133
- puts "Showing all `require <file>` calls that consume #{ENV['CUT_OFF']} mb or more of RSS"
135
+ puts "Showing all `require <file>` calls that consume #{ENV['CUT_OFF']} MiB or more of RSS"
134
136
  puts "Configure with `CUT_OFF=0` for all entries or `CUT_OFF=5` for few entries"
135
137
 
136
138
  puts "Note: Files only count against RAM on their first load."
@@ -145,6 +147,7 @@ namespace :perf do
145
147
 
146
148
  desc "outputs ram usage over time"
147
149
  task :ram_over_time => [:setup] do
150
+ require 'get_process_mem'
148
151
  puts "PID: #{Process.pid}"
149
152
  ram = GetProcessMem.new
150
153
  @keep_going = true
@@ -177,6 +180,8 @@ namespace :perf do
177
180
 
178
181
  desc "iterations per second"
179
182
  task :ips => [:setup] do
183
+ require 'benchmark/ips'
184
+
180
185
  Benchmark.ips do |x|
181
186
  x.report("ips") { call_app }
182
187
  end
@@ -1,3 +1,3 @@
1
1
  module DerailedBenchmarks
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.1"
3
3
  end
@@ -6,6 +6,10 @@ class RequireTree < ActiveSupport::TestCase
6
6
  DerailedBenchmarks::RequireTree.new(name)
7
7
  end
8
8
 
9
+ def teardown
10
+ DerailedBenchmarks::RequireTree.const_set("REQUIRED_BY", {})
11
+ end
12
+
9
13
  test "default_cost" do
10
14
  parent = tree("parent")
11
15
  assert_equal 0, parent.cost
@@ -43,9 +47,44 @@ class RequireTree < ActiveSupport::TestCase
43
47
  assert_equal expected, parent.sorted_children
44
48
 
45
49
  expected = <<-OUT
46
- parent: #{ parent.cost.round(4) } mb
47
- large: #{ large.cost.round(4) } mb
48
- small: #{ small.cost.round(4) } mb
50
+ parent: #{ parent.cost.round(4) } MiB
51
+ large: #{ large.cost.round(4) } MiB
52
+ small: #{ small.cost.round(4) } MiB
53
+ OUT
54
+ capture = StringIO.new
55
+
56
+ parent.print_sorted_children(0, capture)
57
+
58
+ assert_equal expected, capture.string
59
+ end
60
+
61
+ test "attributes duplicate children" do
62
+ parent = tree("parent")
63
+ parent.cost = rand(5..10)
64
+ small = tree("small")
65
+ small.cost = rand(10..100)
66
+
67
+ large = tree("large")
68
+ large.cost = small.cost + 1
69
+
70
+ dup = tree("large")
71
+ dup.cost = 0.4
72
+ small << dup
73
+
74
+ parent << small
75
+ parent << large
76
+
77
+ expected = [large, small]
78
+ assert_equal expected, parent.sorted_children
79
+
80
+ expected = [dup]
81
+ assert_equal expected, small.sorted_children
82
+
83
+ expected = <<-OUT
84
+ parent: #{ parent.cost.round(4) } MiB
85
+ large: #{ large.cost.round(4) } MiB (Also required by: small)
86
+ small: #{ small.cost.round(4) } MiB
87
+ large: #{ dup.cost.round(4) } MiB (Also required by: parent)
49
88
  OUT
50
89
  capture = StringIO.new
51
90
  parent.print_sorted_children(0, capture)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: derailed_benchmarks
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Schneeman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-18 00:00:00.000000000 Z
11
+ date: 2015-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: memory_profiler
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '10.4'
75
+ version: '10'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '10.4'
82
+ version: '10'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: capybara
85
85
  requirement: !ruby/object:Gem::Requirement