This is very preliminary but I figured someone might be bored over christmas and could provide some insight to me before I go digging into this too much.
Trying to debug site performance issues, I turn almost everything off and enable xhprof. Slowest function is t which was called 2000 times from system_region_list. That function was called 100 times by... drumroll.. context
The biggest culprits were
context_reaction_block::is_enabled_region (61 times) and
context_reaction_block::is_editable_region (40 times)
the 30 calls to system_region_list accounts for 30ms or 1/10 of the page load time.
Can the results of this function be cached by context?
| Comment | File | Size | Author |
|---|---|---|---|
| #21 | 1873450-cache-system-region-list-d8.patch | 1.57 KB | john morahan |
| #20 | after.txt | 1.52 KB | john morahan |
| #20 | before.txt | 1.52 KB | john morahan |
| #10 | 1873450-cache-system-region-list-2.patch | 1.85 KB | john morahan |
| #7 | 1873450-cache-system-region-list.patch | 1.85 KB | john morahan |
Comments
Comment #1
joelcollinsdc commentedno idea if this is the right approach. this didn't same me the full 30ms i was expecting, i got about a 20-25ms performance bump.
Comment #2
joelcollinsdc commentednot sure if this is more or less right that the other way. seems more drupaly to me.
Comment #3
joelcollinsdc commentedhttp://drupalcode.org/sandbox/joelcollinsdc/1873888.git/commitdiff/9b181...
Not sure how to make a patch out of that.
Comment #4
john morahan commentedI just ran into the same issue, and arrived at a similar patch; however, I was just thinking - would it be better to patch system_region_list() itself? That way we could cache for both REGIONS_ALL and REGIONS_VISIBLE while iterating over the list just once.
Comment #5
john morahan commentedSomething like this, perhaps.
Comment #7
john morahan commentedComment #8
technicalknockout commentedSounds like this could improve the performance of the the core function, but could this be over-engineering the solution since it adds more complexity with a new layer of caching? It also sounds like it doesn't address what could be an inefficiency in the context module - does that module need to make 100 calls to this function in the first place?
Comment #9
john morahan commentedWell no, it doesn't *need* to in the sense that context could cache the result itself, as joelcollinsdc originally proposed. But putting the caching in the core function improves the performance regardless of the caller. It's called three times on a normal page view in vanilla core anyway.
Comment #10
john morahan commentedI think the performance of that first call can be improved, too.
Comment #11
technicalknockout commented@joelcollinsdc - maybe you could open up a new issue for the patch to the context module. It seems to me patching context would definitely be helfpul, but might be worth looking into why all the calls are being repeated in the first place and addressing there.
@John Morahan - the original function returns an empty array if the theme_key isn't set, where as the patch doesn't handle that condition. I think something like
else {return array();}at the end would do it. unless you meant to change this?Comment #12
joelcollinsdc commentedI think patching in core would be sufficient if there is buy-in to make that change. I didn't attempt to do it since it seemed that it was context that was abusing an uncached function, but if core even calls it 3x on a regular page load, yea it probably makes sense to cache it.
Comment #13
john morahan commented@technicalknockout the original function returns an empty array if $themes[$theme_key] isn't set (where $themes is the return value from list_themes()). The patch first checks if $list[$theme_key] is set (where $list is the static cache variable), then later it initializes $list[$theme_key] to a pair of empty arrays, checks if $themes[$theme_key] is set before trying to populate them, and finally returns one of them at the end. So the overall behaviour should be unchanged. Or have you observed otherwise?
Comment #14
technicalknockout commentedyes, I see it initializes two empty arrays for keys REGIONS_ALL & REGIONS_VISIBLE, but there is nothing that restricts $theme_key from being passed in as something like $theme_key = 'nonsense'. In this case, the check
<?php if (isset($themes[$theme_key])) ?>returns false and $list[$theme_key] remains unset. At the end the code still will attempt to reach into the uninitialized $list[$theme_key][$show]Comment #15
john morahan commentedNope. $list[$theme_key] is initialized before isset($themes[$theme_key]) is checked:
Comment #16
john morahan commentedIt would indeed fail if $show was something other than REGIONS_ALL or REGIONS_VISIBLE, but as the API doc explicitly states "Possible values: REGIONS_ALL or REGIONS_VISIBLE", I don't think this case is worth worrying about.
Comment #17
technicalknockout commentedSure, it's documented, but it is not enforced and the current behavior is to return an empty array. Maybe it's unlikely, but this change could cause a disruption for existing sites if for some reason they weren't following the API documentation. Is there an advantage to changing how this edge condition is handled currently?
Comment #18
john morahan commentedFair point. However, if people are happy in principle with addressing this in core, it would have to go into D8 first anyway, in which case it hardly matters how consistently we handle misuse of the API (though worth keeping in mind for a backport, obviously).
Also I see drupal_flush_all_caches() calls drupal_static_reset() in D8, so the explicit resets should not be necessary there.
I'll reroll it later.
Comment #19
technicalknockout commentedanother point just thinking more about it (still in reference to D7, haven't looked at D8 at all on this yet) - this function does really little work. The work is done by list_themes and it caches its results internally already. Did you see a performance gain before & after the patch?
Comment #20
john morahan commentedYes, though obviously it's more significant when the context module is involved (see joelcollinsdc's 30ms improvement right at the top; I got even better results), there is a slight improvement even with just core (total inclusive time for that function according to xdebug is 0.997ms before, 0.435ms after).
Comment #21
john morahan commentedd8 version, untested
Comment #23
MixologicI just bumped up against the 2000 t's issue in my own xhprofs, searched in the context queue, found this: https://drupal.org/node/1688330
Definitely the right way to fix this is a drupal static inside of the system_region_list function.
The current behavior of system_region_list is to respond with all regions if there is no second argument, or if the second argument is "REGIONS_ALL". Any other second argument passed to system_region_list is going to behave as if "REGIONS_VISIBLE" was passed. So, to address @technicalknockouts concern, you could simply return
Ideally this wouldn't be an argument dependent function at all and ought to be factored into two separate functions that each cache their static results like system_visible_region_list($theme_key) and system_region_list($theme_key);
To keep a backwards compatible API you could still allow for system_region_list to forward to system_visible_region_list if anything other than REGIONS_ALL is set for $show.
Comment #24
joelcollinsdc commentedAre there modules other than context that call this function many times? If not, maybe this makes more sense to move back to the context issue queue...
related/duplicate: #1688330: Reduce number of calls to system_region_list
Comment #36
quietone commentedThere has been no activity here for 9 years.
Is this still a problem?
If you are experiencing this problem on a supported version of Drupal reopen the issue, by setting the status to 'Active', and comment on what still needs to be done.
Thanks!
Comment #39
mstrelan commentedPostponed as per #36
Comment #40
smustgrave commentedSince there's been no follow up going to close out. If still a valid bug in D11 please re-open
Thanks all