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

Vector optimizations #1522

Merged
merged 7 commits into from
Oct 22, 2016
Merged

Vector optimizations #1522

merged 7 commits into from
Oct 22, 2016

Conversation

l0rinc
Copy link
Contributor

@l0rinc l0rinc commented Aug 23, 2016

Optimizes #1504

Every commit introduces a new type of optimization, i.e.

  • Optimized ofAll for Vector
    • ~150× faster by building the tree from bottom up
  • Optimized prepend and append for Vector
    • ~6× faster by adding the trailing and leading arrays as depthless staging areas (i.e. only modify the tree if these two are full) - removed, as it hinders maintainability
  • Optimized drop and take for Vector
    • >6× faster by keeping the leafs intact on drops, as the middle tree already has an offset and a length that can mask them
  • Optimized map for Vector and Optimized filter for Vector
    • >14× faster by using the internal visitor with ofAll

Benchmarks:

Operation Ratio                                          32     1024    32768 
Create    slang_persistent/java_mutable               1.59×    0.93×    1.11×
Create    slang_persistent/fjava_persistent         198.49×  187.60×  179.09×
Create    slang_persistent/pcollections_persistent  115.43×  220.62×  351.42×
Create    slang_persistent/ecollections_persistent    1.93×    0.91×    1.12×
Create    slang_persistent/clojure_persistent         0.98×   13.66×   11.09×
Create    slang_persistent/scala_persistent          13.76×    8.23×    6.02×

Head      slang_persistent/java_mutable               0.80×    0.58×    0.44×
Head      slang_persistent/fjava_persistent           1.02×    0.74×    0.56×
Head      slang_persistent/pcollections_persistent    2.50×    4.21×    4.80×
Head      slang_persistent/ecollections_persistent    0.94×    0.68×    0.52×
Head      slang_persistent/clojure_persistent         0.85×    0.97×    1.00×
Head      slang_persistent/scala_persistent           0.96×    0.69×    0.52×

Tail      slang_persistent/java_mutable               0.98×    0.70×    0.52×
Tail      slang_persistent/fjava_persistent          69.30×   61.65×   64.54×
Tail      slang_persistent/pcollections_persistent    7.94×   10.77×   15.30×
Tail      slang_persistent/ecollections_persistent   23.08×  178.94× 5353.95×
Tail      slang_persistent/clojure_persistent         1.23×    0.88×    0.75×
Tail      slang_persistent/scala_persistent           4.15×    6.05×    7.77×

Get       slang_persistent/java_mutable               0.77×    0.46×    0.38×
Get       slang_persistent/fjava_persistent          61.60×  114.97×   88.71×
Get       slang_persistent/pcollections_persistent    8.43×   15.06×   18.61×
Get       slang_persistent/ecollections_persistent    0.85×    0.38×    0.36×
Get       slang_persistent/clojure_persistent         0.97×    1.43×    1.49×
Get       slang_persistent/scala_persistent           1.20×    1.20×    0.99×

Update    slang_persistent/java_mutable               0.10×    0.05×    0.03×
Update    slang_persistent/fjava_persistent         115.25×  241.74×  245.64×
Update    slang_persistent/pcollections_persistent    2.32×    4.11×    5.46×
Update    slang_persistent/ecollections_persistent   13.21×  154.34× 3096.27×
Update    slang_persistent/clojure_persistent         0.83×    1.25×    1.25×
Update    slang_persistent/scala_persistent           0.98×    1.05×    1.37×

Map       slang_persistent/java_mutable_loop          0.69×    0.70×    0.62×
Map       slang_persistent/java_mutable               2.82×    2.47×    2.11×
Map       slang_persistent/ecollections_persistent    1.45×    1.17×    1.17×
Map       slang_persistent/scala_persistent           1.34×    1.56×    1.95×

Filter    slang_persistent/java_mutable               2.44×    1.92×    1.45×
Filter    slang_persistent/ecollections_persistent    1.91×    1.37×    1.04×
Filter    slang_persistent/scala_persistent           1.63×    1.68×    1.72×

