WordPress 6.4 Performance Improvements

WordPress 6.4 is the third major releasemajor release A release, identified by the first two numbers (3.6), which is the focus of a full release cycle and feature development. WordPress uses decimaling count for major release versions, so 2.8, 2.9, 3.0, and 3.1 are sequential and comparable in scope. of the year that delivers a better user experience to site visitors by improving performance of the coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. software. This version improves server response time by ~4% over version 6.3.2. This, combined with additional frontend optimizations, leads to improvements to Largest Contentful Paint (LCP)—an important Core Web Vital. For example, Twenty Twenty-One (a classic theme) shows an improvement of ~4% for LCP and Twenty Twenty-Three (a blockBlock Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. theme) shows a ~9% LCP improvement when run on WordPress 6.4 compared with 6.3.2 based on benchmarks conducted by the WordPress Performance team.

This release caps off an ambitious year of work for the Performance Team, with major improvements made in each release. You can read more about those improvements in the performance improvement summary posts for version 6.2 and version 6.3. While version 6.2 focused on server side improvements, and 6.3 focused more on client side improvements, this release was focused on continuing server side improvements while further optimizing and extending the improvements that were made earlier in the year. Meanwhile, the team continued iterating on the tools and methodologies used to measure and report on performance.

Performance highlights for this release

An overview of the performance enhancements in this release is included in the WordPress 6.4 Field Guide and you can read details about the following highlights in their individual dev-note posts:

Server side improvements

Client side improvements


Evolving our benchmarking methodology

Previous performance benchmarks reported for each release were taken by @flixos90 (the performance release leadRelease Lead The community member ultimately responsible for the Release.) on his own computer, using a standardized set of best practices and tools. This release we have attempted to automate the process in order to establish a reproducible methodology that can be used by any contributor using a standard set of tools. 

The process for this release uses the compare-wp-performance GitHubGitHub GitHub is a website that offers online implementation of git repositories that can easily be shared, copied and modified by other developers. Public repositories are free to host, private repositories require a paid subscription. GitHub introduced the concept of the ‘pull request’ where code changes done in branches by contributors can be reviewed and discussed before being merged be the repository owner. https://github.com/ action, originally developed by @swissspidy, which sets up two standard test sites using wp-env inside of a GitHub worker, takes a set of metrics based on 100 requests, and reports them in the action summary (example). To account for variance between each run, this is done 5 times and the median values are used for reporting purposes.

Revisiting previous releases

Since the testing environment has an important impact on performance benchmarks, we wanted to revisit the previous versions from this year and apply the same methodology for comparison. Each of the following metrics show the percentage the metric either improved (negative numbers) or got worse (positive numbers) between versions.

WordPress 6.2.3 vs 6.1.4

Twenty Twenty-One 

  • LCP: +2.87%
  • Server Timing -4.21%

Twenty Twenty-Three

  • LCP: -4.72%
  • Server Timing -18.15%

WordPress 6.3.2 vs 6.2.3

Twenty Twenty-One 

  • LCP: -10.82%
  • Server Timing -3.34%

Twenty Twenty-Three

  • LCP: -14.23%
  • Server Timing -10.21%

WordPress 6.4.0 vs 6.3.2

Twenty Twenty-One 

  • LCP: -3.95%
  • Server Timing -4.05%

Twenty Twenty-Three

  • LCP: -9.06%
  • Server Timing -4.59%

Detailed data can be found in this spreadsheet, with links to the individual GitHub workflows.

Future improvements

While the methodology used for this release is an improvement over the previous process, there is much room for improvement, including finding ways to stabilize the metrics collected during benchmarks for releases and during development, improving the test content and use cases we test for these benchmarks, testing different configurations and environment characteristics (e.g., PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher versions, persistent object cache, etc.).

Props

Props to @joemcgill for taking the time to write this extensive and detailed post, and @flixos90 for review.

#6-4, #core, #core-performance, #performance

Merging Performant Translations into Core

The coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. performance team spent a lot of time this year looking into the performance of the i18ni18n Internationalization, or the act of writing and preparing code to be fully translatable into other languages. Also see localization. Often written with a lowercase i so it is not confused with a lowercase L or the numeral 1. Often an acquired skill./l10nL10n Localization, or the act of translating code into one's own language. Also see internationalization. Often written with an uppercase L so it is not confused with the capital letter i or the numeral 1. WordPress has a capable and dynamic group of polyglots who take WordPress to more than 70 different locales. system in WordPress, after proving that loading translations had a significant hit on response time. This led to an in-depth performance analysis, followed by a dedicated Performant Translations feature pluginFeature Plugin A plugin that was created with the intention of eventually being proposed for inclusion in WordPress Core. See Features as Plugins. offering significant performance boosts for all WordPress sites with zero configuration. Thousands of sites successfully tested the pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party under a wide variety of conditions. Now, the team believes the solution is ready for inclusion in WordPress core.

What it does

Performant Translations is powered by a new, lightweight i18n library that is faster at loading binary MO files and uses less memory. It even supports loading multiple locales at the same time, which makes locale switching faster. In addition to that, it supports translations contained in PHPPHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 5.6.20 or higher files, avoiding a binary file format and leveraging OPCache if available. If an MO translationtranslation The process (or result) of changing text, words, and display formatting to support another language. Also see localization, internationalization. file has a corresponding PHP file, the latter will be loaded instead, making things even faster and use even less memory. In raw numbers, this is how great the optimization is with this approach:

LocaleLocale A locale is a combination of language and regional dialect. Usually locales correspond to countries, as is the case with Portuguese (Portugal) and Portuguese (Brazil). Other examples of locales include Canadian English and U.S. English.ScenarioMemory UsageLoad Time
en_USDefault15 MB159 ms
de_DEDefault29 MB217 ms
de_DEPerformant Translations17 MB166 ms

