Skip to content

Conversation

@samdoiron
Copy link
Contributor

@samdoiron samdoiron commented Jun 25, 2019

For loops support a range syntax for iterating a fixed number of times,
which looks like this.

{% for i in range (1..100) %}
        ...
{% endfor %}

Previously, we converted these ranges to arrays using to_a, which
initialized an array containing each number in the range. Since all we
use these ranges for is iteration, this is far less efficient than
simply keeping the ranges as ranges.

Doing this means that iterating over ranges now takes O(1) rather than O(n)
memory.

  • Remove to_a cast on ranges
  • Add helper method to check for empty ranges
  • Add custom range-specific slicing logic

Benchmarks

TLDR
CPU: ~10% speedup in simple cases, →∞% speedup in pathological cases.
Memory: 7% less usage in simple cases, →∞% less memory in pathological cases

This comes from a change of O(n) -> O(1) in terms of memory usage.

Simple iteration, CPU

Code
require 'benchmark/ips'

require 'liquid.rb'

Benchmark.ips do |x|
  x.time = 10
  x.warmup = 5

  puts
  puts "Running benchmark for #{x.time} seconds (with #{x.warmup} seconds warmup)."
  puts

  small_range_template = <<~LIQUID
  {% for _each in (1..100) %}
    Hello world
  {% endfor %}
  LIQUID
  small_range = Liquid::Template.parse(small_range_template)
  x.report("small range") do
    small_range.render!({})
  end

  large_range_template = <<~LIQUID
  {% for _each in (1..1000000) %}
    Hello world
  {% endfor %}
  LIQUID
  large_range = Liquid::Template.parse(large_range_template)
  x.report("large range") do
    large_range.render!({})
  end

  early_exit_template = <<~LIQUID
  {% for _each in (1..1000000) %}
    {% break %}
  {% endfor %}
  LIQUID
  early_exit = Liquid::Template.parse(early_exit_template)
  x.report("early exit") do
    early_exit.render!({})
  end
end
Before
Running benchmark for 10 seconds (with 5 seconds warmup).

Warming up --------------------------------------
         small range   796.000  i/100ms
         large range     1.000  i/100ms
          early exit     1.000  i/100ms
Calculating -------------------------------------
         small range      8.662k (± 2.7%) i/s -     86.764k in  10.023757s
         large range      0.930  (± 0.0%) i/s -     10.000  in  10.749791s
          early exit     10.655  (± 0.0%) i/s -    107.000  in  10.048596s
After
Running benchmark for 10 seconds (with 5 seconds warmup).

Warming up --------------------------------------
         small range   944.000  i/100ms
         large range     1.000  i/100ms
          early exit     9.465k i/100ms
Calculating -------------------------------------
         small range      9.589k (± 1.9%) i/s -     96.288k in  10.045111s
         large range      1.051  (± 0.0%) i/s -     11.000  in  10.466125s
          early exit     99.545k (± 4.1%) i/s -      1.003M in  10.097012s

Simple iteration, Memory

Code
require 'liquid.rb'
require 'memory_profiler'

def profile(phase, &block)
  puts
  puts "#{phase}:"
  puts

  report = MemoryProfiler.report(&block)

  report.pretty_print(
    color_output: true,
    scale_bytes: true,
    detailed_report: true
  )
end

small_range_template = <<~LIQUID
{% for _each in (1..100) %}
  Hello world
{% endfor %}
LIQUID
small_range = Liquid::Template.parse(small_range_template)
profile("small range") do
  small_range.render!({})
end

large_range_template = <<~LIQUID
{% for _each in (1..1000000) %}
  Hello world
{% endfor %}
LIQUID
large_range = Liquid::Template.parse(large_range_template)
profile("large range") do
  large_range.render!({})
end

early_exit_template = <<~LIQUID
{% for _each in (1..1000000) %}
  {% break %}
{% endfor %}
LIQUID
early_exit = Liquid::Template.parse(early_exit_template)
profile("early exit") do
  early_exit.render!({})
end
Before

small range:

Total allocated: 28.26 kB (226 objects)
Total retained:  1.05 kB (7 objects)

allocated memory by gem
-----------------------------------
  27.99 kB  liquid/lib
   272.0 B  other

