-
Notifications
You must be signed in to change notification settings - Fork 316
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
Performance issues when setting the "active" pseudoclass #293
Comments
Hm, this one is a bit tricky without having RML and stylesheet to test with. What I can say is that the call tree you list is pretty normal. When you change a class or pseudo class we need to figure which style rules apply to the newly changed element definition, including its children. So this line of updates I would say is expected. On the other hand, the high CPU usage is unusual. From my experience, more CPU time is usually spent on layouting rather than the definition updates, so I'm surprised that you see a lot of time being spent there. Would you say you have a lot of elements? Or complicated style sheets? If you e.g. have a lot of hidden elements, we still need to update the definition on all of them, so that may be costly. If you are able to make some RML+RCSS example I can reproduce this with on my end, I would be interested to take a look at that and see if there is anything funny going on. |
It will be a little difficult to set up an RML+RCSS example, but maybe these values will help. My best guess at what is happening is that with every tap/click the whole hover chain is copied to the active chain with each element receiving the "active" pseudoclass. This dirties the definition, on the next Update() call the properties are updated and we run through the stylesheet nodes to see if anything applies. With every node the check consists of string comparison iterating over a vector of strings for classnames and pseudoclassnames. I think it's a case of loads of vector iteration and string comparison, and if that's the case I think you might be able to pick up a noticeable difference when profiling even for a reduced example. My case is probably solvable with an RCSS refactor, I think I can bring that 84 nodes down quite a bit. I'm not convinced that will be enough though. I noticed that As a repro, if you have something at the ready that you can profile I think you can see this happening by:
If my take on this is right that will set and unset |
I agree that For the record, here is some general advice to speed things up if you experience performance issues with element updates:
If we can eliminate some of this advice by speeding things up on the library side I'm all for doing that. |
Thought about this some more, and I came up with different approaches. There are two more approaches that I haven't tested but I think may work:
|
Adding another update to this to clarify that the CPU usage seems to only be disproportional when debugging. Testing still shows a CPU usage reduction from ~20% to ~15% with the added hashed class list when tapping repeatedly. Have you been able to repro any of this when profiling? |
Good job on the improvements. I did some benchmarking, and I can see that there is indeed a performance issue for anyone using a lot of class-only style rules with no tags or IDs, combined with a lot of elements to update. I suspect that is exactly your case. Here is a test where we toggle a pseudo class on an element and run
There is one metric that particularly stand out, namely the So, yeah, we certainly need to fix this as I suspect this is not an all too uncommon use-case. There is a potential 10x performance gain here for the benchmarked use case with a better lookup strategy. You are certainly on to something there, I'll tinker more with trying different strategies. We should also add some more unit tests to make sure we don't accidentally break anything, there are already some tests for style rules, but we need something more rigorous. |
Nice! To throw it out there: perhaps it's possible to replace |
I guess the main issue is hash collisions. Under the assumption that most rules do not match we might get an improvement if we check hash first and then the strings. However, with a better class lookup that assumption might break and possibly be a performance pessimization. Anyway, something to consider for sure :) Actually, I don't think we handle hash collisions as it is during the cache lookup right now. |
… style definition, see #293. - Particularly, performance improvements when using style rules with class names. - Also fixes hash collisions in tag/id or node lists, previously they could result in the wrong styles being applied.
Hey, so I've made some changes (for now in the I've reworked the index a bit. Now we have multiple style cache indices for requirements in prioritized order, including one for classes. I too used the strategy of indexing the first class name. I also tried to add the pseudo classes to the index. I'm not entirely sure about this one though, since in practice they are not really that unique (usually just a handful ones like :hover and :active) and are typically combined with classes or at least tags. Could you try and see if you get any difference with and without the pseudo class index (added in a separate commit)? If you don't, then I'd feel more comfortable removing it. Here is a detailed benchmark before and after implementation of the new style index, and separately with the pseudo class index as well.
So now lookup of classes are basically as fast as looking up ids or tags. What's more is that I've also made sure that we handle hash collisions correctly now. This did cost a bit of performance, but with some other minor improvements we ended up pretty much the same as previously (or slightly better) for the non-class lookups. |
Thank you for this! It seems to do great for my case. Do let me know if you'd like me to test the pseudoclass case. I agree with your logic that we probably won't need it as the selectors are most likely tied to other identifiers. |
Ah, those numbers are great to see! Thanks for testing. The pseudoclass change is at the head of the |
I see a slight improvement of around one percent when taping quickly, probably not worth the extra work we do in all other cases. This is very helpful though, thank you! |
… style definition, see #293. - Particularly, performance improvements when using style rules with class names. - Also fixes hash collisions in tag/id or node lists, previously they could result in the wrong styles being applied.
Okay, I decided to drop the pseudo class index then. Now it's all merged to master as well, so I'm closing this issue as it seems we have a substantial speed-up. :) Of course, please let me know if there are any related issues such as wrong styles being applied. |
I've noticed that on my application there is a massive performance impact when setting the "active" pseudoclass in
Element::ProcessDefaultAction(Event& event)
.More specifically setting the class and removing it again triggers a cascade of update calls through the DOM tree, calling through:
Element::Update()
Element::UpdateProperties()
ElementStyle::UpdateDefinition()
StyleSheet::GetElementDefiniton()
StyleSheetNode::IsApplicable()
...where it checks the classnames and pseudoclassnames.
For a little more context I am using the lib on a mobile device, so this is happening in response to touch events - repeatedly tapping the screen drives CPU usage up to 50-60%. Not setting the pseudoclass brings CPU usage down to 8%. I think to repro this you would need to click pretty wildly, but worth having a look into!
Afaict this is being caused by a massive number of calls into
IsClassSet()
(not so muchIsPseudoClassSet()
but I guess that's because they appear less in my StyleSheetNodes) when it runs through all theStyleSheetNode
s multiple times in an update call.The text was updated successfully, but these errors were encountered: