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

Replace interpolated interpolation with cleaner method #965

Merged
merged 1 commit into from Oct 26, 2017
Merged

Replace interpolated interpolation with cleaner method #965

merged 1 commit into from Oct 26, 2017

Conversation

dillonwelch
Copy link
Contributor

In micro-benchmarking, this change performs within margin of error of
the existing code but saves a string allocation each time the method is
called (assuming the quote strings in the old code were frozen).

begin
  require "bundler/inline"
rescue LoadError => e
  $stderr.puts "Bundler version 1.10 or later is required. Please update
                your Bundler"
  raise e
end

gemfile(true) do
  source "https://rubygems.org"

  gem "benchmark-ips"
end

def allocate_count
  GC.disable
  before = ObjectSpace.count_objects
  yield
  after = ObjectSpace.count_objects
  after.each { |k,v| after[k] = v - before[k] }
  after[:T_HASH] -= 1 # probe effect - we created the before hash.
  GC.enable
  result = after.reject { |k,v| v == 0 }
  GC.start
  result
end

interpolated = "hi"

  puts "'"' + interpolated + '"'"
  puts allocate_count { 1000.times { '"' + interpolated + '"' } }
  puts "%(\"\#{interpolated}\")"
  puts allocate_count { 1000.times { %("#{interpolated}") } }
  puts "\"\#{interpolated}\""
  puts allocate_count { 1000.times { "\"#{interpolated}\"" } }

Benchmark.ips do |x|
  x.report("'"' + interpolated + '"'") { '"' + interpolated + '"' }
  x.report("%(\"\#{interpolated}\")")  { %("#{interpolated}") }
  x.report("\"\#{interpolated}\"")     { "\"#{interpolated}\"" }
  x.compare!
end
' + interpolated + '
{:FREE=>-1892, :T_STRING=>2052}
%("#{interpolated}")
{:FREE=>-1001, :T_STRING=>1000}
"#{interpolated}"
{:FREE=>-1001, :T_STRING=>1000}
Warming up --------------------------------------
' + interpolated + '    81.706k i/100ms
%("#{interpolated}")   106.128k i/100ms
   "#{interpolated}"   137.855k i/100ms
Calculating -------------------------------------
' + interpolated + '      3.892M (±23.2%) i/s -     17.975M in   5.007068s
%("#{interpolated}")      3.722M (±17.3%) i/s -     17.830M in   5.022549s
   "#{interpolated}"      3.725M (±15.0%) i/s -     18.059M in   5.023493s

Comparison:
' + interpolated + ':  3892392.6 i/s
   "#{interpolated}":  3725385.8 i/s - same-ish: difference falls within error
%("#{interpolated}"):  3722401.7 i/s - same-ish: difference falls within error

results of benchmark.rb

                                         Haml |     ERB |  Erubis |
-------------------------------------------------------------------
Cached                                  0.094 |   0.083 |   0.073 |
ActionView                             16.937 |  11.760 |         |
ActionView with deep partials          38.459 |  40.428 |         |

compared to current master

                                         Haml |     ERB |  Erubis |
-------------------------------------------------------------------
Cached                                  0.174 |   0.420 |   0.224 |
ActionView                             21.360 |  11.631 |         |
ActionView with deep partials          43.563 |  39.238 |         |

@amatsuda
Copy link
Member

@oniofchaos Could you squash your commits, resolve the conflict in CHANGELOG, then push -f please?

In micro-benchmarking, this change performs within margin of error of
the existing code but saves a string allocation each time the method is
called (assuming the quote strings in the old code were frozen).