allocated memory by file
-----------------------------------
  22.38 kB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb
   3.03 kB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
   1.06 kB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb
   920.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
   592.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb
   272.0 B  for_range_memory_profile.rb

allocated memory by location
-----------------------------------
  18.34 kB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97
   4.04 kB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:70
   1.58 kB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:157
   1.06 kB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:136
   1.06 kB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb:27
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:137
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
   232.0 B  for_range_memory_profile.rb:25
   184.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:183
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:19
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:32
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:160
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:20
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:35
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:36
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:148
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:154
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:221
    40.0 B  for_range_memory_profile.rb:24

allocated memory by class
-----------------------------------
   20.0 kB  String
   6.57 kB  Array
   1.39 kB  Hash
   144.0 B  Liquid::Context
    80.0 B  Liquid::ForloopDrop
    80.0 B  Proc

allocated objects by gem
-----------------------------------
       224  liquid/lib
         2  other

allocated objects by file
-----------------------------------
       202  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb
         9  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb
         6  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
         6  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
         2  for_range_memory_profile.rb
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb

allocated objects by location
-----------------------------------
       101  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:70
       101  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:19
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:183
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:137
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:20
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:32
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:35
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:36
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:136
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:148
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:154
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:157
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:160
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:221
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb:27
         1  for_range_memory_profile.rb:24
         1  for_range_memory_profile.rb:25

allocated objects by class
-----------------------------------
       113  Array
       104  String
         6  Hash
         1  Liquid::Context
         1  Liquid::ForloopDrop
         1  Proc

retained memory by gem
-----------------------------------
   1.05 kB  liquid/lib

retained memory by file
-----------------------------------
   696.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
   312.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb

retained memory by location
-----------------------------------
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:148
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:154

retained memory by class
-----------------------------------
   928.0 B  Hash
    80.0 B  Array
    40.0 B  String

retained objects by gem
-----------------------------------
         7  liquid/lib

retained objects by file
-----------------------------------
         3  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
         3  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb

retained objects by location
-----------------------------------
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:148
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:154
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146

retained objects by class
-----------------------------------
         4  Hash
         2  Array
         1  String


Allocated String Report
-----------------------------------
       100  "\n  Hello world\n"
       100  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97

         1  "\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  He"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97

         1  "\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  He"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:157

         1  "_each"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139

         1  "_each-(1..100)"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:148


Retained String Report
-----------------------------------
         1  "_each-(1..100)"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:148


large range:

Total allocated: 271.44 MB (2000026 objects)
Total retained:  1.05 kB (7 objects)

allocated memory by gem
-----------------------------------
 271.44 MB  liquid/lib
   272.0 B  other

allocated memory by file
-----------------------------------
  223.0 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb
   36.8 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
  11.64 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb
   912.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
   592.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb
   272.0 B  for_range_memory_profile.rb

allocated memory by location
-----------------------------------
  183.0 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97
   40.0 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:70
  25.17 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:157
  11.64 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:136
  11.64 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb:27
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:137
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
   232.0 B  for_range_memory_profile.rb:35
   176.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:183
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:19
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:32
    72.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:160
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:20
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:35
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:36
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:148
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:154
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:221
    40.0 B  for_range_memory_profile.rb:34

allocated memory by class
-----------------------------------
 208.17 MB  String
  63.27 MB  Array
   1.39 kB  Hash
   136.0 B  Liquid::Context
    80.0 B  Proc
    72.0 B  Liquid::ForloopDrop

allocated objects by gem
-----------------------------------
   2000024  liquid/lib
         2  other

allocated objects by file
-----------------------------------
   2000002  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb
         9  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb
         6  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
         6  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
         2  for_range_memory_profile.rb
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb

allocated objects by location
-----------------------------------
   1000001  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:70
   1000001  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:19
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:183
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:137
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:20
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:32
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:35
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:36
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:136
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:148
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:154
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:157
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:160
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:221
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb:27
         1  for_range_memory_profile.rb:34
         1  for_range_memory_profile.rb:35

allocated objects by class
-----------------------------------
   1000013  Array
   1000004  String
         6  Hash
         1  Liquid::Context
         1  Liquid::ForloopDrop
         1  Proc

retained memory by gem
-----------------------------------
   1.05 kB  liquid/lib

retained memory by file
-----------------------------------
   696.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
   312.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb

retained memory by location
-----------------------------------
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:148
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:154

retained memory by class
-----------------------------------
   928.0 B  Hash
    80.0 B  Array
    40.0 B  String

retained objects by gem
-----------------------------------
         7  liquid/lib

retained objects by file
-----------------------------------
         3  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
         3  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb

retained objects by location
-----------------------------------
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:148
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:154
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146

retained objects by class
-----------------------------------
         4  Hash
         2  Array
         1  String


Allocated String Report
-----------------------------------
   1000000  "\n  Hello world\n"
   1000000  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97

         1  "\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  He"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97

         1  "\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  He"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:157

         1  "_each"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139

         1  "_each-(1..1000000)"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:148


Retained String Report
-----------------------------------
         1  "_each-(1..1000000)"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:148


early exit:

Total allocated: 23.28 MB (28 objects)
Total retained:  1.01 kB (6 objects)

allocated memory by gem
-----------------------------------
  23.27 MB  liquid/lib
   272.0 B  other

allocated memory by file
-----------------------------------
  11.64 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
  11.64 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb
   912.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
   592.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb
   416.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb
   272.0 B  for_range_memory_profile.rb
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/break.rb

allocated memory by location
-----------------------------------
  11.64 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:136
  11.64 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb:27
   336.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:137
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
   232.0 B  for_range_memory_profile.rb:45
   176.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:183
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:70
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:19
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:32
    72.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:160
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:20
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:35
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:36
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/break.rb:13
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:154
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:157
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:221
    40.0 B  for_range_memory_profile.rb:44

allocated memory by class
-----------------------------------
  23.27 MB  Array
   1.39 kB  Hash
   416.0 B  String
   136.0 B  Liquid::Context
    80.0 B  Proc
    72.0 B  Liquid::ForloopDrop
    40.0 B  Liquid::BreakInterrupt

allocated objects by gem
-----------------------------------
        26  liquid/lib
         2  other

allocated objects by file
-----------------------------------
         9  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb
         6  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
         5  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
         4  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb
         2  for_range_memory_profile.rb
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/break.rb
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb

allocated objects by location
-----------------------------------
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:70
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:19
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:183
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:137
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:20
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:32
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:35
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:36
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/break.rb:13
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:136
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:154
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:157
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:160
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:221
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb:27
         1  for_range_memory_profile.rb:44
         1  for_range_memory_profile.rb:45

allocated objects by class
-----------------------------------
        14  Array
         6  Hash
         4  String
         1  Liquid::BreakInterrupt
         1  Liquid::Context
         1  Liquid::ForloopDrop
         1  Proc

retained memory by gem
-----------------------------------
   1.01 kB  liquid/lib

retained memory by file
-----------------------------------
   696.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
   272.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb

retained memory by location
-----------------------------------
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:154

retained memory by class
-----------------------------------
   928.0 B  Hash
    80.0 B  Array

retained objects by gem
-----------------------------------
         6  liquid/lib

retained objects by file
-----------------------------------
         3  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb

retained objects by location
-----------------------------------
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:154
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146

retained objects by class
-----------------------------------
         4  Hash
         2  Array


Allocated String Report
-----------------------------------
         2  "\n  "
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:157

         1  "\n  \n"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97

         1  "_each"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139


Retained String Report
-----------------------------------
After

small range:

Total allocated: 26.18 kB (225 objects)
Total retained:  1.05 kB (7 objects)

allocated memory by gem
-----------------------------------
   25.9 kB  liquid/lib
   272.0 B  other

allocated memory by file
-----------------------------------
  22.38 kB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb
   1.97 kB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
   920.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
   592.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb
   272.0 B  for_range_memory_profile.rb
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb

allocated memory by location
-----------------------------------
  18.34 kB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97
   4.04 kB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:70
   1.58 kB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:156
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:137
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
   232.0 B  for_range_memory_profile.rb:25
   184.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:183
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:19
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:32
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:159
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:20
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:35
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:36
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:147
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:153
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:221
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb:16
    40.0 B  for_range_memory_profile.rb:24

allocated memory by class
-----------------------------------
   20.0 kB  String
   4.44 kB  Array
   1.39 kB  Hash
   144.0 B  Liquid::Context
    80.0 B  Liquid::ForloopDrop
    80.0 B  Proc
    40.0 B  Range

allocated objects by gem
-----------------------------------
       223  liquid/lib
         2  other

allocated objects by file
-----------------------------------
       202  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb
         9  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb
         6  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
         5  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
         2  for_range_memory_profile.rb
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb

allocated objects by location
-----------------------------------
       101  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:70
       101  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:19
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:183
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:137
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:20
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:32
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:35
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:36
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:147
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:153
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:156
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:159
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:221
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb:16
         1  for_range_memory_profile.rb:24
         1  for_range_memory_profile.rb:25

allocated objects by class
-----------------------------------
       111  Array
       104  String
         6  Hash
         1  Liquid::Context
         1  Liquid::ForloopDrop
         1  Proc
         1  Range

retained memory by gem
-----------------------------------
   1.05 kB  liquid/lib

retained memory by file
-----------------------------------
   696.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
   312.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb

retained memory by location
-----------------------------------
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:147
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:153

retained memory by class
-----------------------------------
   928.0 B  Hash
    80.0 B  Array
    40.0 B  String

retained objects by gem
-----------------------------------
         7  liquid/lib

retained objects by file
-----------------------------------
         3  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
         3  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb

retained objects by location
-----------------------------------
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:147
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:153
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146

retained objects by class
-----------------------------------
         4  Hash
         2  Array
         1  String


Allocated String Report
-----------------------------------
       100  "\n  Hello world\n"
       100  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97

         1  "\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  He"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:156

         1  "\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  He"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97

         1  "_each"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139

         1  "_each-(1..100)"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:147


Retained String Report
-----------------------------------
         1  "_each-(1..100)"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:147


large range:

Total allocated: 248.17 MB (2000025 objects)
Total retained:  1.05 kB (7 objects)

allocated memory by gem
-----------------------------------
 248.17 MB  liquid/lib
   272.0 B  other

allocated memory by file
-----------------------------------
  223.0 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb
  25.17 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
   912.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
   592.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb
   272.0 B  for_range_memory_profile.rb
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb

allocated memory by location
-----------------------------------
  183.0 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97
   40.0 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:70
  25.17 MB  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:156
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:137
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
   232.0 B  for_range_memory_profile.rb:35
   176.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:183
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:19
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:32
    72.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:159
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:20
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:35
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:36
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:147
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:153
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:221
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb:16
    40.0 B  for_range_memory_profile.rb:34

allocated memory by class
-----------------------------------
 208.17 MB  String
   40.0 MB  Array
   1.39 kB  Hash
   136.0 B  Liquid::Context
    80.0 B  Proc
    72.0 B  Liquid::ForloopDrop
    40.0 B  Range

allocated objects by gem
-----------------------------------
   2000023  liquid/lib
         2  other

allocated objects by file
-----------------------------------
   2000002  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb
         9  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb
         6  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
         5  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
         2  for_range_memory_profile.rb
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb

allocated objects by location
-----------------------------------
   1000001  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:70
   1000001  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:19
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:183
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:137
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:20
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:32
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:35
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:36
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:147
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:153
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:156
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:159
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:221
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb:16
         1  for_range_memory_profile.rb:34
         1  for_range_memory_profile.rb:35

allocated objects by class
-----------------------------------
   1000011  Array
   1000004  String
         6  Hash
         1  Liquid::Context
         1  Liquid::ForloopDrop
         1  Proc
         1  Range

retained memory by gem
-----------------------------------
   1.05 kB  liquid/lib

retained memory by file
-----------------------------------
   696.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
   312.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb

retained memory by location
-----------------------------------
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:147
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:153

retained memory by class
-----------------------------------
   928.0 B  Hash
    80.0 B  Array
    40.0 B  String

retained objects by gem
-----------------------------------
         7  liquid/lib

retained objects by file
-----------------------------------
         3  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
         3  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb

retained objects by location
-----------------------------------
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:147
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:153
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146

