Tests are back at 1h-1h40, depending on the actual testbot. It used to be down to 45m-1h after the plugin discovery namespace issue was resolved.

There's no obvious single big thing to point at, but I have a few ideas to improve performance. Note that the numbers below overlap, e.g. both dfac() and module enable trigger kernel rebuilds.

  • 3 calls to dfac() per test method, 36% in total.

    Do we need all these? Can we optimize the install process and skip unnecessary steps?

    57% of that is menu_router_rebuild()
    - 90% of that _menu_navigation_links_rebuild()
    - 70% of that saving menu links
    - 22% system_rebuild_module_data()

  • 24% Drupal\Core\DrupalKernel::buildContainer()
    54% yaml parsing (86 repeated calls to the same files)
    37% ContainerBuilder::compile() with 17 calls.

    Can we statically cache loaded yml files? Prevent container compiles/dumps if nothing has changed?

  • We have a few more required modules than we used to: module_enable() is 25% with 12 modules (11 + one of the test).

    • entity.module, datetime is now a dependency of node.
      #1541298: Remove Node module dependency from Testing profile would remove that and datetime from a lot of tests.
    • field_sql_storage will away I guess when we merge field storage into the entity storage controllers.
    • We want to remove entity.module, as we can now have plugins in components in a sane way.
  • 1000 calls to Symfony\Component\Yaml\Parser::parse()
    86 services yml files (53% of that)
    772 calls from drupal_parse_info_file() (but only 28% of the time)

Comments

berdir’s picture

Issue tags: +Test suite performance

Tagging.

berdir’s picture

StatusFileSize
new108.72 KB

Attaching the xhprof file that I was looking at.

Note when you try to repeat this, remove the cpu flag from xhprof_enable(), that increases the overhead a lot, especially of the yaml parsing, as there are *a lot* of method calls involved.

cweagans’s picture

Can we statically cache loaded yml files?

Instead of calling Parser::parse() directly, we could call drupal_parse_info_file() (and maybe rename it). Any file parsed with this function is statically cached. However, I'm not sure how we have 1000 calls to Symfony\Component\Yaml\Parser::parse(). That seems a little weird.

berdir’s picture

Status: Active » Needs review
StatusFileSize
new1.57 KB

Actually, it's not. It's drupal_static()'ally cached, which we clear 3 times in a test. Seems like we could easily make that a static(), no reason to reset it?

And the function does more than, it also checks version and stuff.

So I prefer to keep them separate static caches.

The attached patch does that, seeing a considerable performance improvement for even a test with a single test method. Should even be bigger for multiple test methods as we save even more calls.

Let's see what the testbot thinks about this.

chx’s picture

Status: Needs review » Reviewed & tested by the community

Lovely!

tim.plunkett’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: -Test suite performance

The last submitted patch, yaml-static-cache-2006434-4.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
Issue tags: +Test suite performance

#4: yaml-static-cache-2006434-4.patch queued for re-testing.

cweagans’s picture

Status: Needs review » Reviewed & tested by the community

This is nice :)

berdir’s picture

Status: Reviewed & tested by the community » Needs review

Maybe, but I'm not happy about the testbot results (not really seeing any improvements there), want to investigate some more and also do another profile run now that this is out of the way.

Maybe keep this is a meta task and create separate issues for specific patches?

moshe weitzman’s picture

It would be ideal if we focused on refactoring and not just adding caching. Maybe the YAML files can be an exception but my point stands in general. Speeding up or ditching or otherwise dealing with menu links would be very much appreciated. Those gets rebuilt during regular cache clear of Drupal as well.

Do we have a meta for converting these to unit tests? That always helps, in an indirect way.

berdir’s picture

Status: Needs review » Active

Opened #2026367: Use static caches for parsed .services.yml and .info.yml files for the patch, to keep this open as a general profiling issue. Please RTBC the patch there :)

mile23’s picture

Do we have a meta for converting these to unit tests?

#1938068: Convert UnitTestBase to PHPUnit

moshe weitzman’s picture

I was referring to the effort to convert from web tests to unit tests, not the effort to convert from one type of unit test to a better type of unit test.

sun’s picture

Title: Speed up web tests » [meta] Speed up web tests
Issue summary: View changes
Related issues: +#2160091: drupal_rebuild() rebuilds container twice, since drupal_flush_all_caches() also rebuilds it

I also noticed the issue of dfac() being called way more often in D8 in #2160091: drupal_rebuild() rebuilds container twice, since drupal_flush_all_caches() also rebuilds it

berdir’s picture

Some new notes, when looking at an xhprof report for running views_ui\DisplayTest, which contains an impressive amount of 12 test methods. Runs 4min without xhprof, 5 m with.

