For some use cases, there is a performance gain to be had by statically caching the output of views_arg_load(), which I've implemented in the attached patch.
As an example, The National Interest has 17 menu links on the frontpage which point to views. 5 are in the main navigation (Security, Society, Economics, Politics, Global Governance). The remainder are in the footer of the website, under the 'Explore by Topic' and 'Explore by Region' headers. I would also like you to note that the menu items under the 'Explore by Topic' header are identical to the 5 in the main navigation.
In this instance, views_arg_load() is called 17 times, of which 5 of the calls are repeating work done earlier.
I've attached two screenshots from webgrind showing the performance gain by statically caching the output of this function. You will note that the total inclusive time spent in views_arg_load() drops from 29.06% to 22.84% of the total pageload time.
Not shown in the attached screenshots but also relevant is that the time spent in $view->unpack_object() is reduced from 79.47% inclusive time to 71.43% inclusive.
I confirmed these numbers with several profiling runs before and after the patch, and the numbers are fairly consistent, give or take .5% between all the runs I did.
Comments
Comment #1
brianV commentedOops - attached is a revision without the excess whitespace.
Comment #2
locomo commentedsubscribe
Comment #3
drewish commentedsubscribing.
Comment #4
longwavePatch works for me and similarly reduces time spent in views_arg_load() and unpack_options(). Also applies to 6.x-2.x with minor fuzz.
Comment #5
merlinofchaos commentedI like this patch.
The method of generating the cache key looks like it could lead to false positives because value and index look like they could accidentally come together to form numbers.
I know it is *highly* unlikely to happen, but that's what edge cases are for.
I recommend:
Then use $key in all the places the calculation is used.
The only place that : is a valid value is $value so you shouldn't get any weird combinations that can masquerade as other combinations.
Comment #6
longwaveThe $key change is included in this updated patch, plus a correction for the function comment block.
Comment #7
merlinofchaos commentedDefinitely RTBC now. Sadly out of gas for committing for the day, will try to get this one in my next burst.
Comment #8
merlinofchaos commentedCommitted to all branches.
Comment #10
hanoiiI was profiling a site on my own and while going through different points of where a lot of time was spent on views_arg_load().
Let me explain first my scneario:
It's an ecommerce site with a somehow big menu structure populated by taxonomy using taxonomy_menu, and because each of those taxonomy/term/xxx that gets into the menu a views_arg_load is executed.
By applying this patch, the keys I get are as follows:
And a lot more of course. views_arg_load it's being executed 180 times collecting around 3.x seconds of execution time which is a lot.
This patch does nothing to me, because the key are too specific and for each of this menu the key is new so no real cache is being made. Also, although view::load() caches the views, views_get_view only clones an empty view, so on my case, each time the function is execution the same view and same display is initialized every time by:
which in my case it doesn't make much sense, because the display and the view is exactly the same, so why set the display and initialize it on each run?
I have changed the key to
And the performance boost is incredible, it was reduced to 88ms.
So, although I have worked a little bit through views entrails and also with some external modules, I am not sure of the implications of this, but for the logic that's around the loaded views on this specific function, I think a wider key makes a lot of sense.
Even the following key would do it:
Because the set_display() will change it, but I think it's worth to cache each display on it's own.
EDIT: I even don't think this wider key would affect the cache in the way you were thinking it at the first point.
Thoughts?
I don't mind rolling a patch but it's really one line change, and while we are there, can we please commit this into 2.x as well, this is huge!
Thanks,
a.=
Comment #11
hanoiiI guess I leave the discussion for 3.x first, but really, it's cheap to commit to 2.x at the same time.
Comment #12
dawehnerCan you please provide a real patch?
This is not really usuable here.
Comment #13
hanoiiOk, sorry, being a one line I though it was easy to review. Attached is the patch for 3.x, applies the same to 2.x. I didn't realize the first patch was actually committed to the 2.x,
EDIT: Also bear with me if this is not an appropriate patch. Kind of new to git, only followed the instructions on the create patch page.
Comment #14
brianV commentedI think
$valueneeds to remain in the key, since the value that is stored in$views[$key]is dependent on$value:Comment #15
hanoiiYou are right, but then we are in the same scenario, I didn't realize that I was doing something wrong, I don't really want to cache the argument value, what I want to cache is the views already initialized and with the display set, actually you can cache both things really. Will submit and improved patch in a short while.
Comment #16
brianV commentedAh, I see where you are going with this. Not just caching function output, but also the initialized views. Gotcha - that would be a nice improvement.
Comment #17
hanoiiOk, attached is the improved patch. both things are cached.
With this approach, I realize I had to remove $views->destroy() in order for the cached initialized view makes sense. I wonder if there are implications with that but I really think not.
Of course I lost a few milliseconds as the previous patch was super fast because it was wrong, but still in the order of the 150ms, which is still a lot of performance gain.
Comment #18
hanoiiProbably an easier patch to review, just with git diff, I realized git format-patch includes every single commit as separate diffs, is that the proper way of submitting patches now?
Comment #19
brianV commentedOnly see one potential issue:
Is it necessary to set the display and init the handlers again if that is done prior to the view getting placed in the cache?
Why the time we reach this point in the code, this has been done in all possible cases.
Comment #20
hanoiiI lost the changes I did on the first time and I left those there the second time by accident, of course, you are right.
Comment #21
brianV commentedWell, looks good to my eyes, but I'll let someone more knowledgeable about views internals set it to RTBC.
I think this would actually decrease performance in some scenarios, however. The use case you presented is an ideal scenario for this tweak, but on the flip side, consider one where the menu contains 20 menu links each pointing to a unique view / display combination. In that use case, all this patch does is increase memory usage (by holding 20 initialized views in memory since $view->destroy() is not called) without any corresponding speed increase.
So the real question is, in the typical use case, does the performance benefit from this justify the extra resource usage in worst-case type scenarios.
Comment #22
hanoiiI understand what you are saying, we could always make this optional by a configuration, but probably not worth adding complexity into it.
Not sure what to say, it definitely made an impact on my use case, if I were to chose between memory consumption and page load, I would go with the latter, although I know eventually one will hit the other in the but I still think this might be useful to have it.
Comment #23
hanoiiI give this a thought while I was out, and if this becomes and issue or a stopper to maybe accepting this, there might be a way in hook_menu_alter() to decide whether to cache the views or not, depending on how many times an argument for a specific view/display will be loaded, I believe this might be known there, but not 100% sure.
Comment #24
dawehnerShort question, why do we store the same view, but for different displays?
The rest looks fine from my perspective! Great patch
Powered by Dreditor.
Comment #25
merlinofchaos commenteddereine: I think becuase args vary based upon things like the path in the display, so each view + display creates a unique combination.
Comment #26
dawehnerOh yes i was wrong, but i'm still wondering whether it would be possible to store just a single view for multiple displays.
Comment #27
donquixote commented[slightly off track]
In an ideal world, we would not load the view, but just do what is minimally required to do the access check, or to load the argument.
This could either be via existing loaders like user_load() or taxonomy_term_load(), or new dedicated loaders like views_taxonomy_term_load(), OR
by loading just a small part of the view - not as much as would be necessary to actually render it.
This would be a lot more work, I'm afraid..
There might still be cases where it is required to load the complete view, if we want to show a "page not found" if the view is empty.
We can still load the view in views_access(), if it turns out to be necessary.
But otherwise, we could probably get away cheaper than that.
[/slightly off track]
Comment #28
mustanggb commented