From 34dc8f251beb825c1a44b85779647e1edca93f2a Mon Sep 17 00:00:00 2001 From: Geremia Taglialatela Date: Sun, 8 Sep 2024 15:48:36 +0200 Subject: [PATCH] Optimize string concatenation using `<<` operator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change enhances the efficiency of HTML string construction, by replacing `+=` operator with `<<`. The shovel operator (`<<`) modifies strings in-place, reducing memory allocation and improving overall performance compared to the `+=` operator. ### Test Case Ruby 3.3.5 x64 on M1 Pro ```rb Arbre::Context.new { div { |d| d.ul { li } } }.to_s ``` #### Before ``` --- Memory Profiler --- Total allocated: 4624 bytes (80 objects) Total retained: 0 bytes (0 objects) allocated memory by gem ----------------------------------- 4624 arbre/lib allocated memory by file ----------------------------------- 2560 arbre/html/tag.rb 1120 arbre/element/builder_methods.rb 400 arbre/element_collection.rb 280 arbre/element.rb 264 arbre/context.rb --- Benchmark: IPS --- test 50.546k (± 1.4%) i/s - 254.550k in 5.037005s --- Benchmark: Memory --- test 6.208k memsize ( 0.000 retained) 99.000 objects ( 0.000 retained) 29.000 strings ( 0.000 retained) ``` #### After ``` --- Memory Profiler --- Total allocated: 3840 bytes (64 objects) Total retained: 0 bytes (0 objects) allocated memory by gem ----------------------------------- 3840 arbre/lib allocated memory by file ----------------------------------- 1776 arbre/html/tag.rb 1120 arbre/element/builder_methods.rb 400 arbre/element_collection.rb 280 arbre/element.rb 264 arbre/context.rb --- Benchmark: IPS --- test 52.205k (± 1.6%) i/s - 265.659k in 5.090028s --- Benchmark: Memory --- test 5.424k memsize ( 0.000 retained) 83.000 objects ( 0.000 retained) 19.000 strings ( 0.000 retained) ``` --- lib/arbre/html/tag.rb | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/arbre/html/tag.rb b/lib/arbre/html/tag.rb index e7272a9..d6f958f 100644 --- a/lib/arbre/html/tag.rb +++ b/lib/arbre/html/tag.rb @@ -116,25 +116,23 @@ def closing_tag def indent(open_tag, child_content, close_tag) spaces = ' ' * indent_level * INDENT_SIZE - html = "" + html = +"" if no_child? || child_is_text? if self_closing_tag? - html += spaces + open_tag.sub( />$/, '/>' ) + html << spaces << open_tag.sub( />$/, '/>' ) else # one line - html += spaces + open_tag + child_content + close_tag + html << spaces << open_tag << child_content << close_tag end else # multiple lines - html += spaces + open_tag + "\n" - html += child_content # the child takes care of its own spaces - html += spaces + close_tag + html << spaces << open_tag << "\n" + html << child_content # the child takes care of its own spaces + html << spaces << close_tag end - html += "\n" - - html + html << "\n" end def self_closing_tag?