These numbers were taken by testing the Performant Translations plugin against WordPress 6.5-alpha-57028 with the Twenty Twenty-One theme and a few active plugins. As you can see, memory usage and load time overhead are reduced to a minimum.

Next steps

The core performance team has opened #59656 to track merging Performant Translations into core in time for the next 6.5 release. A pull request is already available and currently undergoing code review. Once that is completed, it will be ready to be merged into trunktrunk A directory in Subversion containing the latest development code in preparation for the next major release cycle. If you are running "trunk", then you are on the latest revision. where the library will be able to see even wider testing.

There is also a Meta patch ready for serving PHP files as part of language packs shipped by translate.wordpress.org, building upon a GlotPress PR. Both are in need of code review right now. While these two changes unlock the full power of Performant Translations, they are not blockers for the core merge and could even land later.

The Performant Translations plugin will continue to be maintained even after a core merge to build on top of the core solution with a distinct additional feature. As is already the case today, the plugin will automatically convert any MO files to PHP files if a PHP file does not currently exist. This is useful for sites where translations are not coming from translate.wordpress.org or only exist locally on that server.


Thank you to @mukesh27, @westonruter for reviewing and helping with this post.

#core, #i18n, #performance

Performance Chat Summary: 7 November 2023

Meeting agenda here and the full chat log is available beginning here on Slack.

Announcements

Priority Projects

Server Response Time

Link to roadmap projects and link to the GitHub project board

Contributors: @joemcgill @swissspidy @thekt12 @mukesh27 @pereirinha

  • @thekt12 working on #59314 Research showed that there is very little benefit going ahead with file_exists caching and it’s not easy to cache locate_block_template()
    • @joemcgill I think we can probably close #59314 as a wontfix as well, but was waiting to see what the results were of the research you were doing into replacing all of the file_exists checks in coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. with your PoC. Did you ever run any numbers for that?
    • @thekt12 ran into issues while doing it so stopped in between. Will try that again this week
    • @joemcgill Sounds good. I think I’ll close that ticketticket Created for both bug reports and feature development on the bug tracker. regardless, and if you notice that it’s worth attempting to replace the native file_exists implementation then we can open a new ticket for that.
  • @thekt12 also reviewing #45601
  • @mukesh27 For #59315 I ran function benchmark, which indicated a ~3% performance improvement for the function. Given this result, we recommend closing this ticket with a wontfix resolution
  • @swissspidy Regarding i18ni18n Internationalization, or the act of writing and preparing code to be fully translatable into other languages. Also see localization. Often written with a lowercase i so it is not confused with a lowercase L or the numeral 1. Often an acquired skill. performance, still planning a blogblog (versus network, site) post after 6.4 is out

Database Optimization

Link to roadmap projects and link to the GitHub project board

Contributors: @mukesh27 @thekt12

  • @mukesh27 for #56912 we are waiting for @spacedmonkey feedback
    • @spacedmonkey suggest to keep it open and puntpunt Contributors sometimes use the verb "punt" when talking about a ticket. This means it is being pushed out to a future release. This typically occurs for lower priority tickets near the end of the release cycle that don't "make the cut." In this is colloquial usage of the word, it means to delay or equivocate. (It also describes a play in American football where a team essentially passes up on an opportunity, hoping to put themselves in a better position later to try again.) to a future release
    • @flixos90 I’m not sure about the site_icon option. I reviewed that when I looked at the ticket recently, and it’s autoloaded as far as I can tell. That’s why I am thinking there is no outstanding work to do
  • @thekt12 This was already ready during 6.4, I feel this is ready to be merged for 6.5 given a second review.
     https://github.com/WordPress/wordpress-develop/pull/5295 It’s a small bugbug A bug is an error or unexpected result. Performance improvements, code optimization, and are considered enhancements, not defects. After feature freeze, only bugs are dealt with, with regressions (adverse changes from the previous version) being the highest priority. fix
    • @joemcgill I can give it a fresh look and commit it if it looks good to go

JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. & CSSCSS Cascading Style Sheets.

Link to roadmap project and link to the GitHub project board

Contributors: @mukesh27 @10upsimon @westonruter

  • @joemcgill The project board still has this leftover effort for async/defer, which includes #59301. I assume that’s not something actively being worked on?

Images

Link to roadmap projects and link to the GitHub project board

Contributors: @flixos90 @thekt12 @adamsilverstein @joemcgill @pereirinha @westonruter

  • @westonruter For Image Loading Optimization (overview issue), the initial pull request for LCP and lazy-loading detection (i.e. page metrics) has been merged into the feature branchbranch A directory in Subversion. WordPress uses branches to store the latest development code for each major release (3.9, 4.0, etc.). Branches are then updated with code for any minor releases of that branch. Sometimes, a major version of WordPress and its minor versions are collectively referred to as a "branch", such as "the 4.0 branch". (feature/image-loading-optimization). I’m working on the next pull request for page metrics storage which I hope to have ready for review by EOD today. With detection and storage in place, I’ll move on to applying them to actually apply the optimizations on pages.
    • @joemcgill One question I had about 878 is why you decided to register a separate post type for the storage, rather than post metaMeta Meta is a term that refers to the inside workings of a group. For us, this is the team that works on internal WordPress sites like WordCamp Central and Make WordPress.? Just curious, really.
    • @westonruter Good question. The reason is that URLs needing to be optimized are more than just singular posts, where postmeta is not available. It’s similar to oEmbed Caches in core, where we have a custom post typeCustom Post Type WordPress can hold and display many different types of content. A single item of such a content is generally called a post, although post is also a specific post type. Custom Post Types gives your site the ability to have templated posts, to simplify the concept. for that as well
  • @joemcgill On a separate topic, I’ve started some initial research into ways we might be able to improve the accuracy of the algorithm we use to determine the sizes attribute that core adds to images. This could be an important optimization if we can be more accurate and potentially avoid serving larger than necessary images to site visitors.
  • @westonruter On the previous topic…which relates to this, the detection logic is capturing the bounding rect for each image in the viewport, so this could feed into the sizes calculation.
    • @joemcgill Oh sure…if we could do front-end calculation of the image dimensions, the sizes attribute could be much more accurate.
  • @pbearne Added Backgrounds to images got a plug by Kevin Powell https://www.youtube.com/watch?v=345V2MU3E_w&ab_channel=KevinPowell

