Did some profiling on #1828642: Convert as many Views tests as possible to DrupalUnitTestBase to see where we are losing so much time compared to other (Drupal)UnitTests.
Turns out most time is actually spent in loading default config. Of the 20s it took to run Field: Unit Tests (with 7 test methods), 14s (70%) is spent in config_install_default_config(), 8s in views_config_import_create(), almost 10s is spent in Symfony\Component\Yaml\Parser::parse(), with 1250 calls to it. Note: Those times overlap of course :)
Then I remembered that profiling slows function calls down a lot (and w'ere talking about 13 million function calls here... o.0) so, I tested it without xhprof and the total test time went down to 7s.
Still, it's something that's worth checking, I think there are two separate ways to improve the speed of these tests.
- Load fewer default config files by splitting up views_test_config into multiple modules. That's probably the easiest way but it will only help with the tests and the downside is that the number of modules is going up, so it's more time spent in parsing .info files :) (which might become yaml files too ;))
- Speed up the actual yaml parsing. Installed the yaml extension, did some tests and came up with this:
$start = microtime(TRUE);
$extension = yaml_parse_file('core/modules/views/tests/views_test_config/config/views.view.test_destroy.yml');
dpm($extension, 'extension');
dpm(round((microtime(TRUE) - $start) * 1000, 3), 'extension ms');
use Symfony\Component\Yaml\Parser;
$parser = new Parser();
$start = microtime(TRUE);
$symfony =$parser->parse(file_get_contents('core/modules/views/tests/views_test_config/config/views.view.test_destroy.yml'));
dpm($symfony, 'symfony');
dpm(round((microtime(TRUE) - $start) * 1000, 3), 'symfony ms');
dpm($symfony == $extension, 'result is equal');
The dpm at the end confirms that the end result, at least in this case is the same, so it looks like it might actually be able to use it as a replacement if available. I did run that with and without xhprof, to also learn more about how much that is exactly slowed down.
Without xhprof:
extension ms => 2.911
symfony ms => 9.355
With xhprof:
extension ms => 4.321
symfony ms => 29.101
The exact numbers vary a bit, but the trend is that extension ms without xprof = 1x, symfony ms without xhprof = 3x, and symfony ms with xhprof is 10x. So according to these numbers, we might be able to make the actual parsing ~3x times faster when using the yaml extension, if we can actually ensure that they result in the same structure.
Comment | File | Size | Author |
---|---|---|---|
#18 | 1851234.18.patch | 15.67 KB | alexpott |
#17 | 1851234.17.patch | 14.9 KB | alexpott |
#13 | 1851234.13.patch | 6.62 KB | alexpott |
#10 | xhprof-still-woah.png | 90.83 KB | alexpott |
#9 | xhprof-woah.png | 197.36 KB | alexpott |
Comments
Comment #1
BerdirComment #2
gddComment #3
dawehnerAs discussed with tim we will introduce some kind of parameter similar to protected $modules on tests, which specifies the needed views config
and move the config files to another directory so they are not loaded on each test.
Comment #4
sun1) Yes, it has always been the intention to allow sites to use the php-yaml extension for parsing and dumping instead of the user-space Symfony Yaml component. My original plan was actually to bake a conditional check right into Config\FileStorage and simply/automatically use the php-yaml extension if it is available.
2) The effort on 1) only got side-tracked and blocked on research whether YAML 1.1 is sufficiently compatible with YAML 1.2 - limited to the configuration system's usage. php-yaml internally uses LibYAML, which implements the YAML 1.1 spec. Symfony's Yaml component implements the latest and stable YAML 1.2 spec.
3) When executed in C code, I even wonder how much of a gain the additional cache backend layer in front of the FileStorage is, or whether that wouldn't be superfluous. But of course, evaluating that would only be a follow-up step to 2) and 1).
Comment #5
BerdirYes, exactly, that's what I've been wondering about too, making it swapable will require us to verify that the array returned by the parsers is actually equal. The views example that I used was.
A conditional check in FileStorage is one option, having two implementations and configure the service based on the existence of the extension would be another. This should even be possible in a contrib module.
Depends on a lot of things, I'm quite sure it's still worth to have the caching layer, especially if we can improve it to do better caching than a single cache get/set*. But removing it would just be a matter of configuring the container services differently, which again, is a thing that a contrib module can now do.
* Maybe not by default, but I guess it's going to be really useful for large sites to have custom cache implementations which can e.g. cache the stuff that's loaded on all pages in a single cache entry and so on.
Comment #6
tim.plunkettSee also #1856272: Speed up tests by only installing the views used by that tests, the exciting parts are in ViewTestData::importTestViews().
Comment #7
Berdir@msonnabout created a pretty neat test script here: https://gist.github.com/msonnabaum/5052884
My results based on that are here: https://gist.github.com/Berdir/5052940
Comment #8
alexpottSee #1920902: Add a Drupal Yaml wrapper so we can default to PECL Yaml component if it is available for an implementation of a wrapper to use PECL extension or Symfony's php land implementation if the extension is not available.
Comment #9
alexpottLooking at the Symfony code I think there are some optimisations we can do here - will submit an upstream PR in a bit.
The xhprof for this looks very promising - although as @Berdir has said Symfony's YAML parser is noticeably slowed by xhprof.
Comment #10
alexpottDisabling cpu and memory flags in XHProf so the profiling as less of an impact - still shows a massive improvement.
Comment #11
alexpottUsing https://gist.github.com/msonnabaum/5052884
HEAD
Patch applied
So this patch definitely is an optimisation.
Comment #12
alexpottCreated a PR https://github.com/symfony/symfony/pull/10312
Comment #13
alexpotthttps://github.com/symfony/symfony/pull/10312 got merged!
Created another one already :) https://github.com/symfony/symfony/pull/10317
With this https://gist.github.com/msonnabaum/5052884 now looks like
Comment #14
alexpottConsidering both https://github.com/symfony/symfony/pull/10312 and https://github.com/symfony/symfony/pull/10317 have now been merged - we could commit the fixes directly to our /vendor to get the benefits early?
Comment #15
catchI think we should do that - but can we do it via updating composer to point to the hash?
Comment #16
alexpottNot really - we're already on 2.4.* and fabpot merged to 2.3-dev (no idea why) so it is not in 2.4.* or 2.4-dev yet.
Comment #17
alexpottSo what we can do is update
and then
$ composer update symfony/yaml
Comment #18
alexpottOr we can fix to a specific commit...
Comment #19
sunThanks! That appears to be in line with some of our other dev dependencies:
Might be a good idea to file an issue to lock down those other dependencies that are currently using
2.4.*
, too...RTBC if bot agrees.
Comment #20
catchLooks great. We can get back on the next point release but I'd rather pin to a commit than handle local patches.