This will most likely be affected by the work done in #1858616: Extract generic CacheCollector implementation and interface from CacheArray.

From #1811816: Benchmark node_page_default vs. node view:

_views_fetch_data() is responsible for 65% of the time spent in drupal_static() with just 30% of the calls. Might make sense to use the fast static cache pattern here?

I'm wondering whether it makes sense to have a service which provides the information, so that there is no need anymore for a procedural function and so no drupal_static.

So we should create a caching class of some flavour to basically replace the code in views/includes/cache.inc. This will save us alot of calls to drupal_static etc... For example, at the moment config() is called on every call to views_cache_get, we can easily get around this.

We could also make this a service in the container, so it is easily swappable too. win!

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

damiankloip’s picture

I will work on an initial patch for this. I think it will be good to play around regardless. We can then track any changes to the interface in #1858616: Extract generic CacheCollector implementation and interface from CacheArray as and when we need to.

damiankloip’s picture

Status: Needs review » Active
FileSize
5.31 KB

Here's an initial conversion, in a ViewsDataCache class. I've currently just hacked this into views_fetch_data for now. It still needs work for sure, just putting what I have up for now.

I will work on this some more later. I would like to get this working, then track the above. The patch is not really tested yet!

damiankloip’s picture

Status: Active » Needs review
damiankloip’s picture

Status: Active » Needs review
FileSize
5.31 KB
damiankloip’s picture

FileSize
5.3 KB

This patch, sorry for the noise.

damiankloip’s picture

FileSize
30.47 KB

Added it to the container, replaced usage of views_fetch_data, removed cache.inc. Haven't done anything with the language parameter yet. This would need to be passed in somehow too.

damiankloip’s picture

FileSize
25.59 KB

Sorry, that was silly, we can't remove cache.inc!

Status: Needs review » Needs work

The last submitted patch, vdc.1867782-7.patch, failed testing.

damiankloip’s picture

That's not too bad, I'll look at this later!

damiankloip’s picture

Status: Needs work » Needs review
FileSize
25.59 KB

That should sort most of them...

Fabianx’s picture

Status: Needs review » Needs work
Issue tags: +Performance

Tagging

Fabianx’s picture

Status: Needs work » Needs review

x-post

dawehner’s picture

Awesome stuff!

+++ b/core/modules/views/lib/Drupal/views/Plugin/Type/DefaultWizardDeriver.phpundefined
@@ -38,7 +38,7 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
+      $views_info = drupal_container()->get('views.views_data')->get($table);