* Parsing config schema still uses 20% of the time, we call FileStorage::readMultiple() 200x times from TypedConfigManager
=> I have no idea what to do about it :( Note that the overhead here is probably quite large as symfony yaml parsing involves a trillion method calls (some methods are called 4M times...)

* 17% of the time is deleting cache tags (6600 calls for 12 test methods)
** 9% of the whole time is entity render cache tags, 98% of those are menu_link'.
** 4.4% is menu_cache_clear(), which is also called for every saved menu link (for the menu)
=> menu_links currently define a view builder and don't use it, we can remove it and then that should save 10% of the time there. @amateescu is already working on an patch, see #2100467: Menu links are not ready to use a view builder so remove it from the annotation for now
=> We can look into doing something similar as we do with menu router rebuild and collect cache tags and write them at the end of the request. The most obvious advantage would be that we only need to update each tag once per request, for example for menu's, for example, on my pretty standard installation, I have 50 links in the admin menu, that means 50 Cache::deleteTags() calls for the same tag when rebuilding the menu links and real sites with have *a lot* more than that.

* Those 12 test methods result in 48 calls to drupal_flush_all_caches(), which takes up ~40% of the whole execution time for me. Those calls are coming from install_finished(), _install_profile_modules_finished() and 2x resetAll(), once in setUp() and once in enableViewsTestModule().
** 80% of that is RouteBuilder::rebuild(),
** 92% of that is spent in the event dispatcher rabbithole,
*** 70% of that is RouterRebuildSubscriber::onRouterRebuild(), this is what triggers menu_router_rebuild() and that menu_link_rebuild_defaults().
**** What's crazy is that we right now spend *83%* of the time it takes to save menu link entities in postSave(), most of that is deleting cache tags as mentioned above and 10% in preSave(), the actual saving of menu links is almost irrelevant ;)
*** 27% is RouteSubscriberBase::onAlterRoutes().
=> We have issues to limit the number of dfac() calls, looks like views ui tests add another call to the 3 common ones.

* DrupalKernel::buildContainer() is called 264 times in total, taking ~13% of the time. Looks like the whole compiler pass stuff takes up a lot of time, especially AnalyzeServiceReferencesPass is a slow one with 5% of the total execution time.

moshe weitzman’s picture

Nice data!

* Parsing config schema still uses 20% of the time, we call FileStorage::readMultiple() 200x times from TypedConfigManager
=> I have no idea what to do about it :( Note that the overhead here is probably quite large as symfony yaml parsing involves a trillion method calls (some methods are called 4M times...)

Good news. All those file reads and yaml parsing is slated to go away completely with #2161591: Change default active config from file storage to DB storage.

berdir’s picture

Nope, it's not. That is *only* config *schema*. Unless you want to move that to the database too? And even if we would, we'd still have to read from yml to put it in the database in the first place. Wouldn't change anything.

Clarification: The actual yaml parsing calls are everything, but 75% of the config yaml parsing is config schema. And only meant that with those 20%, all FileStorage::read() is ~25%.

berdir’s picture

#2171683: Remove all Simpletest overrides and rely on native multi-site functionality instead has apparently slowed down tests, the only thing I could find is that it now uses the Database cache during the installer, which leads to a ton of cache tag delete queries. Opened #2194273: Avoid repeated cache tag deletions to deal with that.

wim leers’s picture

#16: impressive research — thank you!

I'm working on fixing/improving cache tags, one of the things I'll look into is cache tag deletion/invalidation performance. But… it seems like you've already fixed it at #2194273: Avoid repeated cache tag deletions? :)

Having worked on #2179083: Rendered menus (e.g. menu blocks) should set cache tags to inform the page cache, it was pretty obvious that the menu/menu link cache handling is a transitional state, I think it should be feasible to at least significantly clean that up, which would make it more understandable/prepared for perf improvements, and hopefully that'll lead to some perf wins already.

cameron tod’s picture

* Parsing config schema still uses 20% of the time, we call FileStorage::readMultiple() 200x times from TypedConfigManager

Is this addressed by #1851234: Slow yaml parsing slows down tests which load/save many config files (e.g. views)?

berdir’s picture

Did some new tests, we did achieve some nice improvements over the last days.

It also looks like a number of other issues like the improved extension parsing and other issues (menu_router removal possibly? improved testbot run times a lot, the fast bots are currently < 30min, which is pretty impressive!

Comparing to the previous results is tricky as quite a few things changed including the total runtime went down quite a bit (3m22, but not sure if I had xdebug enabled then), a few notes:

* Cache::deleteTags() is down to ~1% of the whole runtime
** What's even more interesting is that 35% of that is drupal_get_complete_schema(), I just re-rolled #2185015: Remove SchemaCache and CacheArray which now removes that tag.
** Another 35% is caused by Drupal\Core\Config\CachedStorage::write(), aka the listByPrefix cache, I want to try and switch to a CacheCollector there... That tag is also deleted *a lot* on actual sites. (Should we reset those numbers on a full cache clear? Otherwise they'd climb forever?)

Config reading is actually up in terms of percentage, because that's not much faster, in comparison to improvements that were made elsewhere. But as mentioned before, there is probably quite some overhead hidden in all those method calls.
* Config::save() is 25% of the whole execution time now, 90% of that spent in typed config/type casting.
* FileStorage::read() is 32% of the time, a large part of it through save()/typed config, so those two numbers overlap a lot.

Enabling the yaml extension and trying again, total execution time is 2m17:
* Config::save() is now down to 3%
* FileStorage::read() is <1%.

To figure out how much of that is xhprof overhead, disabling it and running tests again:

With yaml extension: 1 min 51 sec (2m17 with xhprof)
Without: 2 min 48 sec ( 3m22 with xhprof)

Xhprof or not, the yaml extension makes an impressive difference, at least for tests.

Edit: Using the yaml extension needs the patch from #1920902: Add a Drupal Yaml wrapper so we can default to PECL Yaml component if it is available.

sun’s picture

Not sure why this did not appear in profiling yet: #2209817: Duplicate initialization of maintenance theme registry

moshe weitzman’s picture

Would be great to get a new round of profiling here. It usually turns up new stuff. We still do a slow container rebuild for each module enable.

yesct’s picture

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

cilefen’s picture

It looks like tests may be under one hour now and this is anyway far out of date. Close?

Version: 8.9.x-dev » 9.2.x-dev

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

quietone’s picture

Status: Active » Reviewed & tested by the community

Can across this and checked the latest test run on Drupal 10.1.x and it was under 1 year. I agree with @cilefen suggestion from 2 years ago.

xjm’s picture

Status: Reviewed & tested by the community » Closed (outdated)

Yeah, let's do this.