New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FEATURE: Fusion performance optimization (lazy Component props) #2738
Conversation
If changing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this change makes total sense and i would be fine with the ArrayAccess implementation. This still has issues in combination with @apply since this leads to nested LazyProxy sitautions. Those should be handled and tested aswell.
Example:
prototype(Vendor.Site:Example) < prototype(Neos.Fusion:Component) {
applyValue = "example"
applyError = Neos.Fusion:Fail
renderer = afx`
<Foo:Bar applyValue={props.applyValue} applyError={props.applyError}/>
`
}
prototype(Foo:Bar) < prototype(Neos.Fusion:Component) {
applyValue = null
renderer = afx`
<Foo:Baz {...props} />
`
}
prototype(Foo:Baz) < prototype(Neos.Fusion:Component) {
applyValue = null
renderer = afx`
:: {props.applyValue} ::
`
}
Es expected the path applyError
will never be evaluated which is good but also
applyValue
will not end up in the Foo:Baz
component which it did without the pr.
Thinking about this further i come to the conclusion that @apply will have to learn how to deal with a lazy proxy. |
@mficzel Good point, I'll add a test to cover that case and try to fix it in the Runtime |
Would be great to make @apply support generic objects that implement ArrayAccess. That would allow to use DomainObjects for that in addition to the lazyProxy. |
@mficzel I think we'd need to change the way apply works by having some kind of chain (evaluate key -> isset on apply value 1 -> isset on apply value 2 -> isset in Fusion configuration), which might make things slower again... And we'd loose the possibility to enumerate/set all defined keys for array based implementations unless |
@mficzel This is what I did in aa168b7:
Actually we could use |
I really like the idea to use |
/** | ||
* @Flow\Proxy(false) | ||
*/ | ||
final class LazyProps implements \ArrayAccess, \Iterator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to add this as LazyEvaluationProxy
to the Fusion/Core? That would allow to use it in other places aswell.
As this is no @api yet it also makes sense to think about moving it later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about it too, but this is rather specific for holding an array of keys and defering the evaluation of these. I'm not sure where and how we would re-use this. By only checking the interfaces in Runtime we always have the chance to extract a common class or something else later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:-) i now have a usecase for that. Evaluating only the current page of a multipage form. But since i the fusion-forms shall support Neos 4.3 i will copy that code anyways but thanks for the inspiration.
@mficzel It's now implemented like this. The failing test comes from UriBuilder type hints that are not reflected in the Fusion objects (null vs. empty array). Is somebody fixing this? |
This links to the wrong change (because differetn repository), this neos/flow-development-collection#1871 makes more sense |
@albe should be put this also into 5.2? |
Did not read through all the conversation, so I'm not sure if this is ready. But if you say it is, I'm happy to pull it into 5.2 now :) |
@albe I'd say it should be ready |
@mficzel can you take another look and/or remove your veto? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good codewise, not sure about the breakyness of added typehints in Fusion ComponentImplementation
@@ -51,37 +52,32 @@ public function evaluate() | |||
* @param array $context | |||
* @return array | |||
*/ | |||
protected function prepare($context) | |||
protected function prepare(array $context): array |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: Is this supposed to be extended/inherited and hence the added typehints to be considered breaking?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not @api and no intended extension point. We only seperated this into a special method so we can address this in aspects seperately.
So for me this is ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Finally tested again and could not construct a fusion problem with this approach any more. All nesting and rendering works as expected and i could not come up with an edge case to break this.
However there are two issues when debugging these new proxies that would be a problem for developers:
{Json.stringify(props)}
>> returns empty (could be fixed by implementing JsonSearializable)<Neos.Fusion:Debug>{props}</Neos.Fusion:Debug>
>> runs into errors as the debug cannot be generated
I could force both by using {Array.concat(props,[])}
instead of props
but this is not a solution we can really recommend.
I have no idea how the second issue can be solved but it should be somehow.
@hlubek if you have an idea how to solve the debug issues i would really like to get this in.
|
||
public function offsetSet($path, $value) | ||
{ | ||
// NOOP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't that throw?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean the "set call" should throw an exception? Yes, we could do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think trying to set a lazy prop should throw :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, let's do that IMO. Otherwise looks good :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@albe It's a good idea to resolve all conversations before hitting the merge button. Otherwise open discussions might get lost – like this one.. :)
👍 Love to hear that ;)
Good idea, that should be easy to implement.
Maybe we can also use a test for |
Sounds smart, but i am not sure this will be accepted that easy as a json representation in the debug can hide things that were previously visible. Wouldnt it make more sense to make Debug use Iterator and ArrayAccess. We already have an exception for ArrayObject and DoctrineCollection here https://github.com/neos/flow-development-collection/blob/master/Neos.Flow/Classes/Error/Debugger.php#L200-L203 |
@hlubek i think a have found a more generic approach for the debugging problem neos/flow-development-collection#1980 that would only leave the jsonSerialize open for this pr. |
@mficzel Thanks for the investigation regarding debugging. I added Should be good to go now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like, thanks for this pr
@albe @bwaidelich i think this is ready |
Thanks everyone! Very nice feature :) |
Thanks for reviewing and taking care! |
starting with PHP 7.4 it's not cool to do offset access on integers (which returns null always) I did a little digging, and we have this skip keys logic 3 times in the runtime (probably not extracted for performance reasons) 2 times this logic needs to be adjusted to only operate on strings. Those two snippets where introduced with neos#2738 And 1 time this logic already has an is_string check. https://github.com/neos/neos-development-collection/blob/046e5d02750af0ebf33cc52663799ce09683ba37/Neos.Fusion/Classes/Core/Runtime.php#L667 This was adjusted when 7.4 compatibility was introduced via neos#2804 but the other two references where not adjusted
What I did
props
renderer
, it's quite probable that not all props are used - so a large amount of unnecessary evaluations could be performed (we measured that to be in the range of 20-30% in a larger project)How I did it
LazyProps
object implementingArrayAccess
that evaluates the actually accessed props lazily (and caches the results)How to verify it
array
type annotations are used forprops
)Checklist
Resolves: #2793