Perf: Remove unneeded to_s and freeze strings

- No longer call to_s on string literals passed to undefined filters
- Freeze all string literals at the top of the generated code

This shows a ~9% performance improvement on the fluid benchmark when run without YJIT
This commit is contained in:
Sam Doiron
2022-04-20 11:03:36 -03:00
parent 7c70b56002
commit ba82f8d05e
2 changed files with 16 additions and 22 deletions
-8
View File
@@ -18,14 +18,6 @@ Liquid::Template.register_filter(ShopFilter)
context = Liquid::Context.new([tables, {}], {}, {}, false, Liquid::ResourceLimits.new(Liquid::Template.default_resource_limits))
require 'stackprof'
results = StackProf.run(raw: true, out: 'stackprof.dump') do
500_000.times do
@template.render(context)
end
end
Benchmark.ips do |x|
x.report("render") { @template.render(context) }
+16 -14
View File
@@ -31,9 +31,10 @@ module Liquid
show_ruby = ENV["SHOW_RUBY"] && ENV["SHOW_RUBY"].to_i || 0
STDERR.puts @ruby if show_ruby >= 1
res = RubyVM::InstructionSequence.compile(<<~RUBY).eval.call(@nodes)
# frozen_string_literal: true
->(__nodes) {
->(__context, __output, __l_product) {
__scope = __context.environments.first
__assigns = __context.scopes.last
#{@ruby}
__output
}
@@ -60,10 +61,11 @@ module Liquid
end
def output(node)
self << if node.is_a?(String)
"__output << #{node.inspect}\n"
compiled = compile_expr(node)
self << if compiled.is_a?(String)
"__output << #{compiled}\n"
else
"__output << #{compile_expr(node)}.to_s\n"
"__output << #{compiled}.to_s\n"
end
end
@@ -202,7 +204,7 @@ module Liquid
RUBY
},
"escape" => ->(compiler, expr, args, kwargs) {
"(_t = #{expr}; CGI.escape(_t) if _t)"
"(_t = #{expr}; CGI.escapeHTML(_t) if _t)"
},
"money" => ->(compiler, expr, args, kwargs) {
"(_m = #{expr}; _m.nil? ? '' : \"$ \#{(_m / 100.0).round(2)}\")"
@@ -302,26 +304,26 @@ module Liquid
class Condition
def compile_expr(compiler)
condition = if operator
if operator
left_expr = compiler.compile_expr(left)
right_expr = compiler.compile_expr(right)
expr = "((#{left_expr} #{operator} #{right_expr}) #{child_relation}"
if child_relation
child_expr = compiler.compile_expr(child_condition)
expr = "((#{left_expr} #{operator} #{right_expr}) #{child_relation} #{child_expr})"
else
expr = "(#{left_expr} #{operator} #{right_expr})"
end
expr
else
left_expr = compiler.compile_expr(left)
right_expr = compiler.compile_expr(right)
expr = "#{left_expr}"
end
if child_condition
child_expr = compiler.compile_expr(child_condition)
condition << " #{child_expr}"
end
condition
end
end
class Assign
def compile(compiler)
compliler.declare(@to)
compiler.declare(compiler.var_name(@to))
compiler << "#{compiler.var_name(@to)} = #{compiler.compile_expr(@from)}\n"
end
end