Measurement

Link to roadmap projects and link to the GitHub project board

Contributors: @adamsilverstein @joemcgill @mukesh27 @swissspidy @flixos90

  • @swissspidy My intro blog post about performance testing should be ready soon, just needs some final polish
  • @swissspidy Opened a PR to expand core performance tests to cover localized sites, wp-adminadmin (and super admin), and memory usage
  • @adamsilverstein One small update from Drupal land where they have been working on a similar effort to add automated performance testing… They recently landed their first test collection piece in core with data going to Grafana – https://www.drupal.org/project/drupal/issues/3391689
  • @flixos90 I am looking at metrics (mostly field) a lot these days, for visibility I am resharing the TTFB impact analysis I conducted: https://wordpress.slack.com/archives/C02KGN5K076/p1698947714789279
    It begs the question how much WordPress core is in a position to affect TTFB in the field, vs how much TTFB is impacted by other factors outside of our control (e.g. hosting stack, networknetwork (versus site, blog) connection etc.)
  • @joemcgill I’ve spent a lot of time over the past week getting into the weeds of how we’ve been benchmarking WP versions, in preparation for the 6.4 release. It’s been a helpful exploration and we’ve made improvements to the https://github.com/swissspidy/compare-wp-performance workflow and added the ability to add variance statistics to the benchmarks taken by our benchmark CLICLI Command Line Interface. Terminal (Bash) in Mac, Command Prompt in Windows, or WP-CLI for WordPress. tools. I’ll write up some more details and add them to the conversation in https://github.com/WordPress/performance/issues/849
  • @flixos90 I am also researching a couple other things related to our team’s potential impact on CWV through LCP, e.g. given we have mostly spent time on improving LCP, how much would that allow us to improve overall CWV passing rate of WordPress sites? And the same for TTFB (which, spoiler alert, seems to be a major bottleneck for TTFB)
    • @flixos90 I am going to prepare some of those stats to be a bit more presentable, but from what I am seeing so far, it seems that improving TTFB by let’s say 40% (which obviously is a very ambitious goal) would improve the actual LCP passing rate from the current ~40% to ~60%, which would be huge. Note that ~60% is also the current LCP passing rate of all non-WordPress sites in the HTTPHTTP HTTP is an acronym for Hyper Text Transfer Protocol. HTTP is the underlying protocol used by the World Wide Web and this protocol defines how messages are formatted and transmitted, and what actions Web servers and browsers should take in response to various commands. Archive / CrUX dataset. So is TTFB our main issue in WordPress?
    • @flixos90 It would certainly make sense, since TTFB is the metric with the lowest passing rate for WordPress sites. So this probably isn’t even news, but I think it’s a good other angle to look at the situation from. Improving TTFB would have major impact on LCP, and that would then have major impact on CWV. But easier said than done

Ecosystem Tools

Link to roadmap projects and link to the GitHub repo

Contributors: @mukesh27 @swissspidy @westonruter

Creating Standalone Plugins

Link to GitHub overview issue

Contributors: @flixos90 @mukesh27 @10upsimon

Open Floor

  • @pbearne I was  on a client site last week and noticed that there was a load of get_options calls to missing options each taking 0.002. I wonder if we could work out a way to return early
    • @joemcgill Do these not get picked up by the not_options cache? Or did that not apply in this case?
    • @pbearne didn’t seem to be so, filled up the queries
    • @joemcgill get_option should add any not found option to the cache here, unless there is a bug that causes it to get skipped. If the site isn’t using a persistent object cache and it’s only called once, then that optimization won’t really matter, but otherwise it should.
  • @dmsnell I started working with someone to work on running the GutenbergGutenberg The Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/ E2E suite against the Playground with Core’s trunk. My schedule was a bit off because of a meetupMeetup All local/regional gatherings that are officially a part of the WordPress world but are not WordCamps are organized through https://www.meetup.com/. A meetup is typically a chance for local WordPress users to get together and share new ideas and seek help from one another. Searching for ‘WordPress’ on meetup.com will help you find options in your area. we had a couple weeks ago and a small vacation I took afterwards.

Our next chat will be held on Tuesday, November 14, 2023 at 16:00 UTC in the #core-performance channel in Slack.

#core-performance, #performance, #performance-chat, #summary

Performance Chat Agenda: 7 November 2023

Here is the agenda for this week’s performance team meeting scheduled for Nov 7, 2023 at 16:00 UTC. If you have any topics you’d like to add to this agenda, please add them in the comments below.


This meeting happens in the #core-performance channel. To join the meeting, you’ll need an account on the Make WordPress Slack.

#agenda, #meeting, #performance, #performance-chat

Performance Chat Summary: 31 October 2023

Meeting agenda here and the full chat log is available beginning here on Slack.

Announcements

  • Welcome to our new members of #core-performance
  • WordPress 6.4 Release Candidaterelease candidate One of the final stages in the version release cycle, this version signals the potential to be a final release to the public. Also see alpha (beta). 3 is tomorrow
  • The Performance Hallway Hangout blogblog (versus network, site) post has been updated with a recording of the session

Priority Projects

Server Response Time

Link to roadmap projects and link to the GitHub project board

