#62187 closed enhancement (wontfix)
WP_Theme_JSON: sanitize: Optimize schema calculation by processing only used blocks
Reported by: |
|
Owned by: |
|
---|---|---|---|
Milestone: | Priority: | normal | |
Severity: | normal | Version: | |
Component: | Themes | Keywords: | has-patch |
Focuses: | performance | Cc: |
Description
This ticket introduces a optimization to the WP_Theme_JSON::sanitize()
method to address a performance scaling issue.
Current Issue
The sanitize()
method's performance is affected by the number of registered blocks, even when those blocks are unused in the rendered content. A contributor to this inefficiency is the schema building, which scales at O(N), where N is the number of registered blocks. This schema is then built when sanitize()
is called up to 3 times for each block rendered, contributing to an overall performance impact that approaches O(M * N), where M is the number of blocks rendered.
Specifically:
- The
sanitize()
method builds a$schema
that includes data for every possible registered block, regardless of whether it's used in the input.- It loops over all valid block names, populating
$schema_settings_blocks
and$schema_styles_blocks
. - For each block, it checks for style variations and builds
$schema_styles_variations
. - This results in a large, complex schema even for unused blocks.
- It loops over all valid block names, populating
- The comprehensive schema is then used in the sanitization process when calling
static::remove_keys_not_in_schema( $input[ $subtree ], $schema[ $subtree ] );
.
- Although the
remove_keys_not_in_schema
operation itself is relatively fast (as it uses direct array key access), the overall process is slowed by the initial creation of the unnecessarily large$schema
.
- As a result, even unused blocks contribute to the processing time, leading to scaling issues.
Proposed Solution
Our optimization focuses on building only the parts of the schema that we will actually need before calling the remove_keys_not_in_schema
function. Here's how it works:
- We introduce a new
get_used_blocks()
method to identify which blocks are actually present in the input data. - We then intersect this list of used blocks with the list of valid blocks.
- The
$schema
is then built using only these valid, used blocks.
This approach significantly reduces both the time to build the $schema
array and its final size in most cases, as it only processes blocks that are actually used in the input.
Changes
- Add a new
get_used_blocks()
method to extract block names from the input. - Modify
sanitize()
to build the schema using only the intersection of valid and used blocks.
Benefits
- Improved performance scaling.
- Reduced unnecessary iterations in the sanitization process.
Expected Impact
This optimization reduces the performance penalty associated with registering many blocks, allowing for better scalability of block-based themes and plugins. Our testing methodology and results are as follows:
Testing Method:
- Environment: WordPress site using the Twenty Twenty-Four theme
- Procedure: Visited the home page 10 times for each scenario
- Measurement: Time spent in the sanitize() function
- Result: Median of the 10 measurements
Scenarios and Results:
- No plugins active (94 blocks registered):
- Before: ~4.50 ms
- After: ~2.52 ms
- Improvement: Approximately 44% faster
- With WooCommerce active (223 blocks registered):
- Before: ~7.52 ms
- After: ~3.20 ms
- Improvement: Approximately 57% faster
- With an artificial plugin registering an unusually high number of blocks (2104 blocks registered):
- Before: ~55 ms
- After: ~11.83 ms
- Improvement: Approximately 78% faster
The improvement is particularly notable for sites using plugins that register numerous custom blocks, as shown in the third scenario.
Note: Actual performance gains may vary depending on the specific WordPress setup, server configuration, and other factors.
Testing
Verify that theme JSON sanitization still works correctly for various inputs.
Benchmark performance improvements, especially for plugins that register many blocks.
I was using:
./vendor/bin/phpunit --filter Tests_Block_Template ; ./vendor/bin/phpunit --filter Tests_Interactivity_API_wpInteractivityAPIFunctions ; ./vendor/bin/phpunit --filter Tests_Theme_wpThemeJson ; ./vendor/bin/phpunit --filter WP_REST_Global_Styles_Controller_Test
But obviously all unit tests are important. :)
Performance testing should focus on measuring the time taken by the sanitize()
method under various scenarios.
This change maintains the integrity of the sanitization process while providing performance improvements, especially for complex WordPress setups with many registered blocks.
Attachments (1)
Change History (15)
This ticket was mentioned in PR #7532 on WordPress/wordpress-develop by @mreishus.
4 months ago
#1
- Keywords has-patch added
#2
@
4 months ago
PR here: https://github.com/WordPress/wordpress-develop/pull/7532
One unit test failing, will investigate
#3
@
4 months ago
- Milestone Awaiting Review deleted
- Resolution set to duplicate
- Status changed from new to closed
Thanks @mreishus. We’re already tracking this in #62126. Could you share your investigation there?
#7
@
4 months ago
- Milestone set to 6.8
- Owner set to joemcgill
- Status changed from reopened to reviewing
I'm also going to set this as something to review for the 6.8 milestone, since I think we're too close to the end of the 6.7 release cycle to make this change. Trunk is slated be open for changes related to 6.8 in 2 weeks once we branch at 6.7–RC 1.
On an initial read, the approach looks promising!
#8
@
4 months ago
- Summary changed from Optimize Theme JSON Sanitization for Better Performance Scaling to WP_Theme_JSON: sanitize: Optimize schema calculation by processing only used blocks
@mukesh27 commented on PR #7532:
3 months ago
#9
@mreishus commented on PR #7532:
3 months ago
#10
Thank you, confirmed. This patch makes the out of the box experience worse. The improvement seems to come in after changing to the twentytwentyfour theme and installing plugins like WooCommerce and Jetpack. That is, increasing the number of blocks rendered and blocks registered. Will have to think about it.
### Condition: 10.29.nightly.2025.theme
Save Name | Timestamp | p50 (ms) | Number of Requests |
---|---|---|---|
trunk | 2024-10-29 16:53:36 | 37.12 | 250 |
with-patch | 2024-10-29 16:55:33 | 41.01 | 250 |
### Condition: 10.29.nightly.2024.theme
Save Name | Timestamp | p50 (ms) | Number of Requests |
---|---|---|---|
trunk | 2024-10-29 16:57:15 | 50.41 | 250 |
patch | 2024-10-29 16:57:41 | 49.80 | 250 |
### Condition: 10.29.nightly.2024.theme.with.woocommerce
Save Name | Timestamp | p50 (ms) | Number of Requests |
---|---|---|---|
trunk | 2024-10-29 17:01:06 | 115.02 | 250 |
patch | 2024-10-29 17:01:53 | 112.53 | 250 |
### Condition: 10.29.nightly.2024.theme.with.woocommerce.and.jetpack
Save Name | Timestamp | p50 (ms) | Number of Requests |
---|---|---|---|
trunk | 2024-10-29 17:16:57 | 133.73 | 250 |
patch | 2024-10-29 17:17:45 | 123.37 | 250 |
@mreishus commented on PR #7532:
3 months ago
#11
Closing this PR after reviewing performance data. While the patch improves performance for complex installations with many registered blocks (e.g. WooCommerce + Jetpack), it introduces overhead in the default WordPress configuration. Since core needs to optimize primarily for the common case, this approach isn't the right solution. Will explore alternative optimizations that don't negatively impact the default installation.
Trac ticket: https://core.trac.wordpress.org/ticket/62187