module Rbenchmarker::RegisterRbenchmarkerMethods

Constants

DEFAULT_REPORT_LENGTH
TIMES_THE_METHOD_WAS_CALLED

Public Class Methods

register_rbenchmarker_methods( all: nil, only: [], except: [], added: [], label_width: 0, times: 0, require_hidden_method: false, object_type: nil, target_obj: nil ) click to toggle source

“rbenchmarker” needs to start processing after all the methods of the target class(file) have been read, Rbenchmarker uses the benchmark library (docs.ruby-lang.org/ja/latest/class/Benchmark.html) internally.

“all” option performs static analysis to identify the target method, so put “__FILE__” in the argument. normally, set this option.

method specified in “only” will not be benchmarked. method specified in “except” will not be benchmarked. method specified by “added” is added to the benchmark after the “only” and “except” method filtering is done.

“label_width” specifies the width of the benchmark label.

benchmark is measured by repeatedly executing the benchmarked method for the number of times specified by “times”. Keep in mind that code such as SQL queries will also be executed repeatedly.

“require_hidden_method” option is set to true, methods created dynamically by metaprogramming will also be targeted.

in the “include” option, specify the module that you are including. Arrange the option array in the loading order of the modules to be included. in the “extend” option, specify the module that you are extending. Arrange the option array in the loading order of the modules to be extended. in the “prepend” option, specify the module that you are prepending. Arrange the option array in the loading order of the modules to be prepended.

# File lib/rbenchmarker/register_rbenchmarker_methods.rb, line 29
def self.register_rbenchmarker_methods(
  all: nil,
  only: [],
  except: [],
  added: [],
  label_width: 0,
  times: 0,
  require_hidden_method: false,
  object_type: nil,
  target_obj: nil
)
  # Collect the method names to be benchmarked.
  target_instance_method_names, target_class_method_names = collect_target_methods(
    target_obj, all, require_hidden_method, only, except, added
  )

  # Add benchmark function to the method to be benchmarked.
  instance_method_codes_with_benchmark_function = generate_benchmarking_codes(
    target_instance_method_names, label_width, times, (object_type == 'Module' ? 'module' : 'instance')
  )
  class_method_codes_with_benchmark_function = generate_benchmarking_codes(
    target_class_method_names, label_width, times, (object_type == 'Module' ? 'module' : 'class')
  )

  # Override the original method with the benchmark functionality.
  prepended_instance_methods = prepend_benchmarking_methods(instance_method_codes_with_benchmark_function)
  prepended_class_methods = prepend_benchmarking_methods(class_method_codes_with_benchmark_function)

  case object_type
  when 'Module'
    Rbenchmarker.add_module_with_has_methods_list(target_obj.to_s.to_sym, prepended_instance_methods)
  when 'Class'
    target_obj.prepend prepended_instance_methods
  end

  target_obj.singleton_class.prepend prepended_class_methods
end

Private Class Methods

collect_target_methods(target_obj, target_file, require_hidden_method, only, except, added) click to toggle source

private

# File lib/rbenchmarker/register_rbenchmarker_methods.rb, line 69
def self.collect_target_methods(target_obj, target_file, require_hidden_method, only, except, added)
  if require_hidden_method
    instance_method_name_candidates = target_obj.instance_methods(false) + target_obj.private_instance_methods(false)
    class_method_name_candidates = target_obj.singleton_methods(false)
  elsif target_file
    instance_method_name_candidates, class_method_name_candidates =
      statically_analyze_and_extract_target_method_names(target_obj, target_file)
  else
    instance_method_name_candidates = []
    class_method_name_candidates = []
  end

  filtered_instance_method_names =
    filter_by_only_except_and_added(instance_method_name_candidates, only, except, added, target_obj, 'instance_method')
  filtered_class_method_names =
    filter_by_only_except_and_added(class_method_name_candidates, only, except, added, target_obj, 'class_method')

  [filtered_instance_method_names, filtered_class_method_names]