Contributors: @joemcgill @swissspidy @thekt12 @mukesh27 @pereirinha

  • @joemcgill One of the focuses for 6.5 will looking at several tickets related to improving template loading: https://github.com/WordPress/performance/issues/746
  • @thekt12 As a part of 746, – Created a PoC to test out if persistent cache better than default function for checking files_exists #59314 ( PR#5597 ).
    Need to get some numbers for that
  • @joemcgill As we’ve talked about this exploration, one of the things that came up was the need to come up with a solid approach for cache invalidation, if we do find that the effort is worth pushing forward. Somewhat related, we have #59719 that would be great to get feedback on from anyone interested
  • @pereirinha as for #59595 there’s already a PR and some feedback to look into. The proposed solution is to use object caching. The CPU impact and the wall time are greatly improved, but there’s the memory cost. I’ll be circling back on the feedback shortly

Database Optimization

Link to roadmap projects and link to the GitHub project board

Contributors: @mukesh27 @thekt12

  • @mukesh27 I’ve been working on #56912, but it appears that this ticketticket Created for both bug reports and feature development on the bug tracker. is no longer valid. Some options have been autoloaded in WordPress 6.3, while the remaining options are not loaded in WordPress. Therefore, we should proceed to close this ticket. We are awaiting feedback from Jonny before finalizing the decision.
    • @joemcgill We can give it a couple days to see if @spacedmonkey has anything to add, but think it’s ok to close if you and @flixos90 agree, since Jonny’s been away
  • @brianhenryie On the templates caching, I see it’s using file_exists() but elsewhere in WP I see is_readable() which is more correct, and presumably slightly slower.
    • @joemcgill I think the current implementation is simply for testing purposes, but would you mind adding a comment to the ticket so we remember to review that?

JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. & CSSCSS Cascading Style Sheets.

Link to roadmap project and link to the GitHub project board

Contributors: @mukesh27 @10upsimon @westonruter

  • No updates this week

Images

Link to roadmap projects and link to the GitHub project board

Contributors: @flixos90 @thekt12 @adamsilverstein @joemcgill @pereirinha @westonruter

  • @westonruter I’ve got an initial PR open to introduce an “Image Loading Optimization” module for the Performance Lab pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party. In addition to having scaffolding for the module, this PR also introdues output buffering for template renders as well as a filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. manipulating the output buffer prior to flushing. Reviews for this PR would be appreciated so it can be merged. This will then be used in subsequent PRs for the module to optimize the loading of images (and other things). For example, I have an early draft PR for the detection logic to determine which image is LCP and which images should not be lazy-loaded. See overview issue.

Measurement

Link to roadmap projects and link to the GitHub project board

Contributors: @adamsilverstein @joemcgill @mukesh27 @swissspidy @flixos90

  • @swissspidy For the metrics stabilization exploration I did share some preliminary findings the other day based on my testing. I’m a bit out of options right now as I don’t think I’ve found a magic bullet that just eliminates any variance, so I’d appreciate some eyes there.
  • @joemcgill Related to measurement, I reported here yesterday some observations I made about the automated benchmarking workflow we’ve been experimenting with during this release. Discovering that running benchmarks on the two themes sequentially, was causing the classic theme results to be skewed. I’ve opened a PR on @swissspidy‘s repo that allows you to choose which theme you want to test, as one approach to avoiding that issue.

Ecosystem Tools

Link to roadmap projects and link to the GitHub repo

Contributors: @mukesh27 @swissspidy @westonruter

Creating Standalone Plugins

Link to GitHub overview issue

Contributors: @flixos90 @mukesh27 @10upsimon

Open Floor

  • n/a

Our next chat will be held on Tuesday, November 7, 2023 at 16:00 UTC in the #core-performance channel in Slack.

#core-performance, #performance, #performance-chat, #summary

Performance Chat Agenda: 31 October 2023

Here is the agenda for this week’s performance team meeting scheduled for Oct 31, 2023 at 16:00 UTC. If you have any topics you’d like to add to this agenda, please add them in the comments below.


This meeting happens in the #core-performance channel. To join the meeting, you’ll need an account on the Make WordPress Slack.

#agenda, #meeting, #performance, #performance-chat

Performance Chat Summary: 24 October 2023

No agenda was posted prior to this meeting. The full chat log is available beginning here on Slack.

Announcements

  • We will shift the time of this meeting starting next week, October 31 at 16:00 UTC, to account for seasonal time changes starting next week in Europe.
  • Today is WordPress RCrelease candidate One of the final stages in the version release cycle, this version signals the potential to be a final release to the public. Also see alpha (beta). 2. Everything in the performance focus for this release has been resolved. Thanks to everyone who contributed to the 67 resolved performance focus tickets in this release 🎉
  • The fortnightly bugbug A bug is an error or unexpected result. Performance improvements, code optimization, and are considered enhancements, not defects. After feature freeze, only bugs are dealt with, with regressions (adverse changes from the previous version) being the highest priority. scrub is scheduled for tomorrow at 15:00 UTC in #core-performance in SlackSlack Slack is a Collaborative Group Chat Platform https://slack.com/. The WordPress community has its own Slack Channel at https://make.wordpress.org/chat/..
    • We plan to review 6.5 tickets. If anyone has things they plan to work on during this release, please make sure they are added to the milestone in advance of that meeting.

Priority Projects

Server Response Time

Link to roadmap projects

Contributors: @joemcgill @swissspidy @thekt12 @mukesh27

  • @swissspidy is drafting a blogblog (versus network, site) post covering next steps for merging the Performant Translations pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party (#59656)
  • @joemcgill plans to review work related to improving Template Loading (see: https://github.com/WordPress/performance/issues/746) for 6.5

Database Optimization

Link to roadmap projects

Contributors: @mukesh27 @thekt12

JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. & CSSCSS Cascading Style Sheets.

Link to roadmap project

Contributors: @mukesh27 @10upsimon @westonruter

  • @westonruter No updates for script loading strategies, except that WP 6.4 will implement them on the frontend.
  • @pereirinha I’ve been doing research on #59595. Was supposed to do an update last week, but I didn’t like the results. Today, he returned to it and is much happier with the direction. He plans to update the ticketticket Created for both bug reports and feature development on the bug tracker. before EOD. There will be findings and potential solution.

Images

Link to roadmap projects

Contributors: @flixos90 @thekt12 @adamsilverstein @joemcgill @pereirinha

  • @westonruter Just opened an overview issue to implement “Image Loading Optimization via Client-side Detection”
    • “For the past few weeks, I’ve been working on a document to outline the requirements and a proposed solution”
    • There is also a discussion issue from a year and a half ago that has a good bit of background.

Measurement

Link to roadmap projects

Contributors: @adamsilverstein @joemcgill @mukesh27 @swissspidy @flixos90

  • @joemcgill We had a lively discussion last week during the hallway hangout about this topic.
  • @flixos90 “Hard to summarize, but TLDR I think we are still trying to figure out ways to get more consistent (as in “less variance”) benchmarks within a single environment. While we apparently saw some more consistent metrics in the compare-wp-performance workflow that @swissspidy implemented, there is still high variance, and even the same WordPress versions may produce vastly different numbers. There’s a good chance that this is simply because GH Actions workers aren’t suitable for this kind of testing as they vary in their environment”
  • @flixos90 – I’ve been working on a simple PR to support detecting the kind of content for a WordPress URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org in HTTPHTTP HTTP is an acronym for Hyper Text Transfer Protocol. HTTP is the underlying protocol used by the World Wide Web and this protocol defines how messages are formatted and transmitted, and what actions Web servers and browsers should take in response to various commands. Archive: https://github.com/HTTPArchive/custom-metrics/pull/96

Ecosystem Tools

Link to roadmap projects

Contributors: @mukesh27 @swissspidy @westonruter

Creating Standalone Plugins

Link to GitHub overview issue

Contributors: @flixos90 @mukesh27 @10upsimon

  • @10upsimon Currently iterating on PR 864 in relation to issue 651 (Creating standalone plugins). Thank you to all who left feedback, much appreciated

Open Floor

  • n/a

Our next chat will be held on Tuesday, October 31, 2023 at 16:00 UTC in the #core-performance channel in Slack.

#core-performance, #performance, #performance-chat, #summary

Image loading optimization enhancements in 6.4

WordPress 6.4 comes with several enhancements to the wp_get_loading_optimization_attributes() function which was introduced in 6.3 as a central place to manage loading optimization attributes, specifically for images and iframes.

Quick recap: Loading optimization attributes are those such as loading="lazy" or fetchpriority="high", which can be added to certain HTMLHTML HyperText Markup Language. The semantic scripting language primarily used for outputting content in web browsers. tags to optimize loading performance in the browser.

Simplified complex logic

The logic of the wp_get_loading_optimization_attributes() is generally quite complex, as it caters for several factors that influence when to apply certain loading optimization attributes. When the function was originally introduced, it was particularly hard to follow, since it included lots of early returns as well as closures. This was mostly due to maintaining its original code paths that came from the deprecated wp_get_loading_attr_default() function as much as possible to avoid breakage.

While the function logic is still complex in WordPress 6.4, it has been notably simplified, taking a more sequential and thus easier to follow approach. This facilitated the implementation of further enhancements such as those outlined below.

Please refer to TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress. ticketticket Created for both bug reports and feature development on the bug tracker. #58891 for additional details on these changes.

Managing the decoding="async" attribute

The decoding="async" attribute has been present on images by default since WordPress 6.1 (see Trac ticket #53232). As of WordPress 6.4, the logic for applying the attribute has been consolidated in the wp_get_loading_optimization_attributes() function, as the attribute is a perfect fit for that function.

Deprecated function

As part of that change, the wp_img_tag_add_decoding_attr() function has been deprecated, as its logic is now incorporated into wp_img_tag_add_loading_optimization_attrs(). Unless you are using the now deprecated function in your code, no changes should be needed after this update in regards to the decoding="async" attribute. The change merely enables control over the attribute in a more consistent manner (also see the new filters introduced below).

If you are using the deprecated function and want to use a fully backward compatible replacement, you can implement a custom wrapper function such as the following:

function myplugin_img_tag_add_decoding_attr( $image, $context ) {
	global $wp_version;

	// For WP >= 6.4.
	if ( version_compare( $wp_version, '6.4', '>=' ) ) {
		$image = wp_img_tag_add_loading_optimization_attrs( $image, $context );

		// Strip potential attributes added other than `decoding="async"`.
		return str_replace(
			array(
				' loading="lazy"',
				' fetchpriority="high"',
			),
			'',
			$image
		);
	}
 
    // For WP < 6.4.
    return wp_img_tag_add_decoding_attr( $image, $context );
}

Please refer to Trac ticket #58892 for additional details on these changes.

New filters to control loading optimization attributes

With WordPress 6.4, two filters have been added to wp_get_loading_optimization_attributes() which allow modifying or completely overriding the logic used to apply the loading optimization attributes:

  • The wp_get_loading_optimization_attributes filterFilter Filters are one of the two types of Hooks https://codex.wordpress.org/Plugin_API/Hooks. They provide a way for functions to modify data of other functions. They are the counterpart to Actions. Unlike Actions, filters are meant to work in an isolated manner, and should never have side effects such as affecting global variables and output. can be used to modify the results from the WordPress coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress. logic.
  • The pre_wp_get_loading_optimization_attributes filter can be used to use entirely custom logic and effectively short-circuit the core function by returning a value other than false.

Below are a few examples on how these two filters could be used.

Filter usage examples

You could use the wp_get_loading_optimization_attributes filter to ensure a specific image within post content receives the fetchpriority="high" attribute while other ones do not:

function set_fetchpriority_high_on_specific_image( $loading_attrs, $tag_name, $attr, $context ) {
	if ( 'img' === $tag_name ) {
		if (
			'the_content' === $context &&
			isset( $attr['src'] ) &&
			$attr['src'] === 'https://example.org/a-specific-image.jpg'
		) {
			$loading_attrs['fetchpriority'] = 'high';
		} else {
			unset( $loading_attrs['fetchpriority'] );
		}
	}
	return $loading_attrs;
}
add_filter(
	'wp_get_loading_optimization_attributes',
	'set_fetchpriority_high_on_specific_image',
	10,
	4
);

Alternatively, you could use the wp_get_loading_optimization_attributes filter to disable adding the fetchpriority="high" attribute entirely:

function disable_fetchpriority_high( $loading_attrs ) {
	unset( $loading_attrs['fetchpriority'] );
	return $loading_attrs;
}
add_filter(
	'wp_get_loading_optimization_attributes',
	'disable_fetchpriority_high'
);

You could implement entirely custom logic in a pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party to detect which images appear in the viewport using client-side JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. logic or an external service and replace the function’s default logic with that:

function override_loading_optimization_attributes( $override, $tag_name, $attr, $context ) {
	// Bail if another filter callback already overrode this.
	if ( false !== $override ) {
		return $override;
	}

	// Use custom logic to determine whether image is LCP and whether it appears above the fold.
	if ( 'img' === $tag_name ) {
		$is_lcp      = custom_function_to_detect_whether_image_is_lcp_element( $attr );
		$in_viewport = custom_function_to_detect_whether_image_is_above_the_fold( $attr );

		/*
		 * Always add `decoding="async"`.
		 * Add `fetchpriority="high"` only to the LCP image.
		 * Add `loading="lazy"` to any image below the fold / outside the viewport.
		 */
		$loading_attrs = array( 'decoding' => 'async' );
		if ( $is_lcp ) {
			$loading_attrs['fetchpriority'] = 'high';
		} elseif ( ! $in_viewport ) {
			$loading_attrs['loading'] = 'lazy';
		}
		return $loading_attrs;
	}

	return $override;
}
add_filter(
	'pre_wp_get_loading_optimization_attributes',
	'override_loading_optimization_attributes',
	10,
	4
);

Please refer to Trac ticket #58893 for additional details on these changes.

Support for custom context values

As explained in the 6.3 dev note for the wp_get_loading_optimization_attributes() function, initially it only supported specific context values used by WordPress core. This made it confusing to write custom code making use of that function as you would have been forced to use a context string used elsewhere in core in order to get the performance benefits.

This limitation has been addressed in WordPress 6.4: The function now supports arbitrary contexts, and for the most part does not apply context-specific optimizations. There are two exceptions to this which are the “template_part_header” and “get_header_image_tag” contexts. Images within these contexts will always be interpreted to be in the headerHeader The header of your site is typically the first thing people will experience. The masthead or header art located across the top of your page is part of the look and feel of your website. It can influence a visitor’s opinion about your content and you/ your organization’s brand. It may also look different on different screen sizes.. The list of these two context strings is filterable with a new wp_loading_optimization_force_header_contexts filter, which allows images with other contexts to be always assumed to appear above the fold.

Context filter usage example

Below is an example: Assume that your plugin has a particular header image rendering function where it is safe to assume any image rendered with that function appears above the fold. You can rely on an arbitrary context specific to your function and then force WordPress core to consider images with that context to appear above the fold.

function force_myplugin_special_header_context_above_the_fold( $header_contexts ) {
	$header_contexts['myplugin_special_header'] = true;
	return $header_contexts;
}
add_filter(
	'wp_loading_optimization_force_header_contexts',
	'force_myplugin_special_header_context_above_the_fold'
);

Your image rendering function then needs to call wp_get_loading_optimization_attributes( 'img', $attr, 'myplugin_special_header' ) for the above filter to take effect.

Please refer to Trac ticket #58894 for additional details on these changes.

Hook priority change for wp_filter_content_tags()

The wp_filter_content_tags() was originally introduced in WordPress 5.5 as the foundation for lazy-loading as well as other optimizations for certain tags in a content blob. More recently a problem was identified that the function is called earlier than do_shortcode(), which means that any images added by shortcodes will not be able to make use of the performance benefits from wp_filter_content_tags().

To address that limitation, the hook priority with which wp_filter_content_tags() is hooked into the various filters (“the_content”, “the_excerpt”, “widget_text_content”, and “widget_block_content”) has been changed from the default 10 to 12. For context, the do_shortcode() function has hook priority 11.

While this is technically a breaking change, careful consideration and research across the WordPress plugin directory went into this decision. No plugins in the directory are affected by this change, given that there is only limited direct usage of the wp_filter_content_tags() function, and such usage has not been specific to the core filters. Still, in case you use the wp_filter_content_tags() function directly in your code, you may want to double check that this hook priority change does not result in a problem.

If your plugin processes custom content with the wp_filter_content_tags() function, it is encouraged to call that function after parsing other content such as blocks and shortcodes. The core change will not cause problems if that is currently not the case in your code, however it is recommended to have your custom logic follow a similar order.

Please refer to Trac ticket #58853 for additional details on these changes.

Props to @westonruter and @webcommsat for review and proofreading.

#6-4, #dev-notes, #dev-notes-6-4, #performance

New option functions in 6.4

WordPress 6.4 introduces a number of new functions related to options, with a particular focus on autoloaded options.

While options are autoloaded by default, based on the $autoload parameter of add_option() and update_option(), autoloading too many options is a common cause for slow server response time as well as bugs while using a persistent object cache.

To help pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party developers improve managing their plugins’ options, their performance, and whether to autoload them, two sets of functions are being introduced in 6.4:

  • wp_prime_option_caches() and related wrapper functions can be used to fetch multiple options with a single database query. Update: When this dev notedev note Each important change in WordPress Core is documented in a developers note, (usually called dev note). Good dev notes generally include a description of the change, the decision that led to this change, and a description of how developers are supposed to work with that change. Dev notes are published on Make/Core blog during the beta phase of WordPress release cycle. Publishing dev notes is particularly important when plugin/theme authors and WordPress developers need to be aware of those changes.In general, all dev notes are compiled into a Field Guide at the beginning of the release candidate phase. was first published, the function was named prime_options(), however that name was since revised to wp_prime_option_caches().
  • wp_set_option_autoload_values() and related wrapper functions can be used to update one or more specific options’ autoload values, independently of their option values.

New functions to retrieve multiple options in a single database query

The wp_prime_option_caches( $options ) function expects an array of option names, and then fetches any options that aren’t already cached with a single database query. The options are then stored in the cache so that subsequent get_option() calls for any of those options do not result in separate database queries.

Note that wp_prime_option_caches() does not return the options as its sole responsibility is to update the relevant caches. In order to actually retrieve multiple options at once, a new wrapper function get_options( $options ) has been introduced. It calls wp_prime_option_caches() for the option names given and then get_option() for every individual option, returning an associative array of option names and their values.

Last but not least, a third function wp_prime_option_caches_by_group() is being introduced, which primes all options of a specific option group (the mandatory first parameter of register_setting()). This can be helpful for plugins to fetch all options of a specific option group the plugin uses.

All of the above functions have been introduced not only as a performant way to retrieve multiple options from the database, but also as an alternative to autoloading options that are only needed in a few specific areas. For example, a plugin that currently autoloads a few options which are only used on the plugin’s own WP Adminadmin (and super admin) screen would be encouraged to use the new wp_prime_option_caches() or wp_prime_option_caches_by_group() instead of autoloading the options.

Example

In this example, we assume a plugin has a WP Admin screen that relies on four options: “myplugin_foo”, “myplugin_bar”, “myplugin_foobar”, and “myplugin_barfoo”. All of these options are only used on that admin screen and therefore should not be autoloaded. In other words, any add_option() and update_option() calls for those options should provide “no” for the $autoload parameter.

function myplugin_prime_admin_screen_options() {
	/*
	 * By priming the options here, no further database queries will be used
	 * when later calling `get_option()`.
	 */
	wp_prime_option_caches(
		array( 'myplugin_foo', 'myplugin_bar', 'myplugin_foobar', 'myplugin_barfoo' )
	);
}

function myplugin_add_admin_screen() {
	$hook_suffix = add_menu_page( /* Menu page arguments. */ );
	add_action( "load-{$hook_suffix}", 'myplugin_prime_admin_screen_options' );
}
add_action( 'admin_menu', 'myplugin_add_admin_screen' );

This code would ensure that the options are retrieved in a single database query. Any subsequent get_option() calls for these options would then not result in another database query and thus be extremely fast. As such, the WP Admin screen is just as performant as it would have been with autoloading those options, yet without unnecessarily slowing down performance of the entire site.

To further enhance that example, the plugin could rely on a single option group for which it registers those options. Here is the example code for that:

function myplugin_register_settings() {
	register_setting(
		'myplugin_admin_screen',
		'myplugin_foo',
		array( /* Registration arguments. */ )
	);
	register_setting(
		'myplugin_admin_screen',
		'myplugin_bar',
		array( /* Registration arguments. */ )
	);
	register_setting(
		'myplugin_admin_screen',
		'myplugin_foobar',
		array( /* Registration arguments. */ )
	);
	register_setting(
		'myplugin_admin_screen',
		'myplugin_barfoo',
		array( /* Registration arguments. */ )
	);
}
add_action( 'init', 'myplugin_register_settings' );

function myplugin_prime_admin_screen_options() {
	/*
	 * By priming the options here, no further database queries will be used
	 * when later calling `get_option()`.
	 */
	wp_prime_option_caches_by_group( 'myplugin_admin_screen' );
}

// `myplugin_add_admin_screen()` would remain as in the previous code example.

With that adjustment, the option registration would be the central place to manage the options, and the myplugin_prime_admin_screen_options() function would remain simple without maintaining a list of the exact options.

Please refer to TracTrac An open source project by Edgewall Software that serves as a bug tracker and project management tool for WordPress. ticketticket Created for both bug reports and feature development on the bug tracker. #58962 for additional details on these changes.

New functions to set option autoload values

The wp_set_option_autoload_values( array $options ) function expects an associative array of option names and their autoload values to set, and then updates those options’ autoload values in a single database query.

Additionally, two wrapper functions for the above are also being introduced for ease of use:

  • wp_set_options_autoload( $options, $autoload ) can be used to set multiple options to the same autoload value.
  • wp_set_option_autoload( $option, $autoload ) can be used to set the autoload value for a single option.

These functions can be useful in a plugin deactivation hook: After deactivation, the plugin’s options won’t be used anymore, yet they should not be deleted from the database until the user decides to uninstall the plugin.

Example

In this example, we assume a plugin has two options “myplugin_foo” and “myplugin_bar”, both of which are used in various frontend page loads and therefore autoloaded by default. To properly clean up after itself, such a plugin could implement usage of the new functions as follows:

function myplugin_activate() {
	wp_set_options_autoload(
		array( 'myplugin_foo', 'myplugin_bar' ),
		'yes'
	);
}
register_activation_hook( __FILE__, 'myplugin_activate' );

function myplugin_deactivate() {
	wp_set_options_autoload(
		array( 'myplugin_foo', 'myplugin_bar' ),
		'no'
	);
}
register_deactivation_hook( __FILE__, 'myplugin_deactivate' );

This code would ensure that the options are no longer autoloaded when the plugin has been deactivated. If the plugin gets (re-)activated, the options will be set to autoload again if they are already in the database.

Please refer to Trac ticket #58964 for additional details on these changes.

Related autoload bugbug A bug is an error or unexpected result. Performance improvements, code optimization, and are considered enhancements, not defects. After feature freeze, only bugs are dealt with, with regressions (adverse changes from the previous version) being the highest priority. fix for update_option()

While not directly tied to the above functions, it is worth noting that a bug relevant to autoloading has been addressed in 6.4: When using the $autoload parameter of update_option() alongside an option value update, the function would update the incorrect cache, not respecting the new autoload value. This could have severe implications such as returning a stale option value when the option in fact had already been deleted.

This bug has been fixed in 6.4, so that the $autoload parameter of update_option() can now be reliably used to change the option’s autoload value. It should be noted though that depending on the use-case the above functions to set option autoload values may be more suitable.

Please refer to Trac ticket #51352 for additional details on this bug fix.

Props to @westonruter and @webcommsat for review and proofreading.

#6-4, #dev-notes, #dev-notes-6-4, #performance

Replacing hard-coded style tags with wp_add_inline_style()

This dev notedev note Each important change in WordPress Core is documented in a developers note, (usually called dev note). Good dev notes generally include a description of the change, the decision that led to this change, and a description of how developers are supposed to work with that change. Dev notes are published on Make/Core blog during the beta phase of WordPress release cycle. Publishing dev notes is particularly important when plugin/theme authors and WordPress developers need to be aware of those changes.In general, all dev notes are compiled into a Field Guide at the beginning of the release candidate phase. highlights the changes made in WordPress 6.4 to style loading. The main focus of the changes was to replace manually created style tags printed at the wp_head action with calls to wp_add_inline_style(). This change was implemented to address issues related to redundant code and bypassing the coreCore Core is the set of software required to run WordPress. The Core Development Team builds WordPress.’s style enqueuing system, which made it challenging for third-party developers to manage and control the output of style tags.

Deprecated Functions

To maintain backward compatibility, the following functions have been deprecated and replaced with the new approach:

  • print_embed_styles()
  • print_emoji_styles()
  • wp_admin_bar_header()
  • _admin_bar_bump_cb()
  • the_block_template_skip_link()

Backwards Compatibility Unhooking

Previously, when wanting to unhook certain functions like print_embed_styles() from happening at wp_print_styles(), a theme or pluginPlugin A plugin is a piece of software containing a group of functions that can be added to a WordPress website. They can extend functionality or add new features to your WordPress websites. WordPress plugins are written in the PHP programming language and integrate seamlessly with WordPress. These can be free in the WordPress.org Plugin Directory https://wordpress.org/plugins/ or can be cost-based plugin from a third-party would do:

remove_action( 'wp_print_styles', 'print_emoji_styles' );

In 6.4 this print_emoji_styles() function is now deprecated. Nevertheless, the above method for preventing emoji styles from being printed is retained, even though they are now being printed by wp_enqueue_emoji_styles(). This applies to the other deprecated functions as well, so no developer action is required.

Developer Action Required

For developers who are currently using the wp_print_styles() function, whether it’s in unit tests or within their own code, some adjustments may be necessary to ensure a smooth transition. You should follow the example set by GutenbergGutenberg The Gutenberg project is the new Editor Interface for WordPress. The editor improves the process and experience of creating new content, making writing rich content much simpler. It uses ‘blocks’ to add richness rather than shortcodes, custom HTML etc. https://wordpress.org/gutenberg/. This is what you need to do:

/**
 * Remove the deprecated `print_emoji_styles` handler.
 * It avoids breaking style generation with a deprecation message.
 */
$has_emoji_styles = has_action( 'wp_print_styles', 'print_emoji_styles' );
if ( $has_emoji_styles ) {
    remove_action( 'wp_print_styles', 'print_emoji_styles' );
}

ob_start();
wp_print_styles();
$styles = ob_get_clean();

if ( $has_emoji_styles ) {
    add_action( 'wp_print_styles', 'print_emoji_styles' );
}

This code snippet demonstrates how to handle the situation. It first checks if there’s an action hooked to wp_print_styles for print_emoji_styles(). If it does exist, it removes the action temporarily to avoid issues with style generation.

By following this approach, you can ensure that your code remains compatible with the changes introduced in this commit while avoiding any disruptions to style generation. It’s recommended to review and adjust your code accordingly if you’ve been using the wp_print_styles() function.

Exceptions

In the case of the functions for printing custom backgrounds and custom styles, converting them to use inline styles was deemed infeasible. Changing the style tagtag A directory in Subversion. WordPress uses tags to store a single snapshot of a version (3.6, 3.6.1, etc.), the common convention of tags in version control systems. (Not to be confused with post tags.) IDs in this context could potentially disrupt JavaScriptJavaScript JavaScript or JS is an object-oriented computer programming language commonly used to create interactive effects within web browsers. WordPress makes extensive use of JS for a better user experience. While PHP is executed on the server, JS executes within a user’s browser. https://www.javascript.com/. functionality for several plugins in the repository. Therefore, these functions remain unaffected by this change.

Please refer to #58775 for additional details on these changes.

Props to @spacedmonkey for writing the dev note.
Props to @westonruter, @flixos90, @webcommsat, and @bph for review and proofreading.

#6-4, #dev-notes, #dev-notes-6-4, #performance