Prepend   slang_persistent/java_mutable               0.38×    0.72×   12.15×
Prepend   slang_persistent/fjava_persistent           1.59×    1.64×    1.18×
Prepend   slang_persistent/pcollections_persistent    1.06×    2.58×    3.10×
Prepend   slang_persistent/ecollections_persistent    2.74×   28.35×  615.48×
Prepend   slang_persistent/clojure_persistent         6.39×  132.62× 3502.23×
Prepend   slang_persistent/scala_persistent           0.26×    0.24×    0.18×

Append    slang_persistent/java_mutable               0.25×    0.06×    0.03×
Append    slang_persistent/fjava_persistent           4.99×    1.68×    1.04×
Append    slang_persistent/pcollections_persistent    2.64×    2.06×    2.23×
Append    slang_persistent/ecollections_persistent    8.74×   43.27×  562.24×
Append    slang_persistent/clojure_persistent         1.05×    0.27×    0.19×
Append    slang_persistent/scala_persistent           0.99×    0.27×    0.17×

GroupBy   slang_persistent/java_mutable               0.40×    0.91×    0.83×
GroupBy   slang_persistent/scala_persistent           1.47×    1.09×    1.21×

Slice     slang_persistent/java_mutable               0.65×    0.56×    0.57×
Slice     slang_persistent/pcollections_persistent    0.57×    0.48×    0.51×
Slice     slang_persistent/clojure_persistent         0.63×    0.53×    0.55×
Slice     slang_persistent/scala_persistent           2.66×    6.17×   10.82×

Iterate   slang_persistent/java_mutable               1.10×    0.32×    0.49×
Iterate   slang_persistent/fjava_persistent         422.22×  306.40×  363.16×
Iterate   slang_persistent/pcollections_persistent   12.27×    9.77×   11.87×
Iterate   slang_persistent/ecollections_persistent    2.62×    1.18×    1.68×
Iterate   slang_persistent/clojure_persistent         0.88×    0.84×    1.06×
Iterate   slang_persistent/scala_persistent           1.97×    1.01×    1.12×

i.e. this PR made the following methods faster (for 32768 elements, compared to Scala)

Create   6.02 / 0.04 = ~150.50× faster
Head     0.52 / 0.45 =   ~1.15× faster
Tail     7.77 / 1.02 =   ~7.61× faster
Get      0.99 / 0.85 =   ~1.16× faster
Update   1.37 / 0.50 =   ~2.74× faster
Map      1.95 / 0.06 =  ~32.50× faster
Filter   1.72 / 0.12 =  ~14.33× faster
Slice   10.82 / 1.61 =   ~6.72× faster
Iterate  1.12 / 1.08 =   ~1.03× faster

and memory usages:

for `32` elements
  Java mutable @ ArrayList504 bytes
  Functional Java persistent @ Seq3,064 bytes
  PCollections persistent @ TreePVector1,712 bytes
  Eclipse Collections persistent @ ImmutableArrayList496 bytes
  Clojure persistent @ PersistentVector704 bytes
  Scala persistent @ Vector536 bytes
  Javaslang persistent @ Vector528 bytes

for `1024` elements
  Java mutable @ ArrayList19,064 bytes
  Functional Java persistent @ Seq104,760 bytes
  PCollections persistent @ TreePVector55,984 bytes
  Eclipse Collections persistent @ ImmutableArrayList19,056 bytes
  Clojure persistent @ PersistentVector20,504 bytes
  Scala persistent @ Vector19,736 bytes
  Javaslang persistent @ Vector19,728 bytes

for `32768` elements
  Java mutable @ ArrayList653,672 bytes
  Functional Java persistent @ Seq3,408,624 bytes
  PCollections persistent @ TreePVector1,833,376 bytes
  Eclipse Collections persistent @ ImmutableArrayList653,664 bytes
  Clojure persistent @ PersistentVector700,168 bytes
  Scala persistent @ Vector674,824 bytes
  Javaslang persistent @ Vector674,816 bytes

Note: only very simple optimizations were kept, favoring maintainability over raw speed.
Note2: all collections store boxed, non-primitive values for now.

@l0rinc
Copy link
Contributor Author

l0rinc commented Aug 23, 2016

@danieldietrich, @ruslansennov, @zsolt-donca, @eduardmanas these are the optimizations for Vector, please dig in :)

@codecov-io
Copy link

codecov-io commented Aug 23, 2016

Current coverage is 96.48% (diff: 95.58%)

Merging #1522 into master will increase coverage by 0.01%

@@             master      #1522   diff @@
==========================================
  Files            89         89          
  Lines         10878      10940    +62   
  Methods           0          0          
  Messages          0          0          
  Branches       1832       1850    +18   
==========================================
+ Hits          10495      10556    +61   
+ Misses          241        240     -1   
- Partials        142        144     +2   

Powered by Codecov. Last update 52a43bd...4e5ca8b

@danieldietrich
Copy link
Contributor

Interesting table of measures :-))

I've removed functionaljava and PCollections from the following list because they are too slow.

Also I've reduced the not-so-common methods.

Get and Update are most important to us. Vector is a random-access (index sequence) collection and has to be as good as possible in these operations.

Head, Tail, Prepend and Append are typical linear sequence operations. These are not so interesting because users might choose a linear sequence collection (List or Stream) for that purpose.

This is what remains:

Operation  Ratio                                         32     1024    32768
Create    slang_persistent/java_mutable               0.87×    0.44×      0.58×
Create    slang_persistent/scala_persistent          14.73×    8.65×      6.38×

Get       slang_persistent/java_mutable               0.86×    0.34×      0.30×
Get       slang_persistent/scala_persistent           1.26×    0.86×      0.81×

Update    slang_persistent/java_mutable               0.10×    0.05×      0.02×
Update    slang_persistent/scala_persistent           1.17×    0.98×      1.17×

Prepend   slang_persistent/java_mutable               1.47×    3.99×    100.43×
Prepend   slang_persistent/scala_persistent           1.33×    1.41×      1.44×

Append    slang_persistent/java_mutable               0.26×    0.14×      0.10×
Append    slang_persistent/scala_persistent           0.84×    0.59×      0.60×

Update is slow compared to Java mutable. But I think that is not a surprise. Compared to Scala we are head to head here.

Why Get and Append are relatively slower than in Scala?

@danieldietrich
Copy link
Contributor

Java mutable, are you drunk?

Prepend   slang_persistent/java_mutable               1.47×    3.99×    100.43×

@l0rinc
Copy link
Contributor Author

l0rinc commented Aug 23, 2016

Java mutable, are you drunk?

This would be a case where ArrayDeque would shine :)
Maybe I should compare against it instead :/

@l0rinc
Copy link
Contributor Author

l0rinc commented Aug 23, 2016

Why Get and Append are relatively slower than in Scala?

Not sure yet, but I'm planning on investigating it

Edit: oh boy...
wtf

Edit2: pushed the speed fix for append

@l0rinc
Copy link
Contributor Author

l0rinc commented Aug 25, 2016

@danieldietrich, @ruslansennov, @zsolt-donca, @eduardmanas, these are the optimizations for the new Vector.

Please review it commit-by-commit and take a look at the commit messages to see how much faster it got.

Added map to the optimizations also, now it's somehow 3x faster than ArrayList#stream ;)

@@ -42,7 +43,7 @@ public void testAsserts() {

public static void main(String... args) {
JmhRunner.runDebugWithAsserts(CLASSES);
JmhRunner.runNormalNoAsserts(CLASSES);
JmhRunner.runNormalNoAsserts(CLASSES, JAVA, FUNCTIONAL_JAVA, PCOLLECTIONS, CLOJURE, SCALA, JAVASLANG);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

During optimizations I don't want to wait for pcollections and fjava, so I just delete these temporarily:

JmhRunner.runNormalNoAsserts(CLASSES, SCALA, JAVASLANG);

@l0rinc
Copy link
Contributor Author

l0rinc commented Aug 26, 2016

Pushed new version.
Optimized filter also (as I think map and filter are used most often), it's ~2x faster than Java now (finally some values that are better than Java).

@ruslansennov, I might have found what you meant by double copying, simplified the grouping algorithm to avoid it, thanks!

@danieldietrich, the MutableWrapper is only visible from Vector now :).

@@ -883,6 +884,16 @@ void setCurrentArray() {
}
}