```
begin
  require "bundler/inline"
rescue LoadError => e
  $stderr.puts "Bundler version 1.10 or later is required. Please update
                your Bundler"
  raise e
end

gemfile(true) do
  source "https://rubygems.org"

  gem "benchmark-ips"
end

def allocate_count
  GC.disable
  before = ObjectSpace.count_objects
  yield
  after = ObjectSpace.count_objects
  after.each { |k,v| after[k] = v - before[k] }
  after[:T_HASH] -= 1 # probe effect - we created the before hash.
  GC.enable
  result = after.reject { |k,v| v == 0 }
  GC.start
  result
end

interpolated = "hi"

  puts "'"' + interpolated + '"'"
  puts allocate_count { 1000.times { '"' + interpolated + '"' } }
  puts "%(\"\#{interpolated}\")"
  puts allocate_count { 1000.times { %("#{interpolated}") } }
  puts "\"\#{interpolated}\""
  puts allocate_count { 1000.times { "\"#{interpolated}\"" } }

Benchmark.ips do |x|
  x.report("'"' + interpolated + '"'") { '"' + interpolated + '"' }
  x.report("%(\"\#{interpolated}\")")  { %("#{interpolated}") }
  x.report("\"\#{interpolated}\"")     { "\"#{interpolated}\"" }
  x.compare!
end
```ruby

```
' + interpolated + '
{:FREE=>-1892, :T_STRING=>2052}
%("#{interpolated}")
{:FREE=>-1001, :T_STRING=>1000}
"#{interpolated}"
{:FREE=>-1001, :T_STRING=>1000}
Warming up --------------------------------------
' + interpolated + '    81.706k i/100ms
%("#{interpolated}")   106.128k i/100ms
   "#{interpolated}"   137.855k i/100ms
Calculating -------------------------------------
' + interpolated + '      3.892M (±23.2%) i/s -     17.975M in   5.007068s
%("#{interpolated}")      3.722M (±17.3%) i/s -     17.830M in   5.022549s
   "#{interpolated}"      3.725M (±15.0%) i/s -     18.059M in   5.023493s

Comparison:
' + interpolated + ':  3892392.6 i/s
   "#{interpolated}":  3725385.8 i/s - same-ish: difference falls within error
%("#{interpolated}"):  3722401.7 i/s - same-ish: difference falls within error
```
@dillonwelch
Copy link
Contributor Author

@amatsuda done!

@amatsuda amatsuda merged commit 28e9222 into haml:master Oct 26, 2017
@amatsuda
Copy link
Member

Thanks!

@dillonwelch dillonwelch deleted the eval-interpolated-change branch October 30, 2017 16:41
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this pull request Mar 24, 2020
Update ruby-haml to 5.1.2.
pkgsrc change: add "USE_LANGUAGES=	# none".


## 5.1.2

Released on August 6, 2019
([diff](haml/haml@v5.1.1...v5.1.2)).

* Fix crash in some environments such as New Relic by unfreezing string literals for ParseNode#inspect. [#1016](haml/haml#1016) (thanks [Jalyna](https://github.com/jalyna))

## 5.1.1

Released on May 25, 2019
([diff](haml/haml@v5.1.0...v5.1.1)).

* Fix NameError bug for that happens on ruby 2.6.1-2.6.3 + haml 5.1.0 + rails 4.2.x + erubi. (Akira Matsuda)

## 5.1.0

Released on May 16, 2019
([diff](haml/haml@v5.0.4...v5.1.0)).

* Rails 6 support [#1008](haml/haml#1008) (thanks [Seb Jacobs](https://github.com/sebjacobs))
* Add `escape_filter_interpolations` option for backwards compatibility with haml 4 defaults [#984](haml/haml#984) (thanks [Will Jordan](https://github.com/wjordan))
* Fix error on empty :javascript and :css filter blocks [#986](haml/haml#986) (thanks [Will Jordan](https://github.com/wjordan))
* Respect changes in Haml::Options.defaults in `Haml::TempleEngine` options (Takashi Kokubun)
* Un-freeze TempleEngine precompiled string literals [#983](haml/haml#983) (thanks [Will Jordan](https://github.com/wjordan))
* Various performance/memory improvements [#965](haml/haml#965), [#966](haml/haml#966), [#963](haml/haml#963) (thanks [Dillon Welch](https://github.com/oniofchaos))
* Enable `frozen_string_literal` magic comment for all .rb files [#967](haml/haml#967) (thanks [Dillon Welch](https://github.com/oniofchaos))
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.

None yet

2 participants