Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What is faster: assign or capture? #487

Closed
doktorbro opened this issue Nov 16, 2014 · 19 comments
Closed

What is faster: assign or capture? #487

doktorbro opened this issue Nov 16, 2014 · 19 comments

Comments

@doktorbro
Copy link

I’m curious which procedure runs faster. Given is the variable bar.

Capture

{% capture foobar %}foo{{ bar }}{% endcapture %}

Assign

{% assign foobar = "foo" | append: bar %}
@nulian
Copy link

nulian commented Nov 19, 2014

I guess assign based on it doing the same as capture except capture has all the extra logica for looping through all the block data.

@doktorbro
Copy link
Author

@nulian Thank you for the explanation. This is what I assume too.

Any thoughts from @pushrax?

@fw42
Copy link
Contributor

fw42 commented Nov 21, 2014

require "liquid"
require "benchmark"

template1 = '{% capture foobar %}foo{{ bar }}{% endcapture %}{{ foo }}{{ foobar }}'
template2 = '{% assign foobar = "foo" | append: bar %}{{ foobar }}'

def render(template)
  Liquid::Template.parse(template).render("bar" => "42")
end

puts render(template1)
puts render(template2)

n = 100_000

Benchmark.bm(10) do |b|
  b.report("capture") do
    n.times do
      render(template1)
    end
  end

  b.report("assign") do
    n.times do
      render(template2)
    end
  end
end
$ ruby test.rb
foo42
foo42
                 user     system      total        real
capture     11.750000   0.120000  11.870000 ( 11.899615)
assign      10.890000   0.140000  11.030000 ( 11.039257)

$ ruby test.rb
foo42
foo42
                 user     system      total        real
capture     11.200000   0.130000  11.330000 ( 11.361710)
assign      10.460000   0.070000  10.530000 ( 10.565622)

$ ruby test.rb
foo42
foo42
                 user     system      total        real
capture     11.310000   0.190000  11.500000 ( 11.520578)
assign      10.580000   0.020000  10.600000 ( 10.626223)

@doktorbro
Copy link
Author

@fw42 This is awesome. Now I know how to profile tags.

@doktorbro
Copy link
Author

@fw42 In the line 4 you have a {{ foo }} too much. The code should be:

require "liquid"
require "benchmark"

template1 = '{% capture foobar %}foo{{ bar }}{% endcapture %}{{ foobar }}'
template2 = '{% assign foobar = "foo" | append: bar %}{{ foobar }}'

def render(template)
  Liquid::Template.parse(template).render("bar" => "42")
end

puts render(template1)
puts render(template2)

n = 100_000

Benchmark.bm 10 do |b|
  b.report "capture" do
    n.times do
      render template1
    end
  end

  b.report "assign" do
    n.times do
      render template2
    end
  end
end

Results on my desktop:

#1
                 user     system      total        real
capture     13.160000   0.000000  13.160000 ( 13.166675)
assign      18.190000   0.170000  18.360000 ( 18.440864)

# 2
                 user     system      total        real
capture     13.310000   0.000000  13.310000 ( 13.332725)
assign      18.140000   0.000000  18.140000 ( 18.167654)

# 3
                 user     system      total        real
capture     13.230000   0.000000  13.230000 ( 13.243090)
assign      18.020000   0.200000  18.220000 ( 18.332655)

Surprisingly the capture tag is faster.

@fw42
Copy link
Contributor

fw42 commented Nov 21, 2014

oh good catch

@fw42
Copy link
Contributor

fw42 commented Nov 21, 2014

strange, I would not have expected capture to be faster

@pushrax
Copy link
Contributor

pushrax commented Nov 21, 2014

I'd be interested in knowing this for Liquid-C as well, will give it a test.

@pushrax
Copy link
Contributor

pushrax commented Nov 21, 2014

They are similar with Liquid-C:

#1
                 user     system      total        real
capture      3.640000   0.000000   3.640000 (  3.641564)
assign       3.820000   0.010000   3.830000 (  3.824455)

#2
                 user     system      total        real
capture      3.660000   0.010000   3.670000 (  3.667432)
assign       3.670000   0.000000   3.670000 (  3.677738)

#3
                 user     system      total        real
capture      3.450000   0.000000   3.450000 (  3.463769)
assign       3.620000   0.010000   3.630000 (  3.628440)

