mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-20 03:10:39 -07:00
Create top-level profile timing nodes for multiple template renders
This commit is contained in:
+56
-62
@@ -46,98 +46,92 @@ module Liquid
|
|||||||
include Enumerable
|
include Enumerable
|
||||||
|
|
||||||
class Timing
|
class Timing
|
||||||
attr_reader :code, :partial, :line_number, :children, :total_time, :self_time
|
attr_reader :code, :template_name, :line_number, :children
|
||||||
|
attr_accessor :total_time
|
||||||
alias_method :render_time, :total_time
|
alias_method :render_time, :total_time
|
||||||
|
alias_method :partial, :template_name
|
||||||
|
|
||||||
def initialize(node, template_name)
|
def initialize(code: nil, template_name: nil, line_number: nil)
|
||||||
@code = node.respond_to?(:raw) ? node.raw : node
|
@code = code
|
||||||
@partial = template_name
|
@template_name = template_name
|
||||||
@line_number = node.respond_to?(:line_number) ? node.line_number : nil
|
@line_number = line_number
|
||||||
@children = []
|
@children = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.start(node, template_name)
|
def self_time
|
||||||
new(node, template_name).tap(&:start)
|
@self_time ||= begin
|
||||||
end
|
total_children_time = 0.0
|
||||||
|
|
||||||
def start
|
|
||||||
@start_time = monotonic_time
|
|
||||||
end
|
|
||||||
|
|
||||||
def finish
|
|
||||||
@total_time = monotonic_time - @start_time
|
|
||||||
|
|
||||||
if @children.empty?
|
|
||||||
@self_time = @total_time
|
|
||||||
else
|
|
||||||
total_children_time = 0
|
|
||||||
@children.each do |child|
|
@children.each do |child|
|
||||||
total_children_time += child.total_time
|
total_children_time += child.total_time
|
||||||
end
|
end
|
||||||
@self_time = @total_time - total_children_time
|
@total_time - total_children_time
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
attr_reader :total_time
|
||||||
|
alias_method :total_render_time, :total_time
|
||||||
|
|
||||||
def monotonic_time
|
def initialize
|
||||||
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
@root_children = []
|
||||||
|
@current_children = nil
|
||||||
|
@total_time = 0.0
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :total_render_time
|
def children
|
||||||
|
children = @root_children
|
||||||
def initialize
|
if children.length == 1
|
||||||
@root_timing = Timing.new("", nil)
|
children.first.children
|
||||||
@timing_stack = [@root_timing]
|
else
|
||||||
@render_start_at = nil
|
children
|
||||||
@total_render_time = 0.0
|
|
||||||
end
|
|
||||||
|
|
||||||
def profile
|
|
||||||
return yield if @render_start_at
|
|
||||||
started_at = monotonic_time
|
|
||||||
begin
|
|
||||||
@started_at = started_at
|
|
||||||
yield
|
|
||||||
ensure
|
|
||||||
@started_at = nil
|
|
||||||
@total_render_time += monotonic_time - started_at
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def each(&block)
|
def each(&block)
|
||||||
@root_timing.children.each(&block)
|
children.each(&block)
|
||||||
end
|
end
|
||||||
|
|
||||||
def [](idx)
|
def [](idx)
|
||||||
@root_timing.children[idx]
|
children[idx]
|
||||||
end
|
end
|
||||||
|
|
||||||
def length
|
def length
|
||||||
@root_timing.children.length
|
children.length
|
||||||
end
|
end
|
||||||
|
|
||||||
def profile_node(node, template_name)
|
def profile_node(template_name, code: nil, line_number: nil)
|
||||||
start_node(node, template_name)
|
timing = Timing.new(code: code, template_name: template_name, line_number: line_number)
|
||||||
yield
|
parent_children = @current_children
|
||||||
ensure
|
start_time = monotonic_time
|
||||||
end_node
|
begin
|
||||||
|
@current_children = timing.children
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
@current_children = parent_children
|
||||||
|
timing.total_time = monotonic_time - start_time
|
||||||
|
parent_children << timing
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def start_node(node, template_name)
|
|
||||||
@timing_stack.push(Timing.start(node, template_name))
|
|
||||||
end
|
|
||||||
|
|
||||||
def end_node
|
|
||||||
timing = @timing_stack.pop
|
|
||||||
timing.finish
|
|
||||||
|
|
||||||
@timing_stack.last.children << timing
|
|
||||||
end
|
|
||||||
|
|
||||||
def monotonic_time
|
def monotonic_time
|
||||||
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ module Liquid
|
|||||||
module BlockBodyProfilingHook
|
module BlockBodyProfilingHook
|
||||||
def render_node(context, output, node)
|
def render_node(context, output, node)
|
||||||
if (profiler = context.profiler)
|
if (profiler = context.profiler)
|
||||||
profiler.profile_node(node, context.template_name) do
|
profiler.profile_node(context.template_name, code: node.raw, line_number: node.line_number) do
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -17,7 +17,7 @@ module Liquid
|
|||||||
module DocumentProfilingHook
|
module DocumentProfilingHook
|
||||||
def render_to_output_buffer(context, output)
|
def render_to_output_buffer(context, output)
|
||||||
return super unless context.profiler
|
return super unless context.profiler
|
||||||
context.profiler.profile { super }
|
context.profiler.profile(context.template_name) { super }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Document.prepend(DocumentProfilingHook)
|
Document.prepend(DocumentProfilingHook)
|
||||||
|
|||||||
@@ -103,12 +103,19 @@ class ProfilerTest < Minitest::Test
|
|||||||
with_custom_tag('sleep', SleepTag) do
|
with_custom_tag('sleep', SleepTag) do
|
||||||
context = Liquid::Context.new
|
context = Liquid::Context.new
|
||||||
t = Liquid::Template.parse("{% sleep 0.001 %}", profile: true)
|
t = Liquid::Template.parse("{% sleep 0.001 %}", profile: true)
|
||||||
|
context.template_name = 'index'
|
||||||
t.render!(context)
|
t.render!(context)
|
||||||
first_render_time = context.profiler.total_render_time
|
context.template_name = 'layout'
|
||||||
|
first_render_time = context.profiler.total_time
|
||||||
t.render!(context)
|
t.render!(context)
|
||||||
|
|
||||||
|
profiler = context.profiler
|
||||||
|
children = profiler.children
|
||||||
assert_operator(first_render_time, :>=, 0.001)
|
assert_operator(first_render_time, :>=, 0.001)
|
||||||
assert_operator(context.profiler.total_render_time, :>=, 0.001 + first_render_time)
|
assert_operator(profiler.total_time, :>=, 0.001 + first_render_time)
|
||||||
|
assert_equal(["index", "layout"], children.map(&:template_name))
|
||||||
|
assert_equal([nil, nil], children.map(&:code))
|
||||||
|
assert_equal(profiler.total_time, children.map(&:total_time).reduce(&:+))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user