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

optimize zip() by pre-sizing target. Fixes #376 #435

Merged
merged 1 commit into from
Jan 17, 2018

Conversation

nolequen
Copy link
Contributor

Hope, I found all of them.

@nikhilnanivadekar
Copy link
Contributor

Thanks @nolequen for the PR. From the contract of zip(), we need to pre-size the target collection to the minimum size of the two iterables.
For example: please refer to: immutablePrimitiveArrayList.stg#L718
MutableList<<name><name>Pair> target = Lists.mutable.withInitialCapacity(Math.min(size, otherSize));

Copy link
Contributor

@nikhilnanivadekar nikhilnanivadekar left a comment

Choose a reason for hiding this comment

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

Please pre-size the target collection to the minimum size of the two iterables.

@nolequen
Copy link
Contributor Author

@nikhilnanivadekar, thanks for the feedback! Can you please take a look at the current version.

@nikhilnanivadekar
Copy link
Contributor

nikhilnanivadekar commented Jan 15, 2018

@nolequen can you please rebase on to master that will have @donraab 's changes merged on your branch and squash the commits. We like to keep the git log history incremental otherwise it becomes difficult to trace the changes in git log.

Also regarding pre-sizing it using Iterate.sizeOf() I think we would need to do an instance check since Iterate.sizeOf() is O(n) for Iterable which is not a Collection or RichIterable. See here: L3183
There is 1 place where pre-sizing is done for select I have commented on the relevant line.

@@ -852,7 +852,9 @@ final class Immutable<name>ObjectSingletonMap\<V> extends AbstractImmutable<name
@Override
public \<S> ImmutableBag\<Pair\<V, S>\> zip(Iterable\<S> that)
{
return this.zip(that, HashBag.\<Pair\<V, S>\>newBag()).toImmutable();
Copy link
Contributor

@nikhilnanivadekar nikhilnanivadekar Jan 15, 2018

Choose a reason for hiding this comment

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

It is ok to not pre-size in this file. Since the max the zipped Bag will be is of size 1. Pre-sizing it to 1 defaults it to 10.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As far as I see in objectPrimitiveHashMap.stg pre-sizing to 1 gives map capacity=2 while without presizing it gives capacity=16.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nikhilnanivadekar what kind of instance check do you mean? can you provide an example?

@nikhilnanivadekar
Copy link
Contributor

Also 1 more request, can you please update the copyright to be 2018 and also add and others.
For Example: https://github.com/eclipse/eclipse-collections/blob/master/site/index.html#L3

@nolequen nolequen force-pushed the issue-376 branch 2 times, most recently from ecc0d9d to 8202137 Compare January 15, 2018 20:35
@@ -1,4 +1,4 @@
import "copyright.stg"
Copy link
Contributor

Choose a reason for hiding this comment

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

Please revert this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

}

@Override
public MutableSet<V> select(Predicate<? super V> predicate)
{
return this.delegate.select(predicate, new UnifiedSet<>());
return this.delegate.select(predicate, UnifiedSet.newSet(this.size()));
Copy link
Contributor

Choose a reason for hiding this comment

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

We cannot pre-size for select() as we do not know beforehand for how many elements the Predicate will return true. Can you please revert this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry for this. Reverted.

@@ -1465,7 +1465,9 @@ public class <name>ObjectHashMap\<V> implements Mutable<name>ObjectMap\<V>, Exte
@Override
public \<S> MutableBag\<Pair\<V, S>\> zip(Iterable\<S> that)
{
return this.zip(that, HashBag.\<Pair\<V, S>\>newBag());
int thatSize = Iterate.sizeOf(that);
Copy link
Contributor

Choose a reason for hiding this comment

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

if(that instanceof Collection || that instanceOf RichIterable)
{
    int thatSize = Iterate.sizeOf(that)
    HashBag\<Pair\<V, S>\> target = HashBag.newBag(Math.min(this.size(), thatSize));
    return this.zip(that, target)
}
return this.zip(that, HashBag.\<Pair\<V, S>\>newBag());

@nikhilnanivadekar
Copy link
Contributor

I meant by instance check for only Collection or RichIterable as shown below:

if(that instanceof Collection || that instanceOf RichIterable)
{
    int thatSize = Iterate.sizeOf(that)
    HashBag\<Pair\<V, S>\> target = HashBag.newBag(Math.min(this.size(), thatSize));
    return this.zip(that, target)
}
return this.zip(that, HashBag.\<Pair\<V, S>\>newBag());

@nolequen nolequen force-pushed the issue-376 branch 7 times, most recently from d36b111 to e5ceeab Compare January 16, 2018 13:05
Signed-off-by: nolequen <nolequen@gmail.com>
@nikhilnanivadekar
Copy link
Contributor

Thanks for your contribution!

@nikhilnanivadekar nikhilnanivadekar merged commit 17ec1c9 into eclipse:master Jan 17, 2018
@motlin motlin added this to the 9.2.0 milestone Jan 23, 2018
@motlin
Copy link
Contributor

motlin commented Nov 10, 2019

Sorry I'm late to this. I just came across this while working on something else. I think this is a deoptimization and we should either fix it or revert it. For context, here's an example implementation today:

@Override
public <S> MutableList<Pair<T, S>> zip(Iterable<S> that)
{
    if (that instanceof Collection || that instanceof RichIterable)
    {
        int thatSize = Iterate.sizeOf(that);
        FastList<Pair<T, S>> target = FastList.newList(Math.min(this.size(), thatSize));
        return this.zip(that, target);
    }
    return this.zip(that, FastList.newList());
}

The problem is the clause that instanceof RichIterable. Not all RichIterables know their size. Specifically:

public interface LazyIterable<T>
        extends RichIterable<T>

If that is an instance of SelectIterable, this will cause two iterations where there used to be one.

@nikhilnanivadekar @nolequen @donraab

@motlin
Copy link
Contributor

motlin commented Nov 10, 2019

Here are two somewhat contrived examples that terminate instantly with 9.1.0 and need to be Ctrl+C killed with the current version.

@Test
public void concatenate()
{
    MutableList<Integer> smallList = Lists.mutable.with(1, 2, 3);

    Interval bigInterval = Interval.zeroTo(Integer.MAX_VALUE);
    LazyIterable<Integer> lazyEmptyInterval = bigInterval.select(x -> false);
    LazyIterable<Integer> concatenate = smallList.asLazy().concatenate(lazyEmptyInterval);

    MutableList<Pair<Integer, Integer>> zipped = smallList.zip(concatenate);
    assertEquals(smallList.zip(smallList), zipped);
}

@Test
public void empty()
{
    MutableList<Integer> emptyList = Lists.mutable.empty();

    Interval bigInterval = Interval.zeroTo(Integer.MAX_VALUE);
    LazyIterable<Integer> lazyEmptyInterval = bigInterval.select(x -> false);

    MutableList<Pair<Integer, Integer>> zipped = emptyList.zip(lazyEmptyInterval);
    assertEquals(Lists.mutable.empty(), zipped);
}

@nikhilnanivadekar @nolequen @donraab

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.

None yet

3 participants