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

Rewrite old attribute mapping to save many object allocations #963

Merged
merged 2 commits into from Oct 26, 2017

Conversation

Projects
None yet
2 participants
@oniofchaos
Contributor

oniofchaos commented Oct 25, 2017

Stringifying the keys of a hash can be done without allocating many
arrays like the previous approach did.

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

@old = {a: :b, c: :d, e: :f}

def master_version
  Hash[@old.map { |k, v| [k.to_s, v] }]
end

def fast_version
  result = {}
  @old.each { |k, v| result[k.to_s] = v }
end

puts "master_version"
puts allocate_count { 1000.times { master_version } }
puts "fast_version"
puts allocate_count { 1000.times { fast_version } }

Benchmark.ips do |x|
  x.report("master_version") { master_version }
  x.report("fast_version")     { fast_version }
  x.compare!
end
master_version
{:FREE=>-14768, :T_STRING=>6054, :T_ARRAY=>7000, :T_HASH=>1000, :T_IMEMO=>1000}
fast_version
{:FREE=>-7001, :T_STRING=>6000, :T_HASH=>1000}
Warming up --------------------------------------
      master_version    38.137k i/100ms
        fast_version    50.133k i/100ms
Calculating -------------------------------------
      master_version    451.898k (±19.2%) i/s -      2.174M in   5.002186s
        fast_version    633.579k (±19.4%) i/s -      3.058M in   5.019391s

Comparison:
        fast_version:   633578.7 i/s
      master_version:   451897.6 i/s - same-ish: difference falls within error

Results of benchmark.rb

                                         Haml |     ERB |  Erubis |
-------------------------------------------------------------------
Cached                                  0.086 |   0.074 |   0.066 |
ActionView                             17.940 |  12.237 |         |
ActionView with deep partials          44.639 |  40.158 |         |

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

This comment has been minimized.

Show comment
Hide comment
@amatsuda

amatsuda Oct 25, 2017

Member

@oniofchaos Thanks, this change makes sense.
But can you please rebase from master and push -f here instead of merging origin/master into your branch?

Member

amatsuda commented Oct 25, 2017

@oniofchaos Thanks, this change makes sense.
But can you please rebase from master and push -f here instead of merging origin/master into your branch?

@oniofchaos

This comment has been minimized.

Show comment
Hide comment
@oniofchaos

oniofchaos Oct 26, 2017

Contributor

@amatsuda done!

Contributor

oniofchaos commented Oct 26, 2017

@amatsuda done!

oniofchaos added some commits Oct 24, 2017

Rewrite old attribute mapping to save many object allocations
Stringifying the keys of a hash can be done without allocating many
arrays like the previous approach did.

```ruby
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

@old = {a: :b, c: :d, e: :f}

def master_version
  Hash[@old.map { |k, v| [k.to_s, v] }]
end

def fast_version
  result = {}
  @old.each { |k, v| result[k.to_s] = v }
end

puts "master_version"
puts allocate_count { 1000.times { master_version } }
puts "fast_version"
puts allocate_count { 1000.times { fast_version } }

Benchmark.ips do |x|
  x.report("master_version") { master_version }
  x.report("fast_version")     { fast_version }
  x.compare!
end
```

```ruby
master_version
{:FREE=>-14768, :T_STRING=>6054, :T_ARRAY=>7000, :T_HASH=>1000, :T_IMEMO=>1000}
fast_version
{:FREE=>-7001, :T_STRING=>6000, :T_HASH=>1000}
Warming up --------------------------------------
      master_version    38.137k i/100ms
        fast_version    50.133k i/100ms
Calculating -------------------------------------
      master_version    451.898k (±19.2%) i/s -      2.174M in   5.002186s
        fast_version    633.579k (±19.4%) i/s -      3.058M in   5.019391s

Comparison:
        fast_version:   633578.7 i/s
      master_version:   451897.6 i/s - same-ish: difference falls within error
```
@amatsuda

This comment has been minimized.

Show comment
Hide comment
@amatsuda
Member

amatsuda commented Oct 26, 2017

@oniofchaos Thanks!

@amatsuda amatsuda merged commit 291a7a7 into haml:master Oct 26, 2017

1 check passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details

@oniofchaos oniofchaos deleted the q-centrix:attribute-builder-change branch Oct 26, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment