Skip to content
  • Jean Boussier's avatar
    bf33510d
    Optimize CurrentAttributes method generation · bf33510d
    Jean Boussier authored
    The bulk of the optimization is to generate code rather than use
    `define_method` with a closure.
    
    ```
    Warming up --------------------------------------
                original   207.468k i/100ms
          code-generator   340.849k i/100ms
    Calculating -------------------------------------
                original      2.127M (± 1.1%) i/s -     10.788M in   5.073860s
          code-generator      3.426M (± 0.9%) i/s -     17.383M in   5.073965s
    
    Comparison:
          code-generator:  3426241.0 i/s
                original:  2126539.2 i/s - 1.61x  (± 0.00) slower
    ```
    
    ```ruby
    
    require 'benchmark/ips'
    require 'active_support/all'
    
    class Original < ActiveSupport::CurrentAttributes
      attribute :foo
    end
    
    class CodeGen < ActiveSupport::CurrentAttributes
      class << self
        def attribute(*names)
          ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
            names.each do |name|
              owner.define_cached_method(name, namespace: :current_attributes) do |batch|
                batch <<
                  "def #{name}" <<
                  "attributes[:#{name}]" <<
                  "end"
              end
              owner.define_cached_method("#{name}=", namespace: :current_attributes) do |batch|
                batch <<
                  "def #{name}=(value)" <<
                  "attributes[:#{name}] = value" <<
                  "end"
              end
            end
          end
    
          ActiveSupport::CodeGenerator.batch(singleton_class, __FILE__, __LINE__) do |owner|
            names.each do |name|
              owner.define_cached_method(name, namespace: :current_attributes_delegation) do |batch|
                batch <<
                  "def #{name}" <<
                  "instance.#{name}" <<
                  "end"
              end
              owner.define_cached_method("#{name}=", namespace: :current_attributes_delegation) do |batch|
                batch <<
                  "def #{name}=(value)" <<
                  "instance.#{name} = value" <<
                  "end"
              end
            end
          end
        end
      end
      attribute :foo
    end
    
    Benchmark.ips do |x|
      x.report('original') { Original.foo }
      x.report('code-generator') { CodeGen.foo }
      x.compare!
    end
    ```
    bf33510d
    Optimize CurrentAttributes method generation
    Jean Boussier authored
    The bulk of the optimization is to generate code rather than use
    `define_method` with a closure.
    
    ```
    Warming up --------------------------------------
                original   207.468k i/100ms
          code-generator   340.849k i/100ms
    Calculating -------------------------------------
                original      2.127M (± 1.1%) i/s -     10.788M in   5.073860s
          code-generator      3.426M (± 0.9%) i/s -     17.383M in   5.073965s
    
    Comparison:
          code-generator:  3426241.0 i/s
                original:  2126539.2 i/s - 1.61x  (± 0.00) slower
    ```
    
    ```ruby
    
    require 'benchmark/ips'
    require 'active_support/all'
    
    class Original < ActiveSupport::CurrentAttributes
      attribute :foo
    end
    
    class CodeGen < ActiveSupport::CurrentAttributes
      class << self
        def attribute(*names)
          ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
            names.each do |name|
              owner.define_cached_method(name, namespace: :current_attributes) do |batch|
                batch <<
                  "def #{name}" <<
                  "attributes[:#{name}]" <<
                  "end"
              end
              owner.define_cached_method("#{name}=", namespace: :current_attributes) do |batch|
                batch <<
                  "def #{name}=(value)" <<
                  "attributes[:#{name}] = value" <<
                  "end"
              end
            end
          end
    
          ActiveSupport::CodeGenerator.batch(singleton_class, __FILE__, __LINE__) do |owner|
            names.each do |name|
              owner.define_cached_method(name, namespace: :current_attributes_delegation) do |batch|
                batch <<
                  "def #{name}" <<
                  "instance.#{name}" <<
                  "end"
              end
              owner.define_cached_method("#{name}=", namespace: :current_attributes_delegation) do |batch|
                batch <<
                  "def #{name}=(value)" <<
                  "instance.#{name} = value" <<
                  "end"
              end
            end
          end
        end
      end
      attribute :foo
    end
    
    Benchmark.ips do |x|
      x.report('original') { Original.foo }
      x.report('code-generator') { CodeGen.foo }
      x.compare!
    end
    ```
Loading