-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Interactivity API: Performance bottleneck in data_wp_each_processor due to repeated template parsing
#10249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Conversation
…aching and pre-computation. This commit introduces a caching mechanism for `data-wp-each` template blueprints, improving performance by pre-computing and storing template structures. The `data_wp_each_processor` method has been refactored to utilize this cache.
|
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 Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe 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
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
Originally the patch suggested creating bookmarks at opening tags but this didn't correctly account for how the API handles opening and closing pairs and the traversal stack state. This commit revises the patch so that the blueprint is now a complete, ordered list of every tag (opening and closing) that next_tag() would visit. When we iterate through this list and seek() to each bookmark, we are replicating the original traversal. This ensures that the internal state of the processor (like its tag stack for handling nested elements) is always correct when process_directives is called. The critical optimization is still in place. The expensive part—the while loop that repeatedly scans the HTML string—is gone from the main foreach loop. It's replaced by a loop that performs a series of highly efficient seek() operations. Effectively replacing a string-scanning operation with a much faster array iteration and direct memory jump.
… ticket/64093-interactivity-api-performance-bottleneck-in-data_wp_each_processor-due-to-repeated-template-parsing
…ach` processing.
Previous attempts to optimize `WP_Interactivity_API::data_wp_each_processor` included a "pre-compute and cache" strategy. Instead of re-parsing the template on every iteration, we can build a "blueprint" of the template once, cache it, and then use it to efficiently process each item. However, this solution did not consider how the WP_HTML_Tag_Processor is designed.
The WP_HTML_Tag_Processor architecture works by:
- Sequentially scanning HTML to find tags (via strpos, strcspn, etc.)
- Tracking positions as byte offsets
- Modifying HTML through WP_HTML_Text_Replacement objects
There's no way to "jump to position X" without scanning from the beginning. Byte offsets shift as HTML is modified, and bookmarks are designed for single-pass processing, not cross-rendering reuse.
Looking more carefully at the bottleneck, the real issue is that for EACH item in the array, we:
1. Create a new WP_Interactivity_API_Directives_Processor
2. Call $p->next_tag() repeatedly (which does expensive string scanning)
3. Call get_attribute_names_with_prefix('data-wp-') for each tag
4. Parse directive names and extract values
5. Evaluate directives
The optimization opportunity is steps 2-4, not step 5. We can't avoid re-scanning the HTML (that's how the Tag Processor works), but we can cache what we learned about where directives are and what they are.
This commit introduces the `WP_Interactivity_API_Directive_Cache` class to cache pre-parsed directive information, significantly reducing the O(N×M) complexity of rendering templates with multiple items. The caching mechanism stores directive metadata, improving performance during repeated template rendering.
Additionally, the `_process_directives` method is updated to utilize this cache, enhancing efficiency. Tests are added to ensure the correctness of the caching mechanism and its integration with existing functionality.
…r an additional item in the template while retaining the template tag for client-side hydration.
…pi-directive-cache.php` to improve code cleanliness and fix code sniff errors
e183a7e to
7280cc9
Compare
|
Hello, @Michelleeby! 👋 I've been testing this PR following the steps you provided. Below are the results I got, and there doesn't seem to be much difference between them. What results do you obtain, @Michelleeby? Asking just in case I did something wrong while running the tests. 😄
|
Summary
This PR introduces a patch for this Trac ticket: https://core.trac.wordpress.org/ticket/64093. It provides a mechanism to cache what is learned from processed directives and it refactors
_process_directivesto use this cache when it exists.Trac ticket: https://core.trac.wordpress.org/ticket/64093
Patch Iterations
Version 1
This version of the patch attempted a "pre-compute and cache" strategy. However, when working through the test failures, it was found that its architecture was not going to be easily compatible with that of the
WP_HTML_Tag_Processor. Since it doesn't seem like there is a way to "jump to position X" without scanning from the beginning. Byte offsets shift as HTML is modified, and bookmarks are designed for single-pass processing, not cross-rendering reuse.Version 2
To address this issue with Version 1, Version 2 of the patch provides a mechanism to store directive metadata, rather than attempting to cache the entire template.
Running Experiments
Download, install, and activate the
interactive-stress-testplugin. When activated, the plugin creates three posts: "Stress Test – Light", "Stress Test – Medium", and "Stress Test – Heavy". To run the experiment suite, which by default runs a batch of 10 requests on each post, navigate to the frontend of the post "Stress Test – Light" and click "Run Batch Test".When prompted, click "OK" to continue to the medium post. Wait for the medium tests to finish and when prompted again, click "OK" to continue to the heavy post. When the heavy test finishes, you will be prompted to download the results as a CSV. The results can also be viewed in browser console.
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.