Problem/Motivation

Found when profiling serialization of about 1,800 entities via a drush command.

TypedDataManager::getPropertyInstance() is called by entity ::getTranslatedField().

We already do some static caching of instances based on the object and root definition. However even generating that cache key can be expensive due to lots of calls to EntityDataDefinition::getDataType().

On the drush command I'm testing, there were 176k calls to that, taking 184ms.

By additionally static caching that calls, we can cut about 181ms off that time.

While I don't think we'll see much if any benefit for 'normal' requests, any list of similar entities with lots of fields will have at least some duplication here.

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

Issue fork drupal-3604100

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

catch created an issue. See original summary.

catch’s picture

Status: Active » Needs review
StatusFileSize
new683.69 KB
new681.45 KB

nicxvan’s picture

Cache cache key parts (yes really)

lol.

Is there a script I can use to replicate this?

catch’s picture

You can see it on the front page of umami, if you do TRUNCATE cache_render; TRUNCATE cache_dynamic_page_cache; first.

It is very non-dramatic but it's visible. Attaching xhgui screenshots of that.

catch’s picture

StatusFileSize
new503.21 KB

Looking more, I think we can do similar for ::getRoot() since the $object passed in is often identical. Can use the same SplObjectStorage.

Pushing a commit and attaching another screenshot. This halves the calls to TypedData::getRoot() on the Umami front page.

This appears to get inclusive wall time for TypedDataManager::getPropertyInstance() down from 32ms to about 25ms (with cold render and dynamic page caches).

catch’s picture

Found a memory leak - SplObjectStorage counts as a reference to the object, we can just cap the number of items at 3000 and flush it when a new root object is provided if it's over that number. Tested with the client-specific drush command it still helped a lot.

catch’s picture

Switched from the SplObjectStorage + cap approach to a WeakMap, and WeakMap works great.

Getting about 2 seconds off an 8 second drush command (same as raw SplObjectStorage approach, seems to be identical performance) as well as a ~15mb reduction in memory usage from 58mb to 43mb. The memory usage reduction isn't the weakmap itself, but the work we're saving reducing memory usage while the weakmap means the memory requirement from the new approach is minimal.