File: kernel_require.rb

package info (click to toggle)
ruby-derailed-benchmarks 1.7.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 760 kB
  • sloc: ruby: 1,317; makefile: 4
file content (88 lines) | stat: -rw-r--r-- 2,543 bytes parent folder | download
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
# frozen_string_literal: true

require 'get_process_mem'
require 'derailed_benchmarks/require_tree'

ENV['CUT_OFF'] ||= "0.3"

# This file contains classes and monkey patches to measure the amount of memory
# useage requiring an individual file adds.

# Monkey patch kernel to ensure that all `require` calls call the same
# method
module Kernel
  REQUIRE_STACK = []

  module_function

  alias_method :original_require, :require
  alias_method :original_require_relative, :require_relative

  def require(file)
    measure_memory_impact(file) do |file|
      # "source_annotation_extractor" is deprecated in Rails 6
      # # if we don't skip the library it leads to a crash
      # next if file == "rails/source_annotation_extractor" && Rails.version >= '6.0'
      original_require(file)
    end
  end

  def require_relative(file)
    if Pathname.new(file).absolute?
      require file
    else
      require File.expand_path("../#{file}", caller_locations(1, 1)[0].absolute_path)
    end
  end

  private

  # The core extension we use to measure require time of all requires
  # When a file is required we create a tree node with its file name.
  # We then push it onto a stack, this is because requiring a file can
  # require other files before it is finished.
  #
  # When a child file is required, a tree node is created and the child file
  # is pushed onto the parents tree. We then repeat the process as child
  # files may require additional files.
  #
  # When a require returns we remove it from the require stack so we don't
  # accidentally push additional children nodes to it. We then store the
  # memory cost of the require in the tree node.
  def measure_memory_impact(file, &block)
    mem    = GetProcessMem.new
    node   = DerailedBenchmarks::RequireTree.new(file)

    parent = REQUIRE_STACK.last
    parent << node
    REQUIRE_STACK.push(node)
    begin
      before = mem.mb
      block.call file
    ensure
      REQUIRE_STACK.pop # node
      after = mem.mb
    end
    node.cost = after - before
  end
end

# Top level node that will store all require information for the entire app
TOP_REQUIRE = DerailedBenchmarks::RequireTree.new("TOP")
REQUIRE_STACK.push(TOP_REQUIRE)

class Object
  private

  def require(path)
    Kernel.require(path)
  end
end

# Don't forget to assign a cost to the top level
cost_before_requiring_anything = GetProcessMem.new.mb
TOP_REQUIRE.cost = cost_before_requiring_anything
def TOP_REQUIRE.print_sorted_children(*args)
  self.cost = GetProcessMem.new.mb - self.cost
  super
end