(my computer gets these results without C):

                 user     system      total        real
capture      6.340000   0.010000   6.350000 (  6.356126)
assign       7.140000   0.010000   7.150000 (  7.158012)

So it looks like the slowness of assign is from the extra complexity in the parsing step.

@doktorbro
Copy link
Author

Adding another append increases the time difference even more.

template1 = '{% capture foobar %}foo{{ bar }}baz{% endcapture %}{{ foobar }}'
template2 = '{% assign foobar = "foo" | append: bar | append: "baz" %}{{ foobar }}'
foo42baz
foo42baz
                 user     system      total        real
capture     13.390000   0.000000  13.390000 ( 13.403049)
assign      21.320000   0.160000  21.480000 ( 21.489370)

@pushrax
Copy link
Contributor

pushrax commented Nov 22, 2014

@penibelst which version of Liquid are you using?

I get this with the extra append:

                 user     system      total        real
capture      6.400000   0.010000   6.410000 (  6.408164)
assign       8.140000   0.000000   8.140000 (  8.146811)

and with C:

                 user     system      total        real
capture      3.610000   0.000000   3.610000 (  3.619326)
assign       3.760000   0.010000   3.770000 (  3.767194)

@doktorbro
Copy link
Author

@pushrax 2.6.1

@pushrax
Copy link
Contributor

pushrax commented Nov 23, 2014

Can you try against 3.0.0? I don't really expect a huge difference, but it's still worthwhile.

@doktorbro
Copy link
Author

@pushrax

v3.0.0
foo42
foo42
                 user     system      total        real
capture     14.880000   0.000000  14.880000 ( 14.890935)
assign      16.330000   0.030000  16.360000 ( 16.374504)

# with extra append
v3.0.0
foo42baz
foo42baz
                 user     system      total        real
capture     15.450000   0.060000  15.510000 ( 15.525165)
assign      19.170000   0.170000  19.340000 ( 19.411027)

@dyspop
Copy link

dyspop commented Jun 20, 2016

does ruby "benchmark" still exist?

$ ruby -v ruby 2.2.1p85 (2015-02-26 revision 49769) [x86_64-darwin14]

$ gem install benchmark ERROR: Could not find a valid gem 'benchmark' (>= 0) in any repository ERROR: Possible alternatives: pbenchmark, penchmark, benchmarkx, benchmark_me, benchmarker

thanks from two years ago

@parkr
Copy link
Contributor

parkr commented Jun 20, 2016

@dyspop I believe benchmark is bundled in the Ruby stdlib now. No gem needed 😄

@dyspop
Copy link

dyspop commented Jun 20, 2016

huzzah

@parkr
Copy link
Contributor

parkr commented Jun 21, 2016

Using benchmark-ips:

require "liquid"
require "benchmark/ips"

puts "Ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
puts "Liquid #{Liquid::VERSION}"

template1 = '{% capture foobar %}foo{{ bar }}{% endcapture %}{{ foo }}{{ foobar }}'
template2 = '{% assign foobar = "foo" | append: bar %}{{ foobar }}'

def render(template)
    Liquid::Template.parse(template).render("bar" => "42")
end

puts render(template1)
puts render(template2)

Benchmark.ips do |x|
  x.report("capture") { render(template1) }
  x.report("assign")  { render(template2) }
end
Ruby 2.3.1-p112
Liquid 3.0.6
foo42
foo42
Warming up --------------------------------------
             capture     1.382k i/100ms
              assign     1.509k i/100ms
Calculating -------------------------------------
             capture     13.982k (± 4.5%) i/s -     70.482k in   5.051386s
              assign     15.041k (± 5.2%) i/s -     75.450k in   5.029955s

@alibosworth
Copy link

Very old issue, but in case it is useful to anyone in the future, I used the benchmark-ips test above from @parkr to test this again, wondering if anything had changed with this in the last 8 years, and for me locally on ruby 3.2.2 it shows assign as now 20% faster:

Ruby 3.2.2-p53
Liquid 5.5.0
foo42
foo42
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]
Warming up --------------------------------------
             capture     2.104k i/100ms
              assign     2.503k i/100ms
Calculating -------------------------------------
             capture     21.162k (± 0.4%) i/s -    107.304k in   5.070682s
              assign     25.027k (± 0.8%) i/s -    125.150k in   5.001010s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants