Skip to content

Optimize Compose HTML EAP SSR rendering - #5756

Open
Clemens Koprolin (clemenskoprolin) wants to merge 8 commits into
masterfrom
clemenskoprolin/compose-html-ssr-performance
Open

Clemens Koprolin (clemenskoprolin) wants to merge 8 commits into
masterfrom
clemenskoprolin/compose-html-ssr-performance

Conversation

@clemenskoprolin

Copy link
Copy Markdown
Collaborator

This PR reduces rendering overhead in Compose HTML EAP string, streaming and hydrated-document rendering.

Performance changes include:

  • Tag metadata: reuse builders and their precomputed tag flags.
  • Attribute names: cache validated names in bounded renderer storage.
  • Composition locals: cache static rendering locals and avoid a temporary provider map.
  • CSS serialization: write declarations directly and convert common values to strings once.
  • Raw-text content: emit validated content directly without rebuilding it.
  • JVM text escaping: use native string searches and bulk copying for longer text.
  • Start-tag results: reuse an immutable result for ordinary tags, keeping allocation predictable.
  • Snapshots: render in the caller's snapshot instead of creating and disposing one per render.

Rendering now uses the caller’s current snapshot. State changes from content and synchronous effects remain there. Callers requiring consistent reads and isolated writes should enter a mutable snapshot.

In our benchmarks, this reduced the first render 13.3%, subsequent renders by 27.7% and reduced the allocated heap by 19.6%.

Involves CMP-10836

Testing

Run from the html directory:

./gradlew -Pcompose.html.eap.enabled=true \
  :html-core-eap:jvmTest \
  :html-core-eap:jsBrowserTest \
  :html-core-eap:wasmJsBrowserTest \
  :html-svg-eap:jvmTest \
  :html-svg-eap:jsBrowserTest \
  :html-svg-eap:wasmJsBrowserTest

Release Notes

N/A

require(
name.isNotEmpty() &&
name.none { it.isISOControl() || it in InvalidHtmlAttributeNameCharacters }
name.none {

@eymar Oleksandr Karpovich (eymar) Oct 15, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change compromises readability for better performance.
Also, I see there is a cache - HtmlAttributeNameCache.

Is not the HtmlAttributeNameCache the more important change performance-wise?
Will it matter if we keep this function more readable but less optimized?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I'll benchmark that specific optimization.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You’re right: the cache is the more important optimization here, I'll restore it to be more readable.

requireValidHtmlTagName(tagName)
tagNameIsValid: Boolean,
): StartTagResult {
if (!tagNameIsValid) requireValidHtmlTagName(tagName)

@eymar Oleksandr Karpovich (eymar) Oct 15, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here too, should this be skipped when validate is false?

Also, below: requireHtmlParserStableTableElement ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants