Make WordPress Core

Opened 4 months ago

Closed 3 months ago

Last modified 3 months ago

#62187 closed enhancement (wontfix)

WP_Theme_JSON: sanitize: Optimize schema calculation by processing only used blocks

Reported by: mreishus's profile mreishus Owned by: joemcgill's profile joemcgill
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:

  1. 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.
  1. The comprehensive schema is then used in the sanitization process when calling static::remove_keys_not_in_schema( $input[ $subtree ], $schema[ $subtree ] );.
  1. 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.
  1. 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:

  1. We introduce a new get_used_blocks() method to identify which blocks are actually present in the input data.
  2. We then intersect this list of used blocks with the list of valid blocks.
  3. 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

  1. Add a new get_used_blocks() method to extract block names from the input.
  2. 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:

  1. No plugins active (94 blocks registered):
    • Before: ~4.50 ms
    • After: ~2.52 ms
    • Improvement: Approximately 44% faster
  1. With WooCommerce active (223 blocks registered):
    • Before: ~7.52 ms
    • After: ~3.20 ms
    • Improvement: Approximately 57% faster
  1. 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)

optimize-sanitize-schema-v1.diff (2.6 KB) - added by mreishus 4 months ago.

Download all attachments as: .zip

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 @mreishus
4 months ago

PR here: https://github.com/WordPress/wordpress-develop/pull/7532

One unit test failing, will investigate

#3 @joemcgill
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?

#4 follow-up: @mreishus
4 months ago

I don't consider this a duplicate of #62126; even if the memoization in #62126 was implemented, the changes here would still speed up the first call to sanitize().

#5 in reply to: ↑ 4 @joemcgill
4 months ago

Replying to mreishus:

I don't consider this a duplicate of #62126; even if the memoization in #62126 was implemented, the changes here would still speed up the first call to sanitize().

That's a fair point. I'll reopen and we can keep these two closely related efforts separate.

#6 @joemcgill
4 months ago

  • Resolution duplicate deleted
  • Status changed from closed to reopened

#7 @joemcgill
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 @mreishus
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

I did XHG profiling and in my test it shows the below result in which it shows some function call increased instead of improvement

https://github.com/user-attachments/assets/803a235f-2df6-4b96-a728-cd54a8b21cd5

@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.

#12 @mreishus
3 months ago

  • Resolution set to wontfix
  • Status changed from reviewing to closed

This ticket was mentioned in Slack in #core-performance by mukeshpanchal27. View the logs.


3 months ago

#14 @mukesh27
3 months ago

  • Milestone 6.8 deleted
Note: See TracTickets for help on using tickets.