+++ b/core/modules/views/views_ui/admin.incundefined
@@ -2298,7 +2298,7 @@ function views_ui_field_list() {
+            $data = drupal_container()->get('views.views_data')->get($item['table']);

It would be possible to just get the views data object once and then use that object.

+++ b/core/modules/views/lib/Drupal/views/ViewsBundle.phpundefined
@@ -32,6 +32,8 @@ public function build(ContainerBuilder $container) {
+    $container->register('views.views_data', 'Drupal\views\ViewsDataCache');

Inject all the things!

+++ b/core/modules/views/lib/Drupal/views/ViewsDataCache.phpundefined
@@ -0,0 +1,195 @@
+    $this->skipCache = config('views.settings')->get('skip_cache');

Yeah if we put this into the constructor on the container this information could be used without a special config call.

+++ b/core/modules/views/lib/Drupal/views/ViewsDataCache.phpundefined
@@ -0,0 +1,195 @@
+    cache($this->bin)->set($key, $value);
...
+    return cache($this->bin)->get($cid);

What about injecting the cache object via the container?

+++ b/core/modules/views/lib/Drupal/views/ViewsDataCache.phpundefined
@@ -0,0 +1,195 @@
+    foreach (module_implements('views_data_alter') as $module) {
+      $function = $module . '_views_data_alter';
+      $function($data);

Just in general: is there a reason to not use drupal_alter?

Status: Needs review » Needs work

The last submitted patch, vdc.1867782-10.patch, failed testing.

damiankloip’s picture

Status: Needs work » Needs review
FileSize
5.39 KB
26.95 KB

Nice, thanks for the initial review, they are some good suggestions.

It now passes in the config factory from the container, Changed the getting of data to use drupal_alter (this was just what was in the current code), Removed the useLanguage flag - so it now always assumes a language, with the langcode being retrieved in the constructor.

Also added your suggestion for the view_ui_field_list() function.

dawehner’s picture

Great improvements!

+++ b/core/modules/views/views_ui/admin.incundefined
@@ -2286,6 +2286,7 @@ function views_ui_form_button_was_clicked($element, &$form_state) {
+  $data = drupal_container()->get('views.views_data')->get();

@@ -2298,12 +2299,12 @@ function views_ui_field_list() {
+            $table_data = $data[$item['table']];

We should better use ->get($item['table']) to not require to load all of the data all the time.

damiankloip’s picture

Good point, I'll work on this in the morning.

damiankloip’s picture

FileSize
3.93 KB
27.68 KB

Made that change, added some docs.

damiankloip’s picture

FileSize
10.3 KB
35.37 KB

OK, so tim.plunkett posted a patch (http://drupal.org/node/1635240#comment-6852750) with the views_fetch_data stuff removed, which passed. So I've merged that into here. I've also moved views_cache_*et function into views.module as there are only 2 now, and removed cache.inc :)

damiankloip’s picture

FileSize
35.41 KB

Oops, fixed the call to the container in EntityReverse class

tim.plunkett’s picture

FileSize
4.62 KB
36.18 KB

A couple nitpicks, but I just fixed them myself. I think @damiankloip said he wanted Berdir to look this over for some benchmarks, so I won't RTBC yet, but I like this patch a lot.

dawehner’s picture

This looks nearly perfect so far!

function views_block_info() {
// Try to avoid instantiating all the views just to get the blocks info.
views_include('cache');

views_include('cache');
// Cache for unpackOptions, but not if we are in the ui.

So there is no need for these two calls anymore

+++ b/core/modules/views/views.moduleundefined
@@ -1154,8 +1154,8 @@ function views_library_info() {
+  $data = drupal_container()->get('views.views_data')->get($table);
...
-  $data = views_fetch_data($table);

+++ b/core/modules/views/views_ui/admin.incundefined
@@ -2298,12 +2299,12 @@ function views_ui_field_list() {
+            $table_data = $data->get($item['table']);
+            if (isset($table_data[$item['field']]) && isset($table_data[$item['field']][$type])
+              && $field_data = $table_data[$item['field']][$type]) {
...
+              if (isset($field_data['field_name'])) {
+                $fields[$field_data['field_name']][$view->get('name')] = $view->get('name');

Nitpick: The empty line seems to make more sense in the current code.

dawehner’s picture

So basically tim fixed the stuff already :)

Let's get some proper benchmarking and then RTBC it.

tim.plunkett’s picture

Restoring tags.

Berdir’s picture

Status: Needs review » Needs work
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/relationship/GroupwiseMax.phpundefined
@@ -93,7 +93,7 @@ public function buildOptionsForm(&$form, &$form_state) {
       $sort_options[$sort_id] = "$sort[group]: $sort[title]";
     }
-    $base_table_data = views_fetch_data($this->definition['base']);
+    $base_table_data = drupal_container()->get('views.views_data')->get($this->definition['base']);

We're just switching one function call with another one so it's IMHO ok, but maybe open a follow-up issue to pass this information to the plugins which need it?

+++ b/core/modules/views/lib/Drupal/views/ViewsBundle.phpundefined
@@ -32,6 +33,16 @@ public function build(ContainerBuilder $container) {
+    $container
+      ->register('cache.views_info', 'Drupal\Core\Cache\CacheBackendInterface')
+      ->setFactoryClass('Drupal\Core\Cache\CacheFactory')
+      ->setFactoryMethod('get')
+      ->addArgument('views_info');

We have a bunch of these now but note that this is not really correct, because cache() does it's own object caching, so cache('views_info') and $container->get('cache.views_info') won't return the same object, which can be a problem depending on how the backend is implemented.

+++ b/core/modules/views/lib/Drupal/views/ViewsDataCache.phpundefined
@@ -0,0 +1,220 @@
+/**
+ * Class to manage and lazy load cached views data.
+ */
+class ViewsDataCache {

I can see that this works differently* than the current design of CacheArray/CacheCollector, wondering if we could use the same interface as that, though. We'll see.

* I haven't followed the original issue which put this in closely, but I do wonder how a cache_set() for each table performs on a site with 400 fields (which means 800+ tables in the views data definition). Wondering if we could do something similar and only store keys for tables that are actually accessed. Different issue, though.

+++ b/core/modules/views/lib/Drupal/views/ViewsDataCache.phpundefined
@@ -0,0 +1,220 @@
+  /**
+   * The views cache bin to use.
+   *
+   * @var string
+   */
+  protected $bin;

It's actually a CacheBackendInterface now, so should be named $cacheBackend and it's also not views specific in anyway ("the *views* cache bin"). This is the only thing that needs to be fixed I think. Apart from the fact that it doesn't seem to apply anymore :)

+++ b/core/modules/views/lib/Drupal/views/ViewsDataCache.phpundefined
@@ -0,0 +1,220 @@
+
+  /**
+   * Destructs the ViewDataCache object.
+   */
+  public function __destruct() {

__destruct() in the container is a bit problematic and can result in segfaults for frequently used stuff. It seems work in case of CacheArray or the path alias issue but did not work at all for lock/keyvalue.

Help me to get #512026: Move $form_state storage from cache to new state/key-value system in, then we can use the service terminator for this :)

I'll do some profiling once there is a patch that applies :)

damiankloip’s picture

Thanks alot for the initial feedback!

Sorry about that, I've just rerolled it now. I think #1862344: Combine the Views plugin managers getting committed broke it :)

1. That's a good idea, Created an issue: #1870970: Only get views table data for plugins that need it
2. I just followed suit with how other implementations were doing this in the core container, shall we fix this here or is there another issue to tackle this for all?
3. Yeah, I think I mentioned at the top of the issue, we will see how the CacheCollecter interface comes on then quite probably use that here.
4. Changed property to cacheBackend
5. I will definitely have a look at that issue and help if I can, if you say this will/could be problematic.

Anyway, you should be able to apply this for profiling now :)

damiankloip’s picture

FileSize
36.34 KB

Oh, you might be needing this too.

damiankloip’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work
Issue tags: -Performance, -VDC

The last submitted patch, vdc-1867782-26.patch, failed testing.

Berdir’s picture

Status: Needs work » Needs review
Issue tags: +Performance, +VDC

#27: vdc-1867782-26.patch queued for re-testing.

Berdir’s picture

2. There is an own issue: #1764474: Make Cache interface and backends use the DIC.

Performance looks nice! A whole bunch of function calls saved and replaced with much faster ones. See screenshots. Memory usage difference can be ignored I think, not even 0,1%.

+++ b/core/modules/views/lib/Drupal/views/ViewsDataCache.phpundefined
@@ -0,0 +1,220 @@
+  /**
+   * The cache bin to use.
+   *
+   * @var string
+   */

@var definition needs to be updated as well, I'd also say cache backend instead of cache bin.

Happy to RTBC once that is fixed.

damiankloip’s picture

FileSize
36.39 KB
991 bytes

Nice, that is good news! Thanks for doing that. Getting rid of all those include calls is a massive win :)

I have made those changes..

Berdir’s picture

+++ b/core/modules/views/lib/Drupal/views/ViewsDataCache.phpundefined
@@ -124,7 +124,7 @@ public function get($key = NULL) {
   /**
-   * Sets the data in the cache in for a cache key.
+   * Sets the data in the cache backend in for a cache key.

Hm, was the "in" supposed to be "bin"? Because in doesn't make much sense, neither in the old nor new comment...

damiankloip’s picture

FileSize
36.38 KB

HAHA, yep. You're right.

Berdir’s picture

Status: Needs review » Reviewed & tested by the community

Ok, I think this is good to go. Nice little performance improvement and code looks nicer as well.

Testbot needs to agree of course, but the last two patches where just documentation changes.

tim.plunkett’s picture

Awesome!

olli’s picture

One small confusing change is this

+++ /dev/null
@@ -1,158 +0,0 @@
-    }
-    return $cache;
-  }
-  // Return an empty array if there is no match.
-  return array();
-}

vs

+++ b/core/modules/views/lib/Drupal/views/ViewsDataCache.php
@@ -0,0 +1,220 @@
+      }
+    }
+
+    // Return an empty array if there is no match.
+    return $this->storage;
+  }

Is that ok?

damiankloip’s picture

Yes, because if nothing gets set to storage, it will be an array anyway, see the property declarations at the top of the class.

damiankloip’s picture

FileSize
437 bytes
36.33 KB

Was just talking to merlinofchaos on IRC, Let's just remove that comment.

olli’s picture

Ok, thanks. #39 looks brilliant.

webchick’s picture

Project: Drupal core » Views (for Drupal 7)
Version: 8.x-dev » 8.x-3.x-dev
Component: views.module » Documentation
Status: Reviewed & tested by the community » Active
Issue tags: +Needs change record

I always get a sad face when we remove useful wrapper functions in favour of drupal_container()->some('crap')->that('chains')->forever('and ever') but this is consistent with what we have done elsewhere.

Committed and pushed to 8.x. Thanks!

This will need a change notice, so moving to the Views queue to track that, per Jess.

webchick’s picture

Title: Replace _views_fetch_data with cache class » Change notice: Replace _views_fetch_data with cache class

Title change.

dawehner’s picture

Status: Active » Needs review
Berdir’s picture

Title: Change notice: Replace _views_fetch_data with cache class » Replace _views_fetch_data with cache class
Status: Needs review » Fixed

I always get a sad face when we remove useful wrapper functions in favour of drupal_container()->some('crap')->that('chains')->forever('and ever') but this is consistent with what we have done elsewhere.

Me too. My main problem is that those are relatively hard to find (maybe we can fix that with some sort of list of what services core provides, e.g. as a new topic in the api documentation about that whole thing?) and you have absolutely no type hinting in IDE's.

Crell's answer to that is that all that stuff should be injected, but we can only inject when we have services/classes/factories where the calling code knows what will actually be required. While that helps and was my suggestion here as well (which has been slightly misunderstood I think, will comment in the follow-up issue), it is not going to happen in 8.x for most of the code.

Change notice looks ok to me.

yched’s picture

It seems ViewsDataCache::get($key) will return the full array of $this->storage when $this->storage[$key] could not be found ?
It that the intended behavior ? Seems odd DX wise (you get a result in a different format when the key you requested doesn't exist / is invalid ?)

[discoverability of drupal_container()->some('crap')->that('chains')->forever('and ever')]
Crell's answer to that is that all that stuff should be injected, but we can only inject when we have services/classes/factories where the calling code knows what will actually be required. While that helps and was my suggestion here as well (which has been slightly misunderstood I think, will comment in the follow-up issue), it is not going to happen in 8.x for most of the code.

#1863816: Allow plugins to have services injected would help a bit with making more stuff injectable.

damiankloip’s picture

Yeah, it's a fair point. This patch was just a port of that logic into the new class (basically to remove the cache.inc file). It would probably not be ideal if you provided a base table like 'nobe' instead of 'node' and it returned ALL THE DATA. So you would only get everything if $key was NULL.

Happy to create a follow up issue for this?

Automatically closed -- issue fixed for 2 weeks with no activity.

Anonymous’s picture

Issue summary: View changes

Updated issue summary.