end
define_benchmarking_code(method_name, width, times, class_type) click to toggle source
# File lib/rbenchmarker/register_rbenchmarker_methods.rb, line 130
    def self.define_benchmarking_code(method_name, width, times, class_type)
      times_to_i = times.to_i.nonzero?
      generate_report_function_code = generate_report_method_text('bm_result')

      <<-RUBY_EVAL
        def #{method_name}(*)
          origin_return_value = nil
          bm_result = # 'bm_result' is Arguments of 'generate_report_method_text'
            Benchmark.bm #{width.to_i.nonzero? || method_name.length + DEFAULT_REPORT_LENGTH} do |r|
              r.report "report def #{method_name}#{"(#{times_to_i}" if times_to_i}#{' loops)' if times_to_i} #{class_type} method" do
                #{times_to_i ? "#{times_to_i}.times{ origin_return_value = super }" : 'origin_return_value = super'}
              end
            end
          #{generate_report_function_code}
          origin_return_value
        end
      RUBY_EVAL
    end
filter_by_only_except_and_added(method_names, only, except, added, target_obj, method_type) click to toggle source
# File lib/rbenchmarker/register_rbenchmarker_methods.rb, line 107
def self.filter_by_only_except_and_added(method_names, only, except, added, target_obj, method_type)
  has_methods = case method_type
                when 'instance_method'
                  target_obj.instance_methods(false) + target_obj.private_instance_methods(false)
                when 'class_method'
                  target_obj.singleton_methods(false)
                end

  added = added.select { |method_name| has_methods.include?(method_name) }

  if !only.empty?
    ((method_names & only) + added).uniq
  elsif !except.empty?
    (method_names - except + added).uniq
  else
    (method_names + added).uniq
  end
end
generate_benchmarking_codes(method_names, width, times, class_type) click to toggle source
# File lib/rbenchmarker/register_rbenchmarker_methods.rb, line 126
def self.generate_benchmarking_codes(method_names, width, times, class_type)
  method_names.map { |method_name| define_benchmarking_code(method_name, width, times, class_type) }
end
generate_report_method_text(bm_report_name) click to toggle source
# File lib/rbenchmarker/register_rbenchmarker_methods.rb, line 149
    def self.generate_report_method_text(bm_report_name)
      <<-RUBY_EVAL
        new_report = #{bm_report_name}.first
        bm_reports = Rbenchmarker.tracking_reports
        if bm_reports[new_report.label.to_sym]
          previous_report = bm_reports[new_report.label.to_sym]
          key = new_report.label.to_sym
          value = {
            utime: previous_report[:utime] << new_report.utime,
            stime: previous_report[:stime] << new_report.stime,
            total: previous_report[:total] << new_report.total,
            real: previous_report[:real] << new_report.real,
            number_of_executions: previous_report[:number_of_executions] + 1
          }
          Rbenchmarker.add_report(key, value)
        else
          key = new_report.label.to_sym
          value = {
            utime: [new_report.utime],
            stime: [new_report.stime],
            total: [new_report.total],
            real: [new_report.real],
            number_of_executions: TIMES_THE_METHOD_WAS_CALLED
          }
          Rbenchmarker.add_report(key, value)
        end
      RUBY_EVAL
    end
prepend_benchmarking_methods(benchmarking_codes) click to toggle source
# File lib/rbenchmarker/register_rbenchmarker_methods.rb, line 178
def self.prepend_benchmarking_methods(benchmarking_codes)
  Module.new do
    benchmarking_codes.each { |code| class_eval code, __FILE__, __LINE__ + 1 }
  end
end
statically_analyze_and_extract_target_method_names(target_obj, target_file) click to toggle source
# File lib/rbenchmarker/register_rbenchmarker_methods.rb, line 89
def self.statically_analyze_and_extract_target_method_names(target_obj, target_file)
  instance_method_name_candidates = []
  class_method_name_candidates = []
  words = []

  all_class_methods = target_obj.singleton_methods(false)
  all_instance_methods = target_obj.instance_methods(false) + target_obj.private_instance_methods(false)
  File.foreach(target_file) { |line| words << line[/def\ (.*?)[ ;|\(\n]/, 1] }
  user_described_all_method_names = words.compact.map { |word| word.match(/^self\./) ? word.gsub(/^self\./, '') : word }

  user_described_all_method_names.each do |def_name|
    class_method_name_candidates << def_name.to_sym if all_class_methods.include?(def_name.to_sym)
    instance_method_name_candidates << def_name.to_sym if all_instance_methods.include?(def_name.to_sym)
  end

  [instance_method_name_candidates.uniq, class_method_name_candidates.uniq]
end