Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Feature: improved diffing #103
The tests failed as it is unable to download Meteor properly grrr .. @mitar, could you handle this at your earliest. I'd like to make an announcement on the Meteor forums as it will give an impetus to Blaze that it is getting some serious improvement in performance.
So, this is in fact more related to the #68. It is known that comparison function can be improved, but it is unclear what is the good one for every case. Because there is a trade-off between doing comparisons (you need CPU cycles, especially on large objects, which can even potentially have cycles) and not, but then doing diffing at some other layer.
For example, doing diffing on data layer can be potentially much larger than doing diffing on DOM layer because results DOM often contains only a subset of initial data.
What exactly is the right approach is a question. For example, some projects are doing completely different thing, where they use reference equality for everything and have immutable data. So if data is immutable and reference is equal (very quick comparison), then you know that data is the same. If references are not equal, you see them as different data. Again, the idea here is that comparison on data are expensive so it might be better to do diffing on some other layer.
Blaze by default is pretty conservative here. It compares only primitive values and the rest leave to DOM layer (middle ground between deep comparison and reference comparison). #45 talks about the idea of improving the DOM layer. And #68 about the idea of improving the equality function.
What I think we should do in the big plan is (#75):
This would allow developers to tailor their Blaze use to their particular need. Maybe their optimization point is really data, so they can to diffing there. Maybe it is DOM, because it is too much data to diff. Or too many reactivity things which get triggered anyway.
BTW, besides diffing there is also caching. I use computed field for that. The issue is that if you have three reactive sources inside autorun and one of them changes, both of those three reactive sources are invalidated and recomputed, instead of two of them returning simply the cached previous value. This is again something I think developers could then do at any granularity they want with the above changes.
I think the current Blaze default is a good conservative equality. But I think we should approach the question of equality in a more general way than trying to modify the default to one or the other special cases.
So here is the challenge. With the current approach, the 'changed' objects trickle down to be compared with what is actually in the DOM, field by field. The comparison is far from perfect and converts content to DOM elements to compare them.
So the current approach is actually very heavy on CPU cycles. Using a comparison function that does it at the object level is much faster (which may partly explain why I am seeing improvements in performance)
I am going to raise a concern here. You have very ambitious plans for Blaze (e.g. different materializers). Who is going to maintain all this. I strongly recommend we keep it simple-stupid (KISS principle) and focus on improving it, and adding higher level features useful to developers that you listed in the guide. This is what is needed to keep Blaze alive. If we have developer time and interest, we can go deeper into Blaze and start making improvements and allow overriding etc.
No, purely visual. Lagging on mobile then smooth scrolling on large list (same on desktop, but less noticeable unless you have a very large list).
I knew that if I made changes to an element it would freeze a bit, that no longer happens. I know it's not scientific, but technically, we can see the problem when tracing the code and observing results.
So you made a pull request for a thing which was not discussed in advance, and which changes one of the fundamental properties of Blaze. As such you must realize that getting this change is a longer process. Moreover, as I described on the forum, it is also unclear that optimizing Blaze for one particular load improves it for all types of loads, or existing types of loads. So this can be potentially backwards incompatible change which will make some other apps slower. I have not seen any quantifiable data confirming that it works better than "it feels better on our apps" statement.
Let me quote from the forum:
So I understand that this fixes your particular need now, but I am trying to look for a larger picture of Blaze. Getting something into Blaze should work for all, and should be well architectured.
This is why I asked you, before you started working on this, to look if incremental DOM/virtual DOM can be integrated with Blaze. Because that type of optimization can be done independently from other changes in Blaze. For this change, I think the order of things should be different, as planned in the roadmap, and this is:
So I completely agree that this is a problem. I wrote about it more than one year ago. What I do not believe is:
I appreciate your work and that you investigated this well, but because it is not something which was planned, it will require also from me to look deeper into claims you are doing and try to understand them better. I also have to do some my own tests because I would like to understand better what is happening about things you are observing. So, I hear what you are saying, I am just not sure that this is the best solution to the problem you are observing. It might be, but it is still unclear to me. And it will take time to analyze this better.
Again, it is just something which is out of order and as such it requires more work.
If you want to help, you can in meantime investigate (maybe you already did) why looping in Minimongo does not work as expected. So we all agree that if one document is inserted in the middle, we would want only that DOM element to be added there and this is it. Why is this not happening? Could
It would be great if we would have example instrumented app to all look into and use as a baseline.
Thanks @mitar, I was only asking as it was left in limbo on the forum https://forums.meteor.com/t/community-involvement-setup/29538/15
Essentially our goal was not incremental DOM per se, it was to improve rendering and avoid wasted calls to DOM. I don't think our solution is optimal, but it is very good with the constraints around it.
Look forward to you completing your review, in the mean time, I'll try to see if I can invest time in this:
It's likely a Tracker / diff issue (something MDG seems to have known about, and so have you)
As far as urgency, there is a message to the community: "Blaze is getting love and it's getting better!"
This could be a temporary fix.
No, this is not what is known. It is known that if a document for a particular entry changes at all, that one will be rerun completely, because default equality for data context is conservative. But this for insertion is something new, at least to me. And is not how it was documented in the past. So if this is happening, I would see this as a bug, maybe even regression. And I would like to know where it is coming from. It should work both for arrays with
What I think we have to get to is:
I think nobody in Meteor community expects "be quick and break things" approach to development. In any case, I do not see that as a good reason to not do things properly. I have also not seen 1000 upvotes or comments to quickly merge on this pull request, so I do not see any urgency from others either.
I know that when you make a pull request you feel amazing and would love to get things merged. But again, when you make a pull request which was not discussed before, then you have to take responsibility to hold your own horses. This is on you.
Yes, this is why I am proposing that we do comparison with previous value of HTMLJS for that object. So instead of converting to DOM elements, we compare HTMLJS itself. And only apply those changes which we find different between HTMLJS values.
Again, this is what I am saying that could be an interesting optimization which would work similar to virtual DOM, now that you investigated incremental DOM and are saying that it is not a good match.
And I agree that we should also optimize the whole pipeline for Tracker recomputations as well. But now imagine that we can make DOM rendering faster without Tracker recomputations optimization. Then when we have both, so great!
No, this is a very simple change, for this particular approach to materializing with comparing HTMLJS, because you are interested in this. Or was.
It would still be great if we can solve the issue there as well. I do not think we should worry too much about pull requests not getting in, they are pretty efficient on merging pull requests recently. And even if it goes late on, it goes. But somebody has to make, to even go in at all.
(I wanted to make this an inline reply to a inline comment, but I am realizing it is pretty valuable, so I am moving it top-level.)
Yes, sorry about that. I understood something else under "data level". For me this is more data context level, which has its own issues. Sorry for misunderstanding.
Maybe we should define terminology here better. To my knowledge we have issues at:
Did I miss anything?
We have an App that affects the DOM directly and had no issues. I think it has to do with carefully managing collisions. We do not with JS affect anything that is handled by Blaze. If that separation is done, we're good
Right! So the first one has to do with the diff-ing algorithm in the compare-sequence lib you had talked about before. This is where the real fix should be applied, unless it breaks something else (is this used by Tracker?)
The second is where I believe we have a lot of savings. We don't build DOM elements to diff against DOM. This is bad design. We should always diff raw data, not display layer. This is what makes Blaze fast (like VirtualDOM) and is the reason Incremental DOM is slower than Virtual DOM.
Edit: And given so many unchanged objects can be fed through, that DOM element creation for diffing is really problematic
Oh, maybe I do know about history, but about internals we are all learning. I think you looked much deeper than I ever did in details of rendering itself. So we are all learning here.
Yes, me too. Or you leave to Blaze, or you use something else. But do not mix. :-)
But I have not been able to reproduce it so that I could look what is happening.
But I was using Minimongo and not arrays directly.
Yes, but I could not really find where diffing is done. I have found diffing for attributes, but for DOM it seems like Blaze simply removes old element and adds a new one, when something changes. It does not try to diff and patch the old one?
added a commit
this pull request
Dec 31, 2016
Based on this pull request, I made a change in e17991a, which prevents unnecessary materialization of DOM. This is what I have able to make a reproduction myself. For the other part, checking a change in data layer I have not been able to make a reproduction.
I am closing this pull request. If we find a good reproduction for that other case, we could look into optimizing that as well.
For more ideas how to optimize Blaze see #111.
Beautiful @mitar! Thanks!
@arggh, we caught this as we outputted to the console every re-render. This can improve performance dramatically by skipping DOM manipulation if we have a very dynamic page. I am guessing this change is going to be very noticeable for a lot of people. Look forward to your benchmark.
There is also room for improvement in the actual diffing but that's a bigger job.
One more thing. I was unable to get to