Skip to content

Fix performance regression in the block-level custom CSS feature#11686

Open
mukeshpanchal27 wants to merge 5 commits intoWordPress:trunkfrom
mukeshpanchal27:perf/10777
Open

Fix performance regression in the block-level custom CSS feature#11686
mukeshpanchal27 wants to merge 5 commits intoWordPress:trunkfrom
mukeshpanchal27:perf/10777

Conversation

@mukeshpanchal27
Copy link
Copy Markdown
Member

Performance regression for #10777

By checking $block['attrs']['className'] first, it ensure that for the 90% of blocks that don't have custom CSS, the function returns in microseconds without ever triggering the preg_match.

Use of AI Tools

N/A


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

@mukeshpanchal27
Copy link
Copy Markdown
Member Author

Based on @westonruter’s findings (https://gist.github.com/westonruter/5ae4155059d2135197976f324d00645c#file-spx-claude-analysis-md), it looks like this runs for every block.

Since most blocks don’t have a custom class, we should return early so we don’t end up calling preg_match() on an empty string.

@github-actions
Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@mukeshpanchal27 mukeshpanchal27 marked this pull request as ready for review April 30, 2026 16:22
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 30, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props mukesh27, westonruter.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions
Copy link
Copy Markdown

Trac Ticket Missing

This pull request is missing a link to a Trac ticket. For a contribution to be considered, there must be a corresponding ticket in Trac.

To attach a pull request to a Trac ticket, please include the ticket's full URL in your pull request description. More information about contributing to WordPress on GitHub can be found in the Core Handbook.

@westonruter
Copy link
Copy Markdown
Member

I asked Claude Opus 4.7 to review this change with the SPX profile reports loaded as context:


Looking at the PR's actual change vs. my profile data — it's a real micro-optimization but the gain is tiny in absolute terms. Here's the math:

What the PR does

Adds an early return when $block['attrs']['className'] === '', skipping the preg_match for blocks with no className at all. The PR description says "90% of blocks don't have custom CSS" but note it's actually checking for no className — blocks with is-style-default, alignment classes, etc., still run preg_match. So the early-return likely fires on a smaller fraction than 90%, but still a majority.

What the profile said about this function

From my three runs:

Page Calls Exclusive total Per-call avg
Hello World (124 blocks) 124 32.9µs 0.27µs
Homepage v1 (934 blocks) 934 202.8µs 0.22µs
Homepage v2 (1,590 blocks) 1,590 202.9µs 0.13µs

The preg_match is the dominant cost inside the function — probably ~60% of the per-call time. Saving it for, say, 70% of calls cuts roughly half the total.

Realistic per-request savings

Page Total regression Savings from this PR % of total
Hello World 8,000µs ~10–20µs 0.1–0.3%
Homepage 1,590 blocks 23,000µs ~100–150µs ~0.5%

So on the order of 100µs per page request in the homepage case, more like 10–20µs on a small page. Well under 1% of the wp70 regression, and probably less than that in production (profiler overhead inflates per-call costs).

Verdict

It's a legitimate, zero-risk fix — six added lines, no behavior change since preg_match on '' would have returned no match and taken the same code path. But on its own it does not materially close the wp7.0-vs-6.9 gap.

The bigger lesson: the regression in this code path lives one level up. wp_render_custom_css_class_name is registered as a render_block filter — so it fires on every single block, full stop. Even with the PR, you're paying:

  • one filter dispatch per block (~150ns × 1,590 = 240µs)
  • one extra hashtable lookup ($block['attrs']['className']) per block
  • the PHP function call overhead

The structural fix would be to not register the filter at all unless there's at least one block with custom CSS support enabled (which is rare — it's an opt-in feature). A check at init to count custom-CSS-supporting block types and conditionally register the filter would eliminate ~99% of these calls entirely. That'd save the full ~200µs on the homepage rather than ~100µs.

Same pattern applies to wp_render_custom_css_support_styles, block_core_gallery_render_context, and the anchor support filters. Each PR like 11686 is a small win; the bigger wins are at the registration layer.

Worth landing this PR as-is, but I'd encourage the contributor (or you) to also propose conditional filter registration as the larger follow-up.

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