In #1744302-11: [meta] Resolve known performance regressions in Drupal 8, I found a huge performance regression from 7.x to 8.x for the front page with no content. Per the following comment in that issue, a large portion of it is related to EntityNG. This surprised me, because at this time, only nodes and comments are converted to EntityNG, and a front page with no content has neither.
Turns out however, that _node_add_access() gets called twice: once for checking access to the node/add link that's part of the Tools menu block, and once by the Views ListingEmpty plugin. Since there are 2 node types ('article' and 'page'), this results in 4 calls to node_access('create', $node_type). In Drupal 7, this access was evaluated without creating a node entity object. But in Drupal 8, it results in 4 calls to entity_create('node'), so that access checkers can always operate on entities rather than sometimes on entities and sometimes on type strings.
At the same time, EntityNG instantiation in general, and nodes in particular, is very expensive, especially the first time in a request. Here's a breakdown:
- Time to
require_once
the 54 classes listed in the attached txt file: 6ms. Note, that's with APC and without using any autoloader at all: just iterating that array and calling require_once on a full path on my hard drive. Maybe my MAMP computer has worse file I/O performance than a production server though. - Time to call
typed_data()->getDefinitions();
exclusive of class loading: 1.5ms. Once called once, it's then statically cached, so this is isolated from the following items. - Time to call
drupal_container()->get('plugin.manager.entity')->getStorageController('node')->getFieldDefinitions(array('EntityType' => 'node', 'Bundle' => 'page'));
exclusive of the above items: 20ms. Again, once called once, it's then statically cached, so this is isolated from the following items. - Time to then call
entity_create('node', array('type' => 'page'));
the first time: 7ms. - Time for each additional
entity_create('node', array('type' => 'page'));
: 2.5ms. The difference between this and the first time is almost entirely due to the $this->prototypes cache in TypedDataManager::getPropertyInstance().
Some thoughts about this:
- A total of 35ms to load/create the first entity in a request is pretty outrageous. I think we need to speed that up. We have #1762258: Speed up the new entity field API open though the patch there is focusing on some other areas than the above.
- Maybe we should change node_access() to not create a $node for the sole purpose of checking creation access on a type? However, while that would speed up pages that don't otherwise instantiate any entities, how impactful in the real world would that be? In the real world, most requests need to instantiate an entity at some point anyway.
- Even the 2.5ms for each additional node instance, once a lot of the typed data info is statically cached, is still a lot (~10x) larger than non-EntityNG entities (I tested a taxonomy_term, which is not yet NG, and found ~0.2ms for that). A lot of that is due to the magic getters and setters which are known to be about 30x slower than direct property access.
Given that we have other Entity Field API performance issues in the queue, I'm not yet sure what we should make the scope of this one. Thoughts?
Attachment | Size | Status | Test result | Operations |
---|---|---|---|---|
entity_create.txt | 2.09 KB | Ignored: Check issue status. | None | None |