tapioca icon indicating copy to clipboard operation
tapioca copied to clipboard

Optimize `URLHelpers.gather_constants`

Open amomchilov opened this issue 1 year ago • 0 comments
trafficstars

Motivation

Profiling tapioca DSL SomeConst identified this method as a hot spot, taking roughly 1 out of the 6 total second spent by Tapioca (not including application load time).

The changes in this PR speed it up by ~500-800ms, but I need to remeasure to confirm.

toy micro-benchmarks
#!/usr/bin/ruby

#require "awesome_print"
#require "active_support/all"
require "benchmark/ips"

module HelloWorld
  def hello_world!
    puts "Hello, word!"
  end
end

class GrandParent; end

class Parent < GrandParent; end

class Child < Parent
  include HelloWorld
end

module Unrelated; end

def ancestors_of(mod) = mod.ancestors
def superclass_of(cls) = cls.superclass

def includes_helper?(mod, helper)
  superclass_ancestors = []
  
  if Class === mod
    superclass = superclass_of(mod)
    superclass_ancestors = ancestors_of(superclass) if superclass
  end
  
  ancestors = Set.new.compare_by_identity.merge(ancestors_of(mod)).subtract(superclass_ancestors)
  ancestors.any? { |ancestor| helper == ancestor }
end

def includes_helper_2?(mod, helper)
  if Class === mod
    superclass = superclass_of(mod)
    ancestors_of(mod).take_while { |a| !superclass.equal?(a) }.include?(helper)
  else
    ancestors_of(mod).include?(helper)
  end
end

def includes_helper_3?(mod, helper)
  return ancestors_of(mod).include?(helper) unless Class === mod

  superclass = superclass_of(mod)

  ancestors_of(mod).each do |a|
    return true if helper.equal?(a)
    return false if superclass.equal?(a)
  end

  false
end

def direct_ancestors_of(mod)
  if Class === mod
    superclass = superclass_of(mod)
    ancestors_of(mod).take_while { |a| !superclass.equal?(a) }
  else
    ancestors_of(mod)
  end
end

Benchmark.ips do |x|
  x.config(warmup: 1, time: 5)
  
  x.report("original") do |times|
    i = 0
    while (i += 1) < times
      includes_helper?(Child, HelloWorld)
      includes_helper?(Child, Unrelated)
    end
  end
  
  x.report("variant 2") do |times|
    i = 0
    while (i += 1) < times
      includes_helper_2?(Child, HelloWorld)
      includes_helper_2?(Child, Unrelated)
    end
  end
  
  x.report("variant 3") do |times|
    i = 0
    while (i += 1) < times
      includes_helper_3?(Child, HelloWorld)
      includes_helper_3?(Child, Unrelated)
    end
  end
  
  x.report("direct_ancestors_of") do |times|
    i = 0
    while (i += 1) < times
      direct_ancestors = direct_ancestors_of(Child)
      direct_ancestors.include?(HelloWorld)
      direct_ancestors.include?(Unrelated)
    end
  end
  
  x.compare!
end

amomchilov avatar Jun 19 '24 20:06 amomchilov