Skip to content
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

Optionaly patch nodes by keys. #357

Merged
merged 10 commits into from
Apr 27, 2020
Merged

Optionaly patch nodes by keys. #357

merged 10 commits into from
Apr 27, 2020

Conversation

akhilman
Copy link
Member

@akhilman akhilman commented Feb 18, 2020

Patch old/new nodes by key if provided.

Continues #356
Solves #354

Progress:

  • New patching algorithm
  • Unit tests
  • Example app
  • Rebase to current master

@akhilman
Copy link
Member Author

I will provide proper example.

Here how circles looks now:
anim

@MartinKavik
Copy link
Member

Good job, keep going 🙂 I'll add keyed Seed benchmark once it's merged and released.

@David-OConnor
Copy link
Member

Hell yea brother

@akhilman
Copy link
Member Author

I finally managed to do it right. Now I’m going to make a proper example and unit test,

@akhilman
Copy link
Member Author

akhilman commented Mar 9, 2020

Here is the test app to play with.
I'm still not happy with the the element ordering. I will try to redo this.

@MartinKavik MartinKavik added this to the 2. Stable basic Seed API milestone Mar 9, 2020
@MartinKavik MartinKavik added NEXT RELEASE enhancement New feature or request labels Mar 9, 2020
@akhilman
Copy link
Member Author

I did it!
The code is still dirty, but it works well.
Most magic happens in the src/virtual_dom/patch_commands.rs. Actually this can almost entirely replace current patch.rs.

Later I will explain how the algorithm works and why I consider it better than ELM's one.

Here how an example app looks like:
anim15

@MartinKavik
Copy link
Member

MartinKavik commented Mar 12, 2020

Very cool! Let me know when I should dive into the code. Also I've written list of potential optimizations - #385 - feel free to update/extend (not only) Virtual DOM section.

@akhilman
Copy link
Member Author

akhilman commented Mar 21, 2020

ELM's algorithm

Takes two nodes the old and the new one.
If the key doesn't match takes two more nodes.
If there is match in keys between the two new and the two old nodes patches the node with matching key and inserts/removes the rest two nodes.
Replaces nodes if there are still no matching keys.
Repeats from start.
https://github.com/elm-lang/virtual-dom/blob/45d7d63ddc26ee4b22b14432e6e426f7ac7a689b/src/Elm/Kernel/VirtualDom.js#L979

Suggested alorithm

Suppose we have old and new child nodes with el_keys:

old: [a] [b] [c] [d] [e] [f] [k] [l]
new: [a] [d] [e] [b] [c] [x] [f] [y]

The algorithm starts by taking nodes form the source iterators one by one in turn (new and old)
and putting them in the corresponding queues:

old: [a]
new: [a]

Now we have the matching key in queues. The algorithm takes nodes from the queues and yields
patch [a] by [a] command.

But then nodes become diverse:

old: [b] [c]
new: [d] [e] [b]

As soon as the algorithm finds the matching key [b] it yields three commands:
insert [d] before old [b], insert [e] before old [b] and then patch [b] by [b].
Old node [c] remains in queue.

The algorithm continues to fill queues and stops with matching nodes [c]. Then it issues patch
command:

old: [c]
new: [c]

Then nodes again become diverse:

old: [d] [e] [f]
new: [x] [f] [y]

The algorithm stops when finds the matching key [f] and yields three commands:
replace [d] by [x], remove [e], patch [f] by [f].

At this point the source iterator for the new nodes has been exhausted and the algorithm
continues to take only old nodes.

old: [k] [l]
new: [y]

At this point both source iterators are exhausted and the algorithm yields:
replace [k] by [y] and remove [l].
The append command means append as a last children and appears when there is no matching key in
front.

As bonus we do not need to call the next_sidling() anymore to find the next node. We always know where to put new node.

@MartinKavik
Copy link
Member

I'm not the author of the previous patching algorithm and I've only fixed some related bugs so I don't know how much it differs from the old code (except the keys).
But your suggestions sounds good and Elm VDOM is fast so I don't see any problems with it, nice.

Please add also some tests so we don't break it during refactors.

And perhaps we should copy-paste your previous comment to the code as the documentation.

@akhilman
Copy link
Member Author

akhilman commented Mar 21, 2020 via email

@akhilman
Copy link
Member Author

@MartinKavik @David-OConnor I'm ready for review.
There still some optimizations that I have in mind, but they could be done later.
Please tell If there is some unclear code. I'll fix it or add comments with explanation.

Copy link
Member

@MartinKavik MartinKavik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first code review done. I've checked all files except patch.rs and patch_gen.rs. The most comments are about styling, best practices, missing docs etc., functionality looks good, thanks!

CHANGELOG.md Outdated Show resolved Hide resolved
examples/el_key/Cargo.toml Outdated Show resolved Hide resolved
examples/el_key/README.md Outdated Show resolved Hide resolved
examples/el_key/README.md Outdated Show resolved Hide resolved
examples/el_key/public/el_key.css Outdated Show resolved Hide resolved
examples/el_key/src/lib.rs Outdated Show resolved Hide resolved
Cargo.toml Outdated Show resolved Hide resolved
src/virtual_dom/node.rs Outdated Show resolved Hide resolved
src/virtual_dom/node/el.rs Outdated Show resolved Hide resolved
examples/el_key/src/lib.rs Outdated Show resolved Hide resolved
src/virtual_dom/patch.rs Outdated Show resolved Hide resolved
src/virtual_dom/patch/patch_gen.rs Outdated Show resolved Hide resolved
src/virtual_dom/patch/patch_gen.rs Outdated Show resolved Hide resolved
src/virtual_dom/patch/patch_gen.rs Outdated Show resolved Hide resolved
src/virtual_dom/patch/patch_gen.rs Outdated Show resolved Hide resolved
@akhilman
Copy link
Member Author

akhilman commented Apr 13, 2020 via email

The keys are used by the diffing algorithm to determine the
correspondence between old and new elements and helps to optimize the
insertion, removal and reordering of elements.

* New el_key attribute for Node.
* New patch algorithm.
* el_key example.

Solves #354
@akhilman akhilman requested a review from MartinKavik April 25, 2020 19:57
@MartinKavik
Copy link
Member

@akhilman
Copy link
Member Author

Thanks for fixes.

@MartinKavik
Copy link
Member

Local non-keyed bench:

  • 0.6.0
    Screenshot_2020-04-27 0 6 0
  • feat/keys
    Screenshot_2020-04-27 feat_keys

The most results are better, good job!
I think that keyed results will be very interesting :)

@MartinKavik MartinKavik changed the title WIP: Optionaly patch nodes by keys. Optionaly patch nodes by keys. Apr 27, 2020
@MartinKavik MartinKavik merged commit 06ed805 into seed-rs:master Apr 27, 2020
@akhilman
Copy link
Member Author

I assume speed up is due to the fact that we no longer call the next_sidling() method. Otherwise, the new algorithm should be slower.

@MartinKavik MartinKavik mentioned this pull request Apr 27, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants