Compare commits

...

2 Commits

Author SHA1 Message Date
Aaron Patterson
94df9a8f52 Update lib/liquid/standardfilters.rb
Co-authored-by: Dylan Thacker-Smith <dylan.smith@shopify.com>
2020-11-16 10:23:20 -08:00
Aaron Patterson
8bf0e7dfae Reduce allocations in truncatewords
I don't know if this is a bottleneck IRL, but while doing some
performance testing I noticed that this method allocates a bunch of
objects and I decided to fix it.

Here is the benchmark I used:

```ruby
require_relative 'theme_runner'

Liquid::Template.error_mode = ARGV.first.to_sym if ARGV.first
profiler = ThemeRunner.new

def count_alloc
  before = GC.stat(:total_allocated_objects)
  yield
  GC.stat(:total_allocated_objects) - before
end

profiler.render # heat
p count_alloc { profiler.render }
p count_alloc { profiler.render }
p count_alloc { profiler.render }
```

Before this change:

```
[aaron@tc-lan-adapter ~/g/liquid (master)]$ ruby -I lib performance/run_once.rb
15753
15750
15752
```

After this change:

```
[aaron@tc-lan-adapter ~/g/liquid (master)]$ ruby -I lib performance/run_once.rb
14015
14010
14011
```

About an 11% reduction in allocations for this test.  I also added some
tests around the current behavior of the method so we can be more sure
the replacement behaves the same way
2020-10-09 16:01:12 -07:00
2 changed files with 17 additions and 5 deletions

View File

@@ -89,13 +89,21 @@ module Liquid
def truncatewords(input, words = 15, truncate_string = "...")
return if input.nil?
wordlist = input.to_s.split
words = Utils.to_integer(words)
words = Utils.to_integer(words)
l = words - 1
l = 0 if l < 0
words = 1 if words <= 0
wordlist.length > l ? wordlist[0..l].join(" ").concat(truncate_string.to_s) : input
# Scan for non-space characters followed by one or more space characters
# `words` times. Also ignore leading whitespace
str = input[/\A[ ]*(?:[^ ]*[ ]+){#{words}}/]
if str
str.strip! # Remove trailing space
str.gsub!(/[ ]{2,}/, " ") # Shrink multiple spaces to one space
str.concat(truncate_string.to_s)
else
input
end
end
# Split input string into an array of substrings separated by given pattern.

View File

@@ -168,6 +168,8 @@ class StandardFiltersTest < Minitest::Test
def test_truncatewords
assert_equal('one two three', @filters.truncatewords('one two three', 4))
assert_equal('one two...', @filters.truncatewords('one two three', 2))
assert_equal('one two...', @filters.truncatewords('one two three', 2))
assert_equal('one two...', @filters.truncatewords(' one two three', 2))
assert_equal('one two three', @filters.truncatewords('one two three'))
assert_equal(
'Two small (13&#8221; x 5.5&#8221; x 10&#8221; high) baskets fit inside one large basket (13&#8221;...',
@@ -175,6 +177,8 @@ class StandardFiltersTest < Minitest::Test
)
assert_equal("测试测试测试测试", @filters.truncatewords('测试测试测试测试', 5))
assert_equal('one two1', @filters.truncatewords("one two three", 2, 1))
assert_equal('one1', @filters.truncatewords("one two three", 0, 1))
assert_equal('one1', @filters.truncatewords("one two three", -1, 1))
end
def test_strip_html