retained objects by class
-----------------------------------
         4  Hash
         2  Array
         1  String


Allocated String Report
-----------------------------------
   1000000  "\n  Hello world\n"
   1000000  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97

         1  "\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  He"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97

         1  "\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  Hello world\n\n  He"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:156

         1  "_each"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139

         1  "_each-(1..1000000)"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:147


Retained String Report
-----------------------------------
         1  "_each-(1..1000000)"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:147


early exit:

Total allocated: 2.66 kB (27 objects)
Total retained:  1.01 kB (6 objects)

allocated memory by gem
-----------------------------------
   2.38 kB  liquid/lib
   272.0 B  other

allocated memory by file
-----------------------------------
   912.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
   592.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb
   416.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb
   384.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
   272.0 B  for_range_memory_profile.rb
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/break.rb
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb

allocated memory by location
-----------------------------------
   336.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:137
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
   232.0 B  for_range_memory_profile.rb:45
   176.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:183
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:70
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:19
    80.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:32
    72.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:159
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:20
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:35
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:36
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/break.rb:13
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:153
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:156
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:221
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb:16
    40.0 B  for_range_memory_profile.rb:44

allocated memory by class
-----------------------------------
   1.39 kB  Hash
   480.0 B  Array
   416.0 B  String
   136.0 B  Liquid::Context
    80.0 B  Proc
    72.0 B  Liquid::ForloopDrop
    40.0 B  Liquid::BreakInterrupt
    40.0 B  Range

allocated objects by gem
-----------------------------------
        25  liquid/lib
         2  other

allocated objects by file
-----------------------------------
         9  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb
         6  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
         4  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb
         4  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
         2  for_range_memory_profile.rb
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/break.rb
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb

allocated objects by location
-----------------------------------
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:70
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:19
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:183
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:137
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:20
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:32
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:35
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:36
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/break.rb:13
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:153
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:156
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:159
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:221
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/utils.rb:16
         1  for_range_memory_profile.rb:44
         1  for_range_memory_profile.rb:45

allocated objects by class
-----------------------------------
        12  Array
         6  Hash
         4  String
         1  Liquid::BreakInterrupt
         1  Liquid::Context
         1  Liquid::ForloopDrop
         1  Proc
         1  Range

retained memory by gem
-----------------------------------
   1.01 kB  liquid/lib

retained memory by file
-----------------------------------
   696.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
   272.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb

retained memory by location
-----------------------------------
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
   232.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
    40.0 B  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:153

retained memory by class
-----------------------------------
   928.0 B  Hash
    80.0 B  Array

retained objects by gem
-----------------------------------
         6  liquid/lib

retained objects by file
-----------------------------------
         3  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb
         2  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb

retained objects by location
-----------------------------------
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:22
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:122
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:153
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:138
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:142
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/template.rb:146

retained objects by class
-----------------------------------
         4  Hash
         2  Array


Allocated String Report
-----------------------------------
         2  "\n  "
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/tags/for.rb:156

         1  "\n  \n"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/block_body.rb:97

         1  "_each"
         1  /Users/samdoiron/src/github.com/Shopify/liquid/lib/liquid/context.rb:139


Retained String Report
-----------------------------------

@samdoiron
Copy link
Contributor Author

Unfortunately our integration benchmarks to not use ranges, so there was no change there. However, this should see a reasonable speedup / memory savings in practical usage.

@ashmaroli
Copy link
Contributor

Why the change from #length to #size? The two methods are aliases of each other..

@samdoiron
Copy link
Contributor Author

@ashmaroli They are aliases in many cases, but length is not defined on ranges, whereas size is.

@ashmaroli
Copy link
Contributor

but length is not defined on ranges

Ha! T.I.L.
Thanks 😃

@ashmaroli
Copy link
Contributor

@samdoiron You can improve the readability of the reports from MemoryProfiler by passing color_output: false to Report#pretty_print.. The escape sequences are too noisy..

@samdoiron
Copy link
Contributor Author

Fixed!

Copy link
Contributor

@pushrax pushrax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Liquid doesn't allow iterating over ranges with floating point bounds (since Ruby doesn't), and doesn't have a way to change the step size, so this math looks all correct to me. Only issue I see is that reverse will not work.

