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

Introduced MovableBidirectionalIterator #283

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

catap
Copy link
Contributor

@catap catap commented Dec 24, 2022

This is a memory optimization for the case when a user required to modify iterator by moving it to a desire position. This works the same way as iteraotr(fromElement) but doesn't create a new iterator that decreases memory footprint at an algorithms which makes a lot of jumps.

@catap catap mentioned this pull request Dec 24, 2022
@catap catap force-pushed the iteratot-jump branch 3 times, most recently from 86629d6 to 88ce051 Compare December 24, 2022 12:22
@catap
Copy link
Contributor Author

catap commented Dec 24, 2022

Just pushed a trivial unit test that's proof that it works as expected. I've also fixed formating by using tab, instead of space which makes generated code looks nice.

@catap catap force-pushed the iteratot-jump branch 3 times, most recently from c4c881d to 5bfc90c Compare December 24, 2022 12:43
@catap
Copy link
Contributor Author

catap commented Dec 24, 2022

And some wording for the doc that makes it a bit cleaner.

@catap
Copy link
Contributor Author

catap commented Dec 24, 2022

Realized that I've missed SubsetIterator. Fixed.

@catap
Copy link
Contributor Author

catap commented Dec 24, 2022

...and adding the same jump to *RBTreeMap's and *AVLTreeMap's both sets: keySet and entrySet.

@catap
Copy link
Contributor Author

catap commented Dec 24, 2022

I hope that I've added it at all sorted Sets now.

@catap
Copy link
Contributor Author

catap commented Dec 27, 2022

Added dummy implementation to empty sets

@catap
Copy link
Contributor Author

catap commented Mar 24, 2023

@vigna any thoughts?

@vigna
Copy link
Owner

vigna commented Mar 24, 2023

Sorry, I somehow lost track of this. The problem I see is that it does not make any sense at the interface level—iterators do not necessarily come from sorted collections.

@catap
Copy link
Contributor Author

catap commented Mar 24, 2023

Sorry, I somehow lost track of this. The problem I see is that it does not make any sense at the interface level—iterators do not necessarily come from sorted collections.

yep, and I haven't found SortedIterator or something like that.

Anyway, it is quite useful at my case :) and I'll be very appricited if it can be merged.

@vigna
Copy link
Owner

vigna commented Mar 24, 2023 via email

@catap
Copy link
Contributor Author

catap commented Mar 31, 2023

I see your point and I agree with it, but I haven't found any nice way to inject that jump. Introducing a conception of JumpingIterator seems like an overkill.

@catap
Copy link
Contributor Author

catap commented Apr 14, 2023

@vigna I'm willing to update this PR to make it easy to merge. To do it, may I ask some guidlines? Thanks!

@vigna
Copy link
Owner

vigna commented Apr 15, 2023

I really don't know. If this would be in Rust everything would be easier as we could combine traits. In the present situation I don't see alternatives to a JumpableIterator-or-something interface. I'm also not entirely convinced of the word "jump"—it suggest moving forward in the iterator sequence, but you're actually implementing a "move". In which case I'd probably have a "rewind()" method (move to the first item, like creating a new iterator) and a "from" method. That is, you can do after creation what you'd do at creation.

Another problem is even if we go for the interface, there's no obvious way to have default implementations, and definitely I want to avoid other optional methods. Any thoughts?

All this aside, do you have any evidence that in your case you have a measurable impact on performance? Small objects that are quickly used and discarded have almost zero impact with modern GCs.

@catap
Copy link
Contributor Author

catap commented Apr 15, 2023

Let me start from the side note. I do have and this was made to make one services quite happy :) Long story short: it decreased response time about 2x in general. And for this services response time is one of critical things :)

The usage of this feature is covered by micro benchmark, and here an example of JMH output with jump:

Benchmark                 (fields)  (ors)  (queries)  Mode  Cnt     Score   Error   Units
both                             5      5          5  avgt   12    65.028 ± 1.266   us/op
both:·gc.alloc.rate              5      5          5  avgt   12    34.850 ± 0.690  MB/sec
both:·gc.alloc.rate.norm         5      5          5  avgt   12  2376.015 ± 0.002    B/op
both:·gc.count                   5      5          5  avgt   12     3.000          counts
both:·gc.time                    5      5          5  avgt   12     5.000              ms

and when I've replaced jump into recreating a brand new iterator:

Benchmark                 (fields)  (ors)  (queries)  Mode  Cnt     Score   Error   Units
both                             5      5          5  avgt   12    64.638 ± 1.337   us/op
both:·gc.alloc.rate              5      5          5  avgt   12   108.726 ± 2.228  MB/sec
both:·gc.alloc.rate.norm         5      5          5  avgt   12  7368.014 ± 0.002    B/op
both:·gc.count                   5      5          5  avgt   12    11.000          counts
both:·gc.time                    5      5          5  avgt   12    20.000              ms

