lib/liquid/profiler.rb in liquid-4.0.4 vs lib/liquid/profiler.rb in liquid-5.0.0

- old
+ new

@@ -1,5 +1,7 @@ +# frozen_string_literal: true + require 'liquid/profiler/hooks' module Liquid # Profiler enables support for profiling template rendering to help track down performance issues. # @@ -21,11 +23,11 @@ # profile.each do |node| # # Access to the node itself # node.code # # # Which template and line number of this node. - # # If top level, this will be "<root>". + # # The top-level template name is `nil` by default, but can be set in the Liquid::Context before rendering. # node.partial # node.line_number # # # Render time in seconds of this node # node.render_time @@ -42,117 +44,96 @@ # class Profiler include Enumerable class Timing - attr_reader :code, :partial, :line_number, :children + attr_reader :code, :template_name, :line_number, :children + attr_accessor :total_time + alias_method :render_time, :total_time + alias_method :partial, :template_name - def initialize(node, partial) - @code = node.respond_to?(:raw) ? node.raw : node - @partial = partial - @line_number = node.respond_to?(:line_number) ? node.line_number : nil - @children = [] + def initialize(code: nil, template_name: nil, line_number: nil) + @code = code + @template_name = template_name + @line_number = line_number + @children = [] end - def self.start(node, partial) - new(node, partial).tap(&:start) + def self_time + @self_time ||= begin + total_children_time = 0.0 + @children.each do |child| + total_children_time += child.total_time + end + @total_time - total_children_time + end end + end - def start - @start_time = Time.now - end + attr_reader :total_time + alias_method :total_render_time, :total_time - def finish - @end_time = Time.now - end - - def render_time - @end_time - @start_time - end + def initialize + @root_children = [] + @current_children = nil + @total_time = 0.0 end - def self.profile_node_render(node) - if Profiler.current_profile && node.respond_to?(:render) - Profiler.current_profile.start_node(node) - output = yield - Profiler.current_profile.end_node(node) - output - else - yield + def profile(template_name, &block) + # nested renders are done from a tag that already has a timing node + return yield if @current_children + + root_children = @root_children + render_idx = root_children.length + begin + @current_children = root_children + profile_node(template_name, &block) + ensure + @current_children = nil + if (timing = root_children[render_idx]) + @total_time += timing.total_time + end end end - def self.profile_children(template_name) - if Profiler.current_profile - Profiler.current_profile.push_partial(template_name) - output = yield - Profiler.current_profile.pop_partial - output + def children + children = @root_children + if children.length == 1 + children.first.children else - yield + children end end - def self.current_profile - Thread.current[:liquid_profiler] - end - - def initialize - @partial_stack = ["<root>"] - - @root_timing = Timing.new("", current_partial) - @timing_stack = [@root_timing] - - @render_start_at = Time.now - @render_end_at = @render_start_at - end - - def start - Thread.current[:liquid_profiler] = self - @render_start_at = Time.now - end - - def stop - Thread.current[:liquid_profiler] = nil - @render_end_at = Time.now - end - - def total_render_time - @render_end_at - @render_start_at - end - def each(&block) - @root_timing.children.each(&block) + children.each(&block) end def [](idx) - @root_timing.children[idx] + children[idx] end def length - @root_timing.children.length + children.length end - def start_node(node) - @timing_stack.push(Timing.start(node, current_partial)) + def profile_node(template_name, code: nil, line_number: nil) + timing = Timing.new(code: code, template_name: template_name, line_number: line_number) + parent_children = @current_children + start_time = monotonic_time + begin + @current_children = timing.children + yield + ensure + @current_children = parent_children + timing.total_time = monotonic_time - start_time + parent_children << timing + end end - def end_node(_node) - timing = @timing_stack.pop - timing.finish + private - @timing_stack.last.children << timing - end - - def current_partial - @partial_stack.last - end - - def push_partial(partial_name) - @partial_stack.push(partial_name) - end - - def pop_partial - @partial_stack.pop + def monotonic_time + Process.clock_gettime(Process::CLOCK_MONOTONIC) end end end