void unsafeLeafIterator(LeafVisitor<T> visitor) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

simplified this significantly

@l0rinc
Copy link
Contributor Author

l0rinc commented Sep 3, 2016

The branching base is selected to be 5 (i.e. 2⁵ = 32), as it seems to be the best compromise between read and write operations:

Operation                         Ratio  base=2  base=3  base=4  base=5  base=6  base=7
Get       java_mutable/slang_persistent   8.84×   5.36×   4.23×   3.40×   3.39×   3.32×
Update    java_mutable/slang_persistent  82.37×  57.74×  53.07×  44.47×  49.36×  77.90×
Iterate   java_mutable/slang_persistent   8.01×   7.06×   3.05×   2.74×   2.83×   2.38×

(it's surprising that update is this slow compared to the mutable counterpart, not sure we can change that, though. However, updating the whole collection (i.e. map) results in a lot better performance than Java, via streams ... I guess it depends on the usecase)

@danieldietrich
Copy link
Contributor

@paplorinc thanks for the great overview. I like it that you not guess but measure!

Could you create the same overview for the previous/not optimized Vector version? Would be nice for comparison.

@l0rinc
Copy link
Contributor Author

l0rinc commented Sep 3, 2016

There's no difference in this regard.

@danieldietrich
Copy link
Contributor

Update is a little faster than in Scala, I think it is fine.

I've recognized your speed-fix regarding append(). Do you have an explanation? trailingLength and array.length are the same. How can int length = trailingLength be slower than int length = array.length??

/** Create a single element array */
static Object[] asArray(Object element) {
final Object[] copy = copy(empty(), 1);
final Object[] copy = newInstance(1);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@danieldietrich, unified these also, as per your suggestion

}
}

<U> BitMappedTrie<U> map(Function<? super T, ? extends U> mapper) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

could be optimized to take advantage of the fast that map keeps the original structure (i.e. almost 2x speedup), but would result in slightly more complicated code.
@danieldietrich?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could also unroll the middle manually, resulting in a measurable change, but distorting the code significantly, i.e.

int i = start;
for (; i < end - 3; i += 4) {
    final int val = index.val;
    elems[val] = mapper.apply(leaf[i]);
    elems[val + 1] = mapper.apply(leaf[i + 1]);
    elems[val + 2] = mapper.apply(leaf[i + 2]);
    elems[val + 3] = mapper.apply(leaf[i + 3]);
    index.val = val + 4;
}
for (; i < end; i++) {
    elems[index.val++] = mapper.apply(leaf[i]);
}

before:

Map slang_persistent/scala_persistent 1.26×  1.53×  1.88×

after:

Map slang_persistent/scala_persistent 1.40×  1.54×  1.92×

i.e. not worth it

Copy link
Contributor

Choose a reason for hiding this comment

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

I also think we do not need the further improvement (for now).

With the new visitor, the map method looks like this:

    <U> BitMappedTrie<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper, "mapper is null");
        final Object[] results = new Object[length()];
        this.<T> visit((leaf, start, end, index) -> {
            for (int i = start; i < end; i++) {
                elems[index++] = mapper.apply(leaf[i]);
            }
            return index;
        });
        return BitMappedTrie.ofAll(elems, length);
    }

