Test Setup Failed
Push — master ( a82c68...32b7b8 )
by Steven
01:27
created

RequireProfiler.gc_analyze()   A

Complexity

Conditions 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
c 2
b 0
f 0
dl 0
loc 13
rs 9.4285
1
# Some based on : https://gist.github.com/277289
2
#
3
# This is a rudimentary script that allows us to
4
#  quickly determine if any gems are slowing down startup
5
6
require 'benchmark'
7
require 'fileutils'
8
require 'bundler'
9
10
module RequireProfiler
11
  class << self
12
13
    attr_accessor :stats
14
15
    def profiling_enabled?
16
      @profiling_enabled
17
    end
18
19
    def profile
20
      start
21
      yield
22
      stop
23
    end
24
25
    def start(tmp_options = {})
26
      @start_time = Time.now
27
      [ ::Kernel, (class << ::Kernel; self; end) ].each do |klass|
28
        klass.class_eval do
29
          def require_with_profiling(path, *args)
30
            RequireProfiler.measure(path, caller, :require) { require_without_profiling(path, *args) }
31
          end
32
          alias require_without_profiling require
33
          alias require require_with_profiling
34
35
          def load_with_profiling(path, *args)
36
            RequireProfiler.measure(path, caller, :load) { load_without_profiling(path, *args) }
37
          end
38
          alias load_without_profiling load
39
          alias load load_with_profiling
40
        end
41
      end
42
      # This is necessary so we don't clobber Bundler.require on Rails 3
43
      Kernel.class_eval { private :require, :load }
44
      @profiling_enabled = true
45
    end
46
47
    def stop
48
      @stop_time = Time.now
49
      [ ::Kernel, (class << ::Kernel; self; end) ].each do |klass|
50
        klass.class_eval do
51
          alias require require_without_profiling
52
          alias load load_without_profiling
53
        end
54
      end
55
      @profiling_enabled = false
56
    end
57
58
    def measure(path, full_backtrace, mechanism, &block)
59
      # Path may be a Pathname, convert to a String
60
      path = path.to_s
61
62
      @stack ||= []
63
      self.stats ||= {}
64
65
      stat = self.stats.fetch(path) { |key| self.stats[key] = { calls: 0, time: 0, parent_time: 0 } }
66
67
      @stack << stat
68
69
      time = Time.now
70
      begin
71
        output = yield  # do the require or load here
72
      ensure
73
        delta = Time.now - time
74
        stat[:time] += delta
75
        stat[:calls] += 1
76
        @stack.pop
77
        @stack.each do |frame|
78
          frame[:parent_time] += delta
79
        end
80
      end
81
82
      output
83
    end
84
85
    def time_block
86
      start = Time.now
87
      yield
88
      Time.now - start
89
    end
90
91
    def gc_analyze
92
      ObjectSpace.garbage_collect
93
      gc_duration_start = time_block { ObjectSpace.garbage_collect }
94
      old_objs = ObjectSpace.count_objects
95
      yield
96
      ObjectSpace.garbage_collect
97
      gc_duration_finish = time_block { ObjectSpace.garbage_collect }
98
      new_objs = ObjectSpace.count_objects
99
100
      puts "New objects: #{(new_objs[:TOTAL] - new_objs[:FREE]) - (old_objs[:TOTAL] - old_objs[:FREE])}"
101
      puts "GC duration: #{gc_duration_finish}"
102
      puts "GC impact: #{gc_duration_finish - gc_duration_start}"
103
    end
104
105
  end
106
end
107
108
# RequireProfiler.gc_analyze do
109
#   # require 'mime-types'
110
#   require 'highline'
111
# end
112
# exit
113
114
RequireProfiler.profile do
115
  Bundler.definition.dependencies.each do |dep|
116
    begin
117
      require dep.name
118
    rescue Exception
119
      # don't care
120
    end
121
  end
122
end
123
124
sorted = RequireProfiler.stats.to_a.sort { |a, b| b[1][:time] - b[1][:parent_time] <=> a[1][:time] - a[1][:parent_time] }
125
126
sorted[0..120].each do |k, v|
127
  puts "#{k} : time #{v[:time] - v[:parent_time]} "
128
end
129