Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
(Cacheable|Bubbleable)Metadata::merge()
always merges, instead of just adopting the values of $other
if $this
is empty.
This has quite a significant effect, because BubbleableMetadata extends CacheableMetadata
, and BubbleableMetadata
is what Renderer
uses to track/merge/bubble all metadata that needs to bubble!
Proposed resolution
Fix that.
Remaining tasks
None.
User interface changes
None.
API changes
None.
Comment | File | Size | Author |
---|---|---|---|
#48 | interdiff-2483433-48.txt | 11.32 KB | damiankloip |
#48 | 2483433-48.patch | 35.71 KB | damiankloip |
#46 | interdiff-2483433-46.txt | 846 bytes | damiankloip |
#43 | interdiff-2483433-43.txt | 18.65 KB | damiankloip |
#37 | interdiff-2483433-37.txt | 4.28 KB | damiankloip |
Comments
Comment #1
Wim LeersXHProf,
/node/1
Minus 3866 function calls! Or 4.3% less function calls overall!
Benchmarking,
/
, UID 1,ab -n 200 -c1
Slightly faster:
Comment #2
dawehnerSo yeah I was wondering whether we could optimize for ages as well.
Comment #3
Wim Leers#1 brought it down to 86,298 function calls, and was identical to what I did in #2335661-137: Outbound path & route processors must specify cacheability metadata. This brings it down to 84,748 function calls.
Comment #4
Wim LeersRedoing the benchmarks with patch #2.
Benchmarking,
/
, UID 1,ab -n 200 -c1
Slightly faster:
Comment #5
Wim LeersClarified why the impact is so big in the IS.
We should do something similar for
BubbleableMetadata::merge()
in a follow-up. Or perhaps here?Comment #6
dawehnerI love how diagrams replaced readable data. Let's better add a pie chart as well ;)
Comment #8
alexpottI'm guessing the performance benefits are due to not doing:
However this is the only place we do this so are we now skipping this completely for some contexts?
Comment #9
Wim Leers#8: No, that's just a nice additional benefit. The number of
array_merge()
,array_unique()
andsort()
function calls saved is also quite large.Will answer this question better by doing another round of profiling.
Expanding scope to what I suggested in #5. And then checking performance again.
XHProf,
/node/1
Minus 7203 function calls! Or 8.0% less function calls overall! Measuring the wall time difference with XHProf is difficult though.
The differences:
Diff%
Diff
(microsec)
Diff%
Diff
(microsec)
Diff%
MemUse
Diff
(bytes)
Diff%
MemUse
Diff
(bytes)
Diff%
PeakMemUse
Diff
(bytes)
Diff%
PeakMemUse
Diff
(bytes)
Diff%
Benchmarking,
/
, UID 1,ab -n 200 -c1
Saves ~2 ms/request at
/node/1
.Comment #10
Wim LeersAnd I want to stress this again (quoting the IS):
That is the main reason why the savings are so significant.
Comment #14
Wim LeersTestbot frustrations++
Comment #15
catchI think we can shave off some more function calls - since both $this->whatever and $other->whatever can be empty.
I tested this vs. #9 on the front page/minimal profile/no page_cache/logged out, and saved an extra 294 (see xhprof diff - the run1 is with the above patch applied). More verbose though.
No interdiff since all the lines change.
Comment #16
Wim Leers#15: I tried that too locally, but deemed it not worth it for similar reasons: 1) it only presents a very small incremental gain, 2) it makes things much harder to read. My version used double ternary operators. Interdiff against #9.
Comment #17
Wim LeersI'm completely fine with #15, BTW. @amateescu said in IRC that he thinks #15 is very clear/understandable. Which is true.
Thoughts from others?
Comment #18
Fabianx CreditAttribution: Fabianx for Acquia commentedI like #15, too.
Still need to answer when we would actually validate cache contexts then ...
Comment #19
dawehnerWell, this now potentially moves the validation to the first place where you have two cache contexts at the same time ... given that I'm wondering whether it should be moved to cache set instead?
Comment #20
Wim LeersIMO changing whether validation happens here is out of scope here. This patch currently has basically zero DX impact.
Excluding merging from validating cache contexts would impact the DX, because exceptions thrown there would make it harder to track down the code responsible for setting an invalid cache context (the stack trace would be useless).
Comment #21
dawehnerFair at least #9 doesn't, if we check both the right and left side, the merging could happen later.
Comment #22
Wim Leers#21: I realized that after I'd posted #20 too. So, yes, the patches here do affect the DX (i.e. the stack trace will be less helpful), but doing #19 would make the stack trace almost completely useless. Or am I seeing that too bleakly?
Comment #23
catchIt'd be even more verbose, but we could validate individual contexts if either is set, but without merging unless both are set?
Comment #24
Wim Leers#23: True. That'd keep the DX at absolute parity. But would also reduce the gain in number of function calls, of course. Then again, reducing the number of function calls for validating cache contexts is being addressed by #2408013: Adding Assertions to Drupal - Test Tools..
Comment #26
Wim LeersSo what do we do with #23?
In the mean time, a reroll to fix the sole test failure.
Comment #27
plachSlightly related issue: #2488296: Improve post render cache placeholders generation performance.
Comment #28
Fabianx CreditAttribution: Fabianx for Acquia commentedNeeds work, we need to decide on what to do with #23 / #8 still ...
Comment #29
Wim LeersOnce #2254865: toolbar_pre_render() runs on every page and is responsible for ~15ms/17000 function calls lands, the number of function calls this is able to remove should be reduced a fair bit, because we'll be doing less rendering on each page load.
Comment #30
Fabianx CreditAttribution: Fabianx for Acquia commentedOn #8 + #23:
I think we need two modes in the end:
- Production mode - where performance trumps
- Debug mode, where you can exactly see what is responsible for the fails
Probably assertions would take care of that.
Comment #31
Wim LeersSounds good. But what's the current status/likelihood of assertions?
Comment #32
catchI think for now I'd keep the DX parity here (i.e. validate on set) - and assume that we'll get assertions in before 8.0.0 to further optimize the validation.
If we don't get assertions in, we could always add a follow-up if we think the performance gain is more important than the DX. But then we might as well use assertions at that point.
Comment #33
damiankloip CreditAttribution: damiankloip commentedRerolled and made another change to add mergeAttachments to BubbleableMetadata. I spoke to catch and we couldn't see a good reason this was callout out, and needing to be overridable. As the renderer service uses BubbleableMetadata directly, so another implementation if it wanted another implementation would do that there instead anyway?
A quick benchmark looks a bit like this,don't take these figures as gospel please:
UPDATE: Just spoke to Fabian too, he said he spoke to Wim and they came to a similar conclusion too. That sounds like playground stuff, but that's how it happened :)
Comment #34
dawehnerNice! Makes also things easier to follow
We don't have any test coverage for that function? I would have expected that things fail
Comment #35
damiankloip CreditAttribution: damiankloip commentedI didn't look at the tests yet, So I would expect stuff to fail. I was just testing this out.
Comment #36
yched CreditAttribution: yched commented+ a lot, felt weird to have BubbleableMetadata::merge() call out to \Drupal::service('renderer') to deal with #attached
Does the new BubbleableMetadata::mergeAttachments() still need to be public ? ::merge() is the real thing, right ?
Comment #37
damiankloip CreditAttribution: damiankloip commentedI think we might want to keep the mergeAttachments method on the renderer and interface, seems a few things use this directly. It can just call out to the new static method I guess? I just copied the docs do BubbleableMetadata::mergeAttachments and RendererInterface::mergeAttachments have the same docblock atm.
EDIT: sorry, messed up the interdiff :)
Still didn't look at test coverage changes.
Comment #39
Wim Leers#33: +1!
I think we want to remove
RendererInterface::mergeAttachments()
altogether — it is just polluting that interface IMO. It made sense at the time, but everything in the render system has been improved structure-wise.Almost nothing uses this in core, definitely not in contrib, so disruption will be minimal.
Comment #40
dawehner\Drupal\system\Tests\Common\MergeAttachmentsTest
is the test coverage you are looking for.Beside from that call, there is 1 single proper function call, I think its fine to change the API?
Comment #41
Wim Leers#40: Yep to both of those.
Comment #42
catchI think the API change here is fine. If we add a bc layer that nobody uses then it's instant dead code, which just makes things harder to maintain/follow with no benefit.
Comment #43
damiankloip CreditAttribution: damiankloip commentedLet's see how this gets on then.
Removed mergeAttachments from the interface, replaced the 2 usages outside of the renderer, moved the tests to BubbleableMetadataTest, so DrupalUnitTest > UnitTest - nice.
EDIT: It was a lazy man's interdiff. So the delete of MergeAttachmentsTest.php is not in there, but is in the actual patch :)
Comment #45
damiankloip CreditAttribution: damiankloip commentedOh, placeholders.
Comment #46
damiankloip CreditAttribution: damiankloip commentedI think postRenderCache just needs to go?
Comment #47
dawehnerNot a bad improvement. This is
/admin/content
without views render caching for anonymous user (with page cache disabled)Sounds like a good usecase for a
@dataProvider
? And maybe dedicated test methods ...Comment #48
damiankloip CreditAttribution: damiankloip commentedmit data providers
Comment #49
dawehnerNice descriptive test methods. Thank you!
Comment #50
alexpottThis issue is a major task that will improve performance significantly and the disruption it introduces is limited. Per https://www.drupal.org/core/beta-changes, this is a good change to complete during the Drupal 8 beta phase. Committed dc53be5 and pushed to 8.0.x. Thanks!
Comment #52
Wim LeersSeveral significant loose ends were left here, resulting in both dead and inconsistent code. Created #2505171: Follow-up for #2483433, with a patch, to fix that.