Currently, drupal_render and _theme call out to \Drupal's service methods to get services, every single time they are called. Since these are in the critical path and can be called many times, it seems worth moving them to services where we can inject the dependency once.
The attached patch is an *extremely* naive version of this. It adds a "Renderer" class that does what drupal_render did, and it adds _theme as a protected method to ThemeManager (since that's the primary/only caller).
The patch does NOT resolves all dependencies. It also doesn't introduce an interface yet, as the methods might change in the future once the conversion is done.
The performance improvement isn't huge, but if nothing else, it gets rid of a ton of profiling noise.
xhprof data below is for a view listing 100 nodes in an 11 column table.
This patch obviously needs cleanup, and both drupal_render and _theme would benefit from being broken up a bit, but I'm not sure it's worth doing all that here since this feels better than what's there now.
Comment | File | Size | Author |
---|---|---|---|
#40 | 2346937-40.patch | 98.47 KB | Wim Leers |
drupal_render_service_xhprof.png | 79.64 KB | msonnabaum |
Comments
Comment #1
Anonymous (not verified) CreditAttribution: Anonymous commentedlooks good to me.
Comment #2
Crell CreditAttribution: Crell commentedThis seems highly sensible to me. I also agree with pushing off most of the refactoring other than just the injection here; the patch is big enough and moving code around inside the Renderer class is not even remotely BC-breaking. Just the clearer injected dependencies is a win already for the classes that are calling drupal_render().
There's only a few tweaks I'd still make cleanup-wise for now:
This can be moved to an object property. Then the closures can reference it either by assigning to a temp variable or binding the closures to $this before calling them (which you can do as of PHP 5.4 so that $this works inside a closure).
Of course, those closures maybe can just turn into methods on this class then. That's probably a bit more refactoring than we want to bother with right now.
@see needs updating.
We can eliminate this part of the docblock as the method is protected.
Since we're introducing it here, let's go ahead and just inject it rather than calling out to the container. It's a plugin so it should still be injectable with the create() method.
Comment #4
larowlanThis is a duplicate, pretty sure dawehner had separate issues for both
Comment #5
msonnabaum CreditAttribution: msonnabaum commentedI figured it was, but I couldnt find the issues.
Comment #6
dawehnerThis is the issue larowlan mentioned, but seriously, let's be pragmatic here, let's try to work in babysteps.
Babysteps, here is a follow up: #2354691: Move static variables and closures out of Renderer
Decdided to eliminate it completly and point to self::render() as it contains the same documentation already anyway.
I am pretty sure you don't know what you are talking about :) We are talking here about the following few limited set of subclasses:
Wrong programming language, things aren't always that easy!
Comment #8
dawehneryeah, sucker!
Comment #9
dawehnerComment #10
claudiu.cristeaDoc blocks are always broken when copying procedural to classes. Also there were references to old procedural functions.
Comment #11
claudiu.cristeaOne more small catch. Interdiff is still against #8.
Comment #13
dawehnerI tried a little benchmark and it seems to be indeed a bit slower, maybe actually triggered by the problem that object method calls are slower than ordinary function calls.
It would though be sad if such things would play such a big role ... http://www.lionsad.de/xhprof-kit/xhprof/xhprof_html/?run1=543bf339ee873&...
Comment #14
Crell CreditAttribution: Crell commentedMethod calls are slower than function calls on the order of nanoseconds from the last time I saw that benchmarked. That shouldn't make a difference. Has the number of calls changed? That's usually the culprit when OOP looks slower than procedural code: The number of stack calls goes up as code gets broken up more.
Comment #15
dawehner@crell
No, this patch actually drops a couple of function calls ... see the link.
Comment #16
dawehnerReroll ...
@WimLeers
Would you be interested in actually getting this in? This could allow future improvements for drupal_render potentially easier.
Comment #17
rickvug CreditAttribution: rickvug commentedThat patch looks really nice. The more code refactored out of common.inc the better.
Comment #20
dawehnerTo bad that noone reviewed it in all that time, anyway.
Comment #21
larowlanTipping this might fail because of htmlfragment/htmlpage issue.
But will be lazy and get the bot to test it.
Reviews and more re-rolls to come.
Comment #23
larowlandoh
Comment #24
larowlanFixes some minor coding standards, added an interface, injected it into \Drupal\Core\Render\MainContent\HtmlRenderer
Comment #25
larowlanI've reviewed this as part of the re-roll, and I think its a great cleanup, I think it gives us more options later in the cycle too.
Comment #27
larowlanfixing fails now, missed a core.services change
Comment #28
larowlanactually, no I didn't - NodeController has a concrete new call to NodeViewController
and unit tests, should have run those first
this should be good
Comment #29
jibranmore then 80 chars.
more then 80 chars.
Comment #30
Wim Leersdawehner++ for the delightful sarcasm :D
I was already following #2239457: Move main complexity of drupal_render() into Drupal\Core\Render\Render, didn't know about this one yet.
Overall: +1, just like I said on that other issue. Another thing I said there:
.Concerns:
drupal_render_root()
, but then requiringRenderer::render($e, TRUE)
when using the Renderer service — we intentionally added a new function to make it easier to discern root calls. I think we should hence keep that for Renderer also, or at the very least have a very good reason to omit it.(Tested using the default frontpage with one extra block and a node.)
Other than that, I only found nitpicks.
s/string/HTML string/?
Also: "converts" versus "turning into", should be consistent.
Indentation for these
@code
tags seems off.Not
::render()
, but\…\Renderer::render()
.Comment #31
dawehnerValid point!
As said on IRC, in case we don't add an interface people also have to adapt the method renames ... so there is not a big gain, in not adding the interface now.
Let's keep it, we probably want one in the future anyway.
A one percent improvement is not too bad, given that there is maybe potential for a little bit more.
I would not trust my old crappy computer, to be honest, I really need a new one which actually works.
Found also a couple of things.
Comment #33
Wim LeersGood point.
Remember the situation msonnabaum described in the IS: a View with 100 nodes and 11 columns. That's a lot more
drupal_render()
calls right there. I think such a use case would indeed show a bigger improvement.Comment #34
Wim LeersRebased (straight reroll).
Only fixing a few docs indentation problems.
:) :) :)
:)
Comment #35
Fabianx CreditAttribution: Fabianx commentedRTBC + 1 - this is great!
Comment #36
dawehnerJust one of potential many follow ups #2378883: Convert existing drupal_render() KernelTestBase tests to PHPUnit tests
Comment #37
Fabianx CreditAttribution: Fabianx commentedThis is now a critical task as it blocks a critical bug that could only be solved sanely by creating a service like this:
#2364381: Exception thrown in drupal_render() causes an exception during the rendering of exceptions
After this issue is in, adding a resetStack method becomes trivial.
Comment #38
alexpottCan remove use for RenderStackFrame.
We should be saying that they will be removed before 9.0.0
Comment #39
alexpottGreat looking patch btw. sorry for the nits.
Comment #40
Wim LeersNits fixed.
Comment #41
alexpottOops I should have give you the exact thing to write...
@deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0. Use the 'renderer' service instead.
Will fix on commit unless this goes back to needs work - leaving at rtbc for a bit to get additional feedback.
Comment #42
alexpottThis issue is a critical task and is allowed per https://www.drupal.org/core/beta-changes. Committed da8ea3b and pushed to 8.0.x. Thanks!
Fixed on commit.
Comment #44
almaudoh CreditAttribution: almaudoh commentedGreat refactorings of D8 in the final stages. Awesome! :)
Comment #46
star-szrThis indeed looks good, thanks all! Since this removed
_theme()
, we are left with quite a few stale references in docs so I created this follow-up: #2388247: Documentation refers to _theme() function, which has been removed