From 1043a6d866ce290b6140acdcff287189cdaa2642 Mon Sep 17 00:00:00 2001 From: Dillon Welch Date: Mon, 23 Oct 2017 17:34:53 -0700 Subject: [PATCH 1/2] Rewrite old attribute mapping to save many object allocations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 ``` --- lib/haml/buffer.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/haml/buffer.rb b/lib/haml/buffer.rb index 81a4e853b0..8fd92bbc4e 100644 --- a/lib/haml/buffer.rb +++ b/lib/haml/buffer.rb @@ -132,7 +132,9 @@ def adjust_tabs(tab_change) def attributes(class_id, obj_ref, *attributes_hashes) attributes = class_id attributes_hashes.each do |old| - AttributeBuilder.merge_attributes!(attributes, Hash[old.map {|k, v| [k.to_s, v]}]) + result = {} + old.each { |k, v| result[k.to_s] = v } + AttributeBuilder.merge_attributes!(attributes, result) end AttributeBuilder.merge_attributes!(attributes, parse_object_ref(obj_ref)) if obj_ref AttributeBuilder.build_attributes( From 3bba45257a3ad8cca068924281719955ac05abf6 Mon Sep 17 00:00:00 2001 From: Dillon Welch Date: Wed, 25 Oct 2017 17:11:07 -0700 Subject: [PATCH 2/2] CHANGELOG.md note --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e08c9dd70c..2525d76df7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Haml Changelog +* Performance/memory usage improvement in `lib/haml/buffer.rb` [#963](https://github.com/haml/haml/pull/963) (thanks [Dillon Welch](https://github.com/oniofchaos)) * Add erubis to gemspec so that `benchmark.rb` works [#964](https://github.com/haml/haml/pull/964) (thanks [Dillon Welch](https://github.com/oniofchaos)) ## 5.0.4