Make WordPress Core

Opened 5 years ago

Last modified 2 years ago

#37006 new defect (bug)

Improve performance of widgets in the customizer (at scale)

Reported by: jessecurry Owned by:
Milestone: Priority: normal
Severity: normal Version: 3.9
Component: Customize Keywords:
Focuses: Cc:


We've got a multisite install that uses the Layers theme and we're seeing extremely poor performance when accessing the customizer (80+ seconds).

We've carried out all of the optimization that the Layers team suggested, but the customizer is still loading massive amounts of data when we try to access it. It's gotten so bad that our server often kills the request. I pulled the big chunk of generated Javascript out of the page and it appears the bulk of it is in the "controls" section.

We managed to pull a few transaction traces with NewRelic.

Attachments (2)

customizer-output.txt.zip (3.4 MB) - added by jessecurry 5 years ago.
Customizer Output (blob of javascript)
newrelic.png (156.2 KB) - added by jessecurry 5 years ago.
Screenshot of transaction trace

Change History (7)

5 years ago

Customizer Output (blob of javascript)

5 years ago

Screenshot of transaction trace

#1 @westonruter
5 years ago

  • Version changed from 4.5.2 to 3.9

@jessecurry thanks. This has been raised previously on the Layers GitHub issues. Please see https://github.com/Obox/layerswp/issues/246

See my comment there: https://github.com/Obox/layerswp/issues/246#issuecomment-194461882

I don't think that storing widgets as posts will help with the specific performance problem here. In fact, it could make performance worse if there is not a persistent object cache. In any case, the Widget Posts module of the Customize Widgets Plus plugin is an implementation of storing widgets in a widget_instance post type. (This plugin needs more documentation and it needs an update on WordPress.org.)

I think the specific problem you are facing is the fact that widgets in Core are still very PHP-centric. PHP is currently required to render the widget control forms, handle processing and sanitization of widget instance data, in addition to rendering the form into the page. PHP should not be required for rendering the form and handling the sanitization in the UI. Server-side sanitization should be applied only when the instance data is sent to the preview.

The core Trac ticket for this is #33507: Allow widget controls to be JS-driven. A big problem of running out of memory is that widgets in Core currently require the WP_Widget::form() method to be called for each and every widget that has a Customizer control. This can greatly balloon the HTML response size. If JS-driven widgets are used, then there would only be one HTML template for a given widget which could be re-used for each widget instance output.

Another issue in Core is that all widgets get rendered up-front regardless of whether they are going to be used. #23909 (Widgets settings loaded and instances registered unnecessarily) is the Trac ticket focused on this, along with #28580 (Speed up customizer by lazy-loading controls and settings as needed).

There are not firm timetables on implementing these Trac tickets, though it's all definitely something I'm interested in for 4.6 if time and resources allow.

I will note that there is now a feature plugin or JS Widgets: https://github.com/xwp/wp-js-widgets/

This should greatly improve performance for generating controls, since they would all be constructed from JS templates. The problem is that all widgets would need to be re-implemented as JS Widgets to see the performance benefit. Also, this plugin doesn't address the lazy-loading of settings.

#2 @jessecurry
5 years ago

Thank you for the detailed reply. Apologies for the duplicate issue.

In order to have a somewhat working customizer we've been looking into a way to load customizer data for only the current page instead of loading everything, we realize this would prevent editing other pages during a customizer session, but this would allow us to continue working with the site.

One of my coworkers was able to hardcode a page ID which allowed us to rapidly enter the customizer and edit that page, but he was unable to figure out a way to dynamically pull the page ID as it's not available to $wp_customize and the method get_current_screen() is not in scope at the time.

It looks like some larger changes are in the pipeline, but we were hoping this hack might allow us to continue working with the site, any insight would be appreciated.

#3 @westonruter
5 years ago

@jessecurry See also this plugin proposal for adding page-specific sidebars that get loaded dynamically: https://github.com/xwp/wp-customize-content-widgets

The problem currently is that all sidebars and all widgets get loaded up front. This doesn't scale, and this is something the plugin would fix.

As for obtaining the page currently in the preview, please refer to the Customize Posts plugin to see the approach taken there. This should be rolled into core via #36582, but you can use the plugin code for now.

#4 @jessecurry
5 years ago

Thanks @westonruter.

#5 @westonruter
5 years ago

  • Summary changed from Extremely poor customizer performance to Improve performance of widgets in the customizer (at scale)
Note: See TracTickets for help on using tickets.