As you may see impact into memory footprint is dramatical, and into speed is insignificant. So, as you had say it hasn't got anything in performance and mainly with GC.

This micro benchmark is runs against faaaar less collections that a production one and production has impact more considerable to be honest, plus it affect in number and period of GC cycles...

The services is running with Shenandoah as GC, I assume it modern enough :) and still less objects make things better and save heap for something else quit useful.

Regarding your approach => I'll rename everything, and try to create a dedicated iterator, maybe it won't be as ugly as I though. Let see.

@catap
Copy link
Contributor Author

catap commented Apr 15, 2023

@vigna do you prefer one more commit with rename and new iterator, or force push with rebased commit?

@catap catap changed the title Introduced jump inside iterator Introduced MovableBidirectionalIterator Apr 15, 2023
@catap
Copy link
Contributor Author

catap commented Apr 15, 2023

here it is.

I've introduced MovableBidirectionalIterator which contains two functions: move(fromElement) and rewind().

It was cleaner that I've expected to be honest.

@vigna
Copy link
Owner

vigna commented Apr 16, 2023

Thinking about it. We're talking about bidirectional iterators. If we have rewind(), we need also something moving to the end. Or not? Just thinking. For example, if we're gonna implement those for linked hash maps, you could get to the start or end in constant time. Maybe begin() and end()? I can't think of an "end" version of rewind().

@catap
Copy link
Contributor Author

catap commented Apr 16, 2023

I agree about moving to the end, but I was blocked because I can’t find the right naming :)

begin() and end() seems like a position and not a verb.

@catap
Copy link
Contributor Author

catap commented Apr 17, 2023

@vigna after some thoughts I agree that begin(), move(..) and end() is the best naming. It already has next() and previous(), so, my assumption that it isn't clear that it is a verb won't hold.

I've added support end() also, and heavy reworked unit tests.

This is ready for review.

@vigna
Copy link
Owner

vigna commented Apr 17, 2023 via email

@catap
Copy link
Contributor Author

catap commented Apr 17, 2023

@vigna yes, after a bit thinking of that mark-reset I've dismissed an idea and removed commit :) because it too wired

@catap
Copy link
Contributor Author

catap commented Aug 21, 2023

@vigna any thought on that?

@vigna
Copy link
Owner

vigna commented Aug 22, 2023

Really on vacation now :). Ferragosto in Italy is sacred. Let's talk about this mid-September...

@catap
Copy link
Contributor Author

catap commented Sep 15, 2023

@vigna how does vacation goes? :)

This is a memory optimization for the case when a user required to
modify iterator by moving it to a desire position. This works the same
way as `iteraotr(fromElement)` but doesn't create a new iterator that
decreases memory footprint at an algorithms which makes a lot of jumps.

So, user simple calls `move(fromElement)`.

It also introduces `begin()` which moves iterator to the first position,
and `end()` which moves iterator to the last element.
@catap
Copy link
Contributor Author

catap commented Sep 29, 2023

@vigna just rebased it to the last master

@vigna
Copy link
Owner

vigna commented Dec 4, 2023

Ok, I know it's like forever but I'm looking into this. There should be no backward compatibility problem because you have default methods right? Just looking around.

@catap
Copy link
Contributor Author

catap commented Dec 4, 2023

@vigna yes, no backward compatibility issues as far as I see. The default method which triggers an exception for this.

@vigna
Copy link
Owner

vigna commented Dec 4, 2023

@vigna yes, no backward compatibility issues as far as I see. The default method which triggers an exception for this.

What?

@@ -24,5 +24,8 @@ public abstract class ABSTRACT_SORTED_SET KEY_GENERIC extends ABSTRACT_SET KEY_G
protected ABSTRACT_SORTED_SET() {}

@Override
public abstract KEY_BIDI_ITERATOR KEY_GENERIC iterator();
public abstract KEY_MOVE_BIDI_ITERATOR KEY_GENERIC iterator(KEY_GENERIC_TYPE fromElement);
Copy link
Owner

Choose a reason for hiding this comment

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

So now what happens to people who implemented a subclass of IntAbstractSortedSet but are returning a standard bidirectional iterator?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, I may move everything into KEY_BIDI_ITERATOR if you think it is safe.

@catap
Copy link
Contributor Author

catap commented Dec 4, 2023

@vigna yes, no backward compatibility issues as far as I see. The default method which triggers an exception for this.

What?

I mean that if someone call new method and hadn't got implemented it, an exception will be thrown.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants