Skip to content
  • Hartley McGuire's avatar
    597b56cd
    Optimize TagBuilder tag generation · 597b56cd
    Hartley McGuire authored
    
    
    Currently there's about a 35% difference between tags generated using
    the `TagBuilder` and tags generated by passing a positional argument to
    `#tag`.
    
    This commit optimizes `TagBuilder` to reduce that difference down to 13%.
    
    The first change is to perform less hash allocations by not splatting
    the options twice in the `TagBuilder` (one at the `tag.a` invocation,
    and one at `tag_string`). The extra splat for `tag_string` was moved
    into `method_missing` since that is the only other caller of this
    private method.
    
    The other change is to only escape the content in `tag_string` if it a
    non-empty.
    
    Additionally, a test was tweaked to ensure that passing `options` to a
    `self_closing_element` is tested as it was previously not.
    
    Benchmark:
    
    ```
    require "action_view"
    require "benchmark/ips"
    
    class Foo
      include ActionView::Helpers
    end
    
    helpers = Foo.new
    
    Benchmark.ips do |x|
      x.report("tag") { helpers.tag("a", href: "foo") }
      x.report("tag_builder") { helpers.tag.a(href: "foo") }
      x.compare!
    end
    ```
    
    Before:
    
    ```
    ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]
    Warming up --------------------------------------
                     tag    67.180k i/100ms
             tag_builder    50.267k i/100ms
    Calculating -------------------------------------
                     tag    673.064k (± 0.4%) i/s -      3.426M in   5.090520s
             tag_builder    504.971k (± 0.4%) i/s -      2.564M in   5.076842s
    
    Comparison:
                     tag:   673063.7 i/s
             tag_builder:   504971.4 i/s - 1.33x  slower
    ```
    
    After:
    
    ```
    ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]
    Warming up --------------------------------------
                     tag    67.374k i/100ms
             tag_builder    59.702k i/100ms
    Calculating -------------------------------------
                     tag    670.837k (± 0.4%) i/s -      3.369M in   5.021714s
             tag_builder    592.727k (± 1.3%) i/s -      2.985M in   5.037088s
    
    Comparison:
                     tag:   670836.6 i/s
             tag_builder:   592726.7 i/s - 1.13x  slower
    ```
    
    Co-authored-by: default avatarSean Doyle <seanpdoyle@users.noreply.github.com>
    597b56cd
    Optimize TagBuilder tag generation
    Hartley McGuire authored
    
    
    Currently there's about a 35% difference between tags generated using
    the `TagBuilder` and tags generated by passing a positional argument to
    `#tag`.
    
    This commit optimizes `TagBuilder` to reduce that difference down to 13%.
    
    The first change is to perform less hash allocations by not splatting
    the options twice in the `TagBuilder` (one at the `tag.a` invocation,
    and one at `tag_string`). The extra splat for `tag_string` was moved
    into `method_missing` since that is the only other caller of this
    private method.
    
    The other change is to only escape the content in `tag_string` if it a
    non-empty.
    
    Additionally, a test was tweaked to ensure that passing `options` to a
    `self_closing_element` is tested as it was previously not.
    
    Benchmark:
    
    ```
    require "action_view"
    require "benchmark/ips"
    
    class Foo
      include ActionView::Helpers
    end
    
    helpers = Foo.new
    
    Benchmark.ips do |x|
      x.report("tag") { helpers.tag("a", href: "foo") }
      x.report("tag_builder") { helpers.tag.a(href: "foo") }
      x.compare!
    end
    ```
    
    Before:
    
    ```
    ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]
    Warming up --------------------------------------
                     tag    67.180k i/100ms
             tag_builder    50.267k i/100ms
    Calculating -------------------------------------
                     tag    673.064k (± 0.4%) i/s -      3.426M in   5.090520s
             tag_builder    504.971k (± 0.4%) i/s -      2.564M in   5.076842s
    
    Comparison:
                     tag:   673063.7 i/s
             tag_builder:   504971.4 i/s - 1.33x  slower
    ```
    
    After:
    
    ```
    ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]
    Warming up --------------------------------------
                     tag    67.374k i/100ms
             tag_builder    59.702k i/100ms
    Calculating -------------------------------------
                     tag    670.837k (± 0.4%) i/s -      3.369M in   5.021714s
             tag_builder    592.727k (± 1.3%) i/s -      2.985M in   5.037088s
    
    Comparison:
                     tag:   670836.6 i/s
             tag_builder:   592726.7 i/s - 1.13x  slower
    ```
    
    Co-authored-by: default avatarSean Doyle <seanpdoyle@users.noreply.github.com>
Loading