summaryrefslogtreecommitdiff
path: root/support/benchmarks/load_allocations.rb
blob: 27f59d47836f33ab883087b95dc774e6880026e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# frozen_string_literal: true

if RUBY_VERSION < "2.1"
  warn "Cannot count allocations on #{RUBY_VERSION}."
  exit 1
end

begin
  require "allocation_tracer"
rescue LoadError
  warn "Allocation tracking requires the gem 'allocation_tracer'."
  exit 1
end

module Benchmarks
  # Calculate the number of allocations during loading.
  class LoadAllocations
    def self.report(columnar: false, full: false, top_x: nil, mime_types_only: false)
      new(
        columnar: columnar,
        top_x: top_x,
        mime_types_only: mime_types_only,
        full: full
      ).report
    end

    def initialize(columnar: false, full: false, top_x: nil, mime_types_only: false)
      @columnar = !!columnar
      @full = !!full
      @mime_types_only = !!mime_types_only

      @top_x = top_x

      return unless @top_x

      @top_x = top_x.to_i
      @top_x = 10 if @top_x <= 0
    end

    def report
      collect
      report_top_x if @top_x
      puts "TOTAL Allocations: #{@count}"
    end

    private

    def report_top_x
      table = @allocations.sort_by { |_, v| v.first }.reverse.first(@top_x)
      table.map! { |(location, allocs)|
        next if @mime_types_only && location.first !~ (%r{mime-types/lib})

        [location.join(":").gsub(%r{^#{Dir.pwd}/}, ""), *allocs]
      }.compact!

      head = (ObjectSpace::AllocationTracer.header - [:line]).map { |h|
        h.to_s.split(/_/).map(&:capitalize).join(" ")
      }
      table.unshift head

      max_widths = [].tap { |mw|
        table.map { |row| row.lazy.map(&:to_s).map(&:length).to_a }.tap do |w|
          w.first.each_index do |i|
            mw << w.lazy.map { |r| r[i] }.max
          end
        end
      }

      pattern = ["%%-%ds"]
      pattern << (["%% %ds"] * (max_widths.length - 1))
      pattern = pattern.join("\t") % max_widths
      table.each do |row|
        puts pattern % row
      end
      puts
    end

    def collect
      @allocations =
        if @columnar
          ObjectSpace::AllocationTracer.trace do
            require "mime/types"
            MIME::Types.first.to_h if @full
          end
        else
          ObjectSpace::AllocationTracer.trace do
            require "mime/types/full"
          end
        end

      @count = ObjectSpace::AllocationTracer.allocated_count_table.values
        .inject(:+)
    end
  end
end