final Object[] results = new Object[length()];
final MutableInt index = new MutableInt();
this.<T> visit((leaf, start, end) -> {
int resultIndex = index.val;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

mutating a local variable and writing it back to MutableInt at the end makes it ~5% faster

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we do not need MutableInt. Instead we could add a visit method that takes a param index and returns the updated index:

@FunctionalInterface
interface LeafVisitor<T> {
    int visit(T leaf, int start, int end, int index);
}

The visit method then looks like this:

    @SuppressWarnings("unchecked")
    <T2> int visit(LeafVisitor<T2[]> visitor) {
        int start = lastDigit(offset);
        int currentIndex = 0;
        for (int index = 0; index < length; ) {
            final T2[] leaf = (T2[]) getLeaf(index);
            final int end = Math.min(leaf.length, start + length - index);

            currentIndex = visitor.visit(leaf, start, end, currentIndex);

            index += end - start;
            start = 0;
        }
        return currentIndex;
    }

BEFORE:

        ...
        final MutableInt index = new MutableInt();
        this.<T> visit((leaf, start, end) -> {
            int resultIndex = index.val;
            for (int i = start; i < end; i++) {
                final T value = leaf[i];
                if (predicate.test(value)) {
                    results[resultIndex++] = value;
                }
            }
            index.val = resultIndex;
        });

        return (length() == index.val) ? ...

AFTER:

        ...
        final int resultLength = this.<T> visit((leaf, start, end, index) -> {
            for (int i = start; i < end; i++) {
                final T value = leaf[i];
                if (predicate.test(value)) {
                    results[index++] = value;
                }
            }
            return index;
        });

        return (length() == resultLength) ? ...

Note: Similar changes could be applied to all other methods that use visit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I hesitated at first, but trying it out seems indeed like a good idea, thanks :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The benchmark speeds are basically the same, pushed a new version.
Did I address all your concerns?

}

/* drop root node while it has a single element */
private static <T> BitMappedTrie<T> collapsed(Object[] array, int offset, int length, int shift) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we could also drop leading nulls, but that wouldn't result in any speed advantage, just aesthetics...

Copy link
Contributor

Choose a reason for hiding this comment

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

Please explain to me how nulls can occur. I have questions:

  • Does array contain these nulls?
  • What is the meaning of these nulls?
  • Is more and more memory consumed over time be nulled array slots?

The BMT should only contain valid nodes.

Before changing the implementation please explain to me how the tree structure changes over time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since the tree is conceptually not completely symmetric (i.e. a prepend changes existing indices, an append doesn't), we don't need to store every array as if it were a full array, i.e. if we have only 20 elements, the array can be of length < 32.
If you have a full array and drop 10 from the end, we can create a new array of size 22.
If however we drop from the beginning, it's easier to make a full array, and only copy the kept elements (and store the difference in the offset), having the effect of prepend with nulls.
If we would want to eliminate all leading nulls, the computations would be more difficult (though I can try again, if this constant overhead (that's applicable only to non-leaf nodes) bothers you).

Copy link
Contributor

Choose a reason for hiding this comment

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

it is fine as is!

@l0rinc
Copy link
Contributor Author

l0rinc commented Sep 15, 2016

@danieldietrich, @zsolt-donca I pushed a new version with the applied recommendations.
The code is A LOT more readable now, simplified every optimization as much as I could, though that meant that map is 2x slower (still faster than java/scala), prepend is almost 10x slower (6x slower than Scala) and append is ~5x slower than before.
We could apply those optimizations at a later stage, if needed.
Some methods have become slightly faster also, e.g. get, update and iterate.

Edit: we could optimize prependAll and appendAll instead...

return leaf[leafIndex];
}

/**
* fetch the leaf, corresponding to the given index.
* Node: the offset and length should be taken into consideration as there may be leading and trailing garbage.
* Also, the returned array is mutable, but should not be mutated!
*/
@SuppressWarnings("unchecked")
T[] getLeaf(int index) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this is also private now, but since it's accessed from iterator, this way we can avoid the bridge


private static final long serialVersionUID = 1L;

private static final BitMappedTrie<?> EMPTY = new BitMappedTrie<>(Arrays.empty(), 0, 0, 0);
private static final BitMappedTrie<?> EMPTY_BMT = new BitMappedTrie<>(Arrays.empty(), 0, 0, 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: I would it name it EMPTY to be consistent with all other Javaslang types that have an EMPTY instance

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, now I can rename it back. Previously it was used from Vector, which had its own EMPTY

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep, EMPTY is better.

public static <T> Vector<T> narrow(Vector<? extends T> vector) {
return (Vector<T>) vector;
}
public static <T> Vector<T> narrow(Vector<? extends T> vector) { return (Vector<T>) vector; }
Copy link
Contributor

@danieldietrich danieldietrich Sep 15, 2016

Choose a reason for hiding this comment

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

Methods should be no on-liners (for readability) It is ok

public Vector<Vector<T>> combinations() {
return rangeClosed(0, length()).map(this::combinations).flatMap(Function.identity());
}
public Vector<Vector<T>> combinations() { return rangeClosed(0, length()).map(this::combinations).flatMap(Function.identity()); }
Copy link
Contributor

Choose a reason for hiding this comment

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

Just a question :) Did you change your local formatter? Now we have many single-line methods. It does not really hurt but I think you may have a reason to reformat. Generally ok for me!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I formatted these manually, the formatter should keep them intact (mine does).
I wanted the formatting to reflect how it would look in e.g. Scala or in a Java 8 lambda: very simple methods in the same line, complex ones on multiple, to increase SNR.

@danieldietrich
Copy link
Contributor

@paplorinc

I pushed a new version with the applied recommendations.

Many thanks for doing it, I really appreciate it! As you said - further optimizations can be applied later.

Let's finalize this PR. I will still take a look. Maybe we need another iteration...

Thx!

@danieldietrich
Copy link
Contributor

(Note: I will focus now on this PR again - starting tomorrow)

Copy link
Contributor

@danieldietrich danieldietrich left a comment

Choose a reason for hiding this comment

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

Looks awesome to me :)

}

/* drop root node while it has a single element */
private static <T> BitMappedTrie<T> collapsed(Object[] array, int offset, int length, int shift) {
Copy link
Contributor

Choose a reason for hiding this comment

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

it is fine as is!

@danieldietrich
Copy link
Contributor

Hi @paplorinc, now I can't merge because of the conflicts. My fault - I waited too long...
Could you please merge/resolve the conflicts?

@l0rinc
Copy link
Contributor Author

l0rinc commented Oct 21, 2016

@ruslansennov, the build seems to be failing because of GWT incompatibility (https://travis-ci.org/javaslang/javaslang/builds/169480221), could you please help (I presume I need some annotations somewhere)

@ruslansennov
Copy link
Member

Hi @paplorinc
Right now I can't help you because I'm very busy until Sunday evening.
Most GWT-related problems can be solved by removing (or marking by @GwtIncompatible annotation) all reflection stuff like isAssignable method etc

@l0rinc
Copy link
Contributor Author

l0rinc commented Oct 21, 2016

Ok, but how do I find out which method is causing the problem (I can't run the GWT tests locally)?

@ruslansennov
Copy link
Member

ruslansennov commented Oct 21, 2016

Now I see that error log shows not enough info

[INFO] java.lang.ClassCastException
[INFO]  at java.lang.Throwable.Throwable(Throwable.java:61)
[INFO]  at java.lang.Exception.Exception(Exception.java:25)
[INFO]  at java.lang.RuntimeException.RuntimeException(RuntimeException.java:25)
[INFO]  at java.lang.ClassCastException.ClassCastException(ClassCastException.java:23)
[INFO]  at javaemul.internal.InternalPreconditions.checkCriticalType(InternalPreconditions.java:141)
[INFO]  at com.google.gwt.lang.Cast.castTo(InternalPreconditions.java:129)
[INFO]  at client.CollectionsTestGwt$6methodref$ofAll$Type.$apply(Character.java:485)
[INFO]  at client.CollectionsTestGwt.testCompileVector(CollectionsTestGwt.java:23)
[INFO]  at Unknown.anonymous(GWTTestMetadataImpl.java:14)

I can't run the GWT tests locally

Why? Just run mvn test and you will see results of JDK tests and GWT tests

@l0rinc
Copy link
Contributor Author

l0rinc commented Oct 21, 2016

@ruslansennov, it seems to be a GWT bug, i.e. apparently method references don't convert arrays to varargs: 5102b4d

public void testCompileVector() {
applyCollection(Vector::ofAll);
applyCollection(chars -> Vector.ofAll(chars));
Copy link
Contributor

Choose a reason for hiding this comment

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

nice finding!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ruslansennov, should we open a bug report for this?

Copy link
Contributor

Choose a reason for hiding this comment

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

At least there are chances that it will be fixed near-time. Our GWT dependency is still an RC (release candidate).

@danieldietrich
Copy link
Contributor

Great, thank you!!!

@danieldietrich danieldietrich merged commit c5039b3 into vavr-io:master Oct 22, 2016
@l0rinc l0rinc deleted the VectorOptimizations branch October 22, 2016 14:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants