Repository navigation
Reduce Compose HTML EAP rendering overhead - #5768
Clemens Koprolin (clemenskoprolin) merged 2 commits into
Conversation
Avoid redundant composition groups in HTML wrappers, reuse CSS value conversions and reduce writer state updates. Reuse raw-text tag metadata and limit document-root and table-text validation to strict mode. Speed up JVM fractional CSS formatting while preserving rounding and notation. Use native searches and bulk copying for HTML escaping, with a lazily initialized lookup table for long strings that require escaping.
| // Walk backwards to retain fallbacks without repeatedly scanning earlier declarations. | ||
| // Empty assignments remove a property. Normal assignments reset its earlier priority. | ||
| return declarations.asReversed().filter { declaration -> | ||
| val included = BooleanArray(declarations.size) |
There was a problem hiding this comment.
How large can it be in practice? Not many I assume.
Two thoughts:
- perhaps the bits in Long (64 bits) are enough to hold the necessary amount of flags
- Otherwise, for jvm this can use
java.util.BitSetto support more than 64 values
But it's fine to keep your current implementation until we see any evidences in the profiler.
adding a comment to consider BitSet for k/jvm would be enough
There was a problem hiding this comment.
In our current test workloads, we only have 5-8 declarations. But we can't expect that to be always the case, so we'd still need a fallback with Long.
Long wasn't faster in complete SSR and reduced allocation by 0.5%. I think the simpler implementation without the fallback is worth keeping, or WDYT?
I also tried BitSet - it was about 5% slower.
There was a problem hiding this comment.
Got it. TIL: BitSet is slower than BooleanArray (at least in this case).
| if (declaration.important) append(" !important") | ||
| } | ||
| } | ||
| return buildString { |
There was a problem hiding this comment.
I see this function (toStyleAttributeValue) has 2 use cases:
- the resulting String is written into a StringBuilder
- the resulting String is used as String directly
It might make sense to have 2 overloads:
- 1: a new variant taking the StringBuilder as a parameter
- 2: same signature as now but implemented via the
1stoverload
which of the existing benchmark workloads "exercises" this function?
Here too, I think no rush to apply my suggestion. Maybe adding a comment in the code is enough.
There was a problem hiding this comment.
Especially fractional-style, but also data-table.
I quickly tried adding two overloads on fractional-style, it had 3.4% less allocation, but 2.7% more render time. It still produces an intermediate CSS String because the SSR caller subsequently HTML-escapes it. I'll add a comment to further look into it in the future.
| } | ||
|
|
||
| internal actual fun StringBuilder.appendEscapedHtmlString(value: String, attribute: Boolean): Boolean { | ||
| if (value.length < 256) return false |
There was a problem hiding this comment.
What's the reasoning behind choosing this specific number - 256 ?
Might make sense to declare a const and reuse it here and in other places.
There was a problem hiding this comment.
It's a heuristic I found using the benchmarks. The overhead of using the specialized serializer serializer for short strings seems to be too high, so we continue using the existing path paths there.
I'll make it a constant & add a short comment.
This PR further reduces rendering overhead in Compose HTML EAP string, streaming and hydrated-document rendering.
Performance changes include:
Document-root and table-text validation now run only in strict mode. Fast mode assumes valid document structure and table content.
In our benchmarks, this improves warmed subsequent renders by 35% overall, with fractional styles improving by ~50%.
Involves CMP-10836
Testing
Run from the
htmldirectory:Release Notes
N/A