Skip to content

Commit

Permalink
Improve performance by using bytesize when comparing if the string ch…
Browse files Browse the repository at this point in the history
…anged (#690)

`length` slows down as the string grows (appears to be O(n)). `bytesize`
stays the same O(1). Even on very small strings like `"<h1>"`,
`bytesize` is much faster than `length`. By swapping over to `bytesize`
we can see an overall improvement in Phlex bench.rb:

**Before (using length):**

```
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23]
Warming up --------------------------------------
                Page    13.160k i/100ms
Calculating -------------------------------------
                Page    133.259k (± 0.9%) i/s -    671.160k in   5.036897s
```

**After (using bytesize):**

```
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23]
Warming up --------------------------------------
                Page    15.000k i/100ms
Calculating -------------------------------------
                Page    154.814k (± 1.2%) i/s -    780.000k in   5.039022s
```

If we further tune `bench.rb` as I've done in this PR to more accurately
represent what might be a typical "string length" when using Phlex
(think larger components or full page views), we see an even more
dramatic improvement:

**Before (using length on larger string):**

```
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23]
Warming up --------------------------------------
                Page    73.000 i/100ms
Calculating -------------------------------------
                Page    734.131 (± 0.4%) i/s -      3.723k in   5.071408s
```

**After (using bytesize on larger string):**

```
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23]
Warming up --------------------------------------
                Page   520.000 i/100ms
Calculating -------------------------------------
                Page      5.243k (± 0.6%) i/s -     26.520k in   5.058093s
```

Comes to about a 7x speed increase. Always faster, with potential for
huge gains. I had a page go from 15 seconds to 300ms with this change
(50x speedup). This was a rare page of over 3 megabytes of HTML all
generated by Phlex, but still pretty fun.
  • Loading branch information
davekaro committed Mar 22, 2024
1 parent df0e82d commit db07a93
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 22 deletions.
34 changes: 18 additions & 16 deletions fixtures/page.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,28 @@ def view_template
render LayoutComponent.new do
h1 { "Hi" }

table id: "test", class: "a b c d e f g" do
tr do
td id: "test", class: "a b c d e f g" do
span { "Hi" }
end
100.times do
table id: "test", class: "a b c d e f g" do
tr do
td id: "test", class: "a b c d e f g" do
span { "Hi" }
end

td id: "test", class: "a b c d e f g" do
span { "Hi" }
end
td id: "test", class: "a b c d e f g" do
span { "Hi" }
end

td id: "test", class: "a b c d e f g" do
span { "Hi" }
end
td id: "test", class: "a b c d e f g" do
span { "Hi" }
end

td id: "test", class: "a b c d e f g" do
span { "Hi" }
end
td id: "test", class: "a b c d e f g" do
span { "Hi" }
end

td id: "test", class: "a b c d e f g" do
span { "Hi" }
td id: "test", class: "a b c d e f g" do
span { "Hi" }
end
end
end
end
Expand Down
12 changes: 6 additions & 6 deletions lib/phlex/sgml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -335,9 +335,9 @@ def yield_content

buffer = @_context.buffer

original_length = buffer.length
original_length = buffer.bytesize
content = yield(self)
__text__(content) if original_length == buffer.length
__text__(content) if original_length == buffer.bytesize

nil
end
Expand All @@ -349,9 +349,9 @@ def yield_content_with_no_args

buffer = @_context.buffer

original_length = buffer.length
original_length = buffer.bytesize
content = yield
__text__(content) if original_length == buffer.length
__text__(content) if original_length == buffer.bytesize

nil
end
Expand All @@ -364,9 +364,9 @@ def yield_content_with_args(*args)

buffer = @_context.buffer

original_length = buffer.length
original_length = buffer.bytesize
content = yield(*args)
__text__(content) if original_length == buffer.length
__text__(content) if original_length == buffer.bytesize

nil
end
Expand Down

0 comments on commit db07a93

Please sign in to comment.