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

Freeze strings to prevent re-allocation of newline/empty strings #961

Closed
wants to merge 6 commits into from
Closed

Freeze strings to prevent re-allocation of newline/empty strings #961

wants to merge 6 commits into from

Commits on Oct 20, 2017

  1. Configuration menu
    Copy the full SHA
    f70a7e5 View commit details
    Browse the repository at this point in the history
  2. CHANGELOG.md note

    dillonwelch committed Oct 20, 2017
    Configuration menu
    Copy the full SHA
    6858136 View commit details
    Browse the repository at this point in the history
  3. Freeze even more strings

    dillonwelch committed Oct 20, 2017
    Configuration menu
    Copy the full SHA
    b939015 View commit details
    Browse the repository at this point in the history

Commits on Oct 23, 2017

  1. Replace interpolated interpolation with cleaner method

    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 committed Oct 23, 2017
    Configuration menu
    Copy the full SHA
    42f4eaa View commit details
    Browse the repository at this point in the history

Commits on Oct 24, 2017

  1. Configuration menu
    Copy the full SHA
    de05e61 View commit details
    Browse the repository at this point in the history
  2. 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
    ```
    dillonwelch committed Oct 24, 2017
    Configuration menu
    Copy the full SHA
    2130e84 View commit details
    Browse the repository at this point in the history