Problem/Motivation

During performance auditing and testing in jsonapi module's issue #3014232: [regression] ResourceTypeRepository is significantly slower in JSON:API 2, becomes noticeable when handling hundreds of interlinked resources it turned out that the cache service cache.static is much slower than using native PHP private variables. mainly caused by the internal serialize on cache set and unserialize on EVERY cache get. In that particular issue the speed-up was around 60% of total request time. and the actual slow method that this was happening from 6 seconds to 30 mili-seconds. (around 200 times).

It turned out that the internal implementation used for static caches namly the cache.static service is \Drupal\Core\Cache\MemoryBackend and it's MUCH slower than the other available one \Drupal\Core\Cache\MemoryCache\MemoryCache

Proposed resolution

Change the factory and use the faster memory cache implementation internally by default or introduce a new service that could utilize that as easy as the cache.static, but to use the faster implementation.

Remaining tasks

Discussion, patch etc.

User interface changes

None expected - tweaks in cache internals.

API changes

TBD.

Data model changes

None expected (cache tweaks).

Release notes snippet

TBD.

Comments

ndobromirov created an issue. See original summary.

gabesullice’s picture

Change the factory and use the faster memory cache implementation internally by default or introduce a new service that could utilize that as easy as the cache.static, but to use the faster implementation.

I think introducing a new service, maybe cache.memory, is the way to go. The reason cache.static is so slow, is because it uses serialize()/unserialize() to store its items. That's supposedly a feature, not a bug, because it means that the cache items can't be altered by reference (hence the name "static").

#1596472: Replace hard coded static cache of entities with cache backends was the issue that added the MemoryCache class, but failed to add a proper cache bin service for it. It only added the entity.memory_cache service, which is now probably being used in a number of places that it wasn't intended to be used.

#2973286: Clean up MemoryCache and MemoryBackend naming issues proposes to rename the classes to make their usage constraints clearer, but I think the more onerous problem is actually that there isn't a general service for MemoryCache and that core.services.yml doesn't have a good comment for cache.static. It just says "A special cache bin that does not persist beyond the length of the request." but it really should be more like its class docblock: "Should be used for unit tests and specialist use-cases only, does not store cached items between requests."

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

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.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.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

ndobromirov’s picture

@andypost I do not see how a serializer is related here as we aim to do no object serialization :).

andypost’s picture

@ndobromirov for me [de]serialize speed also makes sense when we tons of caches used

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

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now 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.

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

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

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

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

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

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

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

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now 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.

borisson_’s picture

Status: Needs work » Active

This is a really interesting issue, I'm not sure what the next steps are, but it seems like there should be a patch here first before we can mark it as needs work.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

geek-merlin’s picture

Status: Active » Needs review
StatusFileSize
new1.3 KB

Intereting!

Doing the most bold approach to see what happens. NR only for the bot.

EDIT: Can ignore. Throws as of a wrong clone stmt.

geek-merlin’s picture

StatusFileSize
new485 bytes

Let's try swap out the static backend for the (badly named) entity.memory.

EDIT: Lots of breaks.

geek-merlin’s picture

StatusFileSize
new1.25 KB

Try replace serialize w/ clone (+remove tag sort), to see what breaks.

Only few test breaks, and NO noticeable change in performance.

Status: Needs review » Needs work

The last submitted patch, 19: 3016690-19.patch, failed testing. View results

geek-merlin’s picture

So it looks like:
- The originating issue found that cache.static is 3x slower than static class property caching (which is not tags aware) for some entity structures with many related objects. Fix was to use entity.memory_cache which does NOT serialize.
- Core since 8.6 uses entity.memory_cache, likely for same reasons
- The difference is, that cache.static does serialize and you get out a new built data object that is an exact copy of what you put in. The entity.memory_cache otoh gives you the same object, and if it was changed in the meantime, you get that changed version.

IF more optimizations are needed here, i can only imagine one route:
- Use https://github.com/myclabs/DeepCopy. And maybe we need / profit from a deep-clone-filter that takes __sleep / __serialize (and thus DependencySerializationTrait) into account.

I think i'm done here.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

catch’s picture

These should never have been added in the way they were IMO, but also probably predate the availability of MemoryCache as distinct from MemoryBackend.

Is there a reason not to use MemoryBackend instead of cache.static in the backend chain? I would think that would work.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.