end

segment = Utils.slice_collection(collection, from, to)
segment.reverse! if @reversed
Copy link
Contributor

@pushrax pushrax Jun 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Range doesn't have reverse! (or reverse), could you add a test for that? To make this work, I think we may need our own range class, since reverse_each uses a temporary array. Or just convert to an array in the reverse case and keep status quo, but I think the optimization here is great and not too hard.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

range.max.downto(range.min) should also work there, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, actually that would make empty? checking difficult. I'll throw together a custom enumerator.

@samdoiron
Copy link
Contributor Author

samdoiron commented Jul 5, 2019

A quick note, since I haven't been able to focus on this the last few days: this will require a concurrent change in Liquid C, which directly generates Range objects. I hope to have a separate PR there to update this behaviour soon.

samdoiron added a commit to Shopify/liquid-c that referenced this pull request Jul 8, 2019
samdoiron added a commit to Shopify/liquid-c that referenced this pull request Jul 8, 2019
samdoiron added a commit to Shopify/liquid-c that referenced this pull request Jul 8, 2019
@samdoiron samdoiron force-pushed the range-optimization branch 2 times, most recently from b3d0be6 to 11b31e4 Compare July 9, 2019 14:47
For loops support a range syntax for iterating a fixed number of times,
which looks like this.

```liquid
{% for i in range (1..100) %}
        ...
{% endfor %}
```

Previously, we converted these ranges to arrays using `to_a`, which
initialized an array containing each number in the range. Since all we
use these ranges for is iteration, this is far less efficient than
using a range iterator.

Doing this means that iterating over ranges now takes O(1) rather than O(n)
memory. See the PR for more benchmarks.

* Remove to_a cast on ranges
* Add ReversableRange iterator
* Add custom range-specific slicing logic
@shopmike
Copy link
Contributor

@samdoiron I just opened up a PR which is a possible match to this for the liquid-c side. Shopify/liquid-c#48

@shopmike
Copy link
Contributor

shopmike commented Aug 29, 2019

I've just realised you already did one Shopify/liquid-c#46 Oh well I guess either implementation is an option

@samdoiron
Copy link
Contributor Author

👍 I'm happy to go with yours.

@shopmike
Copy link
Contributor

shopmike commented Sep 2, 2019

@pushrax can we get your feedback on this and the approach for liquid-c.

Copy link
Contributor

@pushrax pushrax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty much LGTM

@@ -0,0 +1,77 @@
module Liquid
class ReversableRange
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reversible is a more correct spelling I think.

@@ -0,0 +1,77 @@
module Liquid
class ReversableRange
include Enumerable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've been moving away from using Ruby builtin modules in Liquid-accessible types, as their public interface can change between Ruby versions. However, it looks okay to use Enumerable here as this is not a drop and doesn't expose its methods to Liquid.

other.is_a?(self.class) &&
other.min == min &&
other.max == max &&
other.reversed == reversed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we care about making ranges with switched min/max and opposite reverse being equal?


def each
if reversed
index = max
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's likely a premature optimization, but using ivars directly is a fair bit faster in general.

Warming up --------------------------------------
         attr_reader   147.241k i/s -    150.359k times in 1.021174s (6.79μs/i)
                ivar   247.108k i/s -    249.557k times in 1.009912s (4.05μs/i)
Calculating -------------------------------------
         attr_reader   143.032k i/s -    441.723k times in 3.088285s (6.99μs/i)
                ivar   255.390k i/s -    741.323k times in 2.902711s (3.92μs/i)

Comparison:
                ivar:    255389.9 i/s
         attr_reader:    143031.8 i/s - 1.79x  slower

with

    def each
      if reversed
        index = @max
        while index >= @min
          yield index
          index -= 1
        end
      else
        index = @min
        while index <= @max
          yield index
          index += 1
        end
      end
    end

using ReversableRange.new(1, 100)

@shopmike
Copy link
Contributor

I'm assuming this would also fix #1071

@samdoiron
Copy link
Contributor Author

Yes, it would.

@samdoiron samdoiron closed this Apr 21, 2021
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

Successfully merging this pull request may close these issues.

6 participants