-
Notifications
You must be signed in to change notification settings - Fork 30
Description
Hello,
the following code compiles in Scala 2.12 and 2.13 but silently looses parallelism in 2.13:
val items = if (processInParallelEnabled) {
someSetOfThings.toArray.par
} else {
someSetOfThings
}
items.map(expensiveFunction).foldLeft(…)
In Scala 2.12 items
is of type GenIterable[T]
but in 2.13
it's IterableOnce[T]
which does not define .map
. Instead .map
now comes from IterableOnceExtensionMethods
invokes .iterator.map
on anything that's not a Scala 2.13 Iterable[T]
.
.iterator.map
however is a sequential interface (by design), so .map
becomes sequential no matter whether a parallel collection was constructed.
This kind of silent change in behaviour can silently cause some serious performance regressions (luckily we had a person with a lot of foresight at our group who wrote a test for parallel execution of a certain piece of code which caught this regression).
I am not so familiar with Scala collections as to suggest a solution but I'd like to make a few remarks:
- I'd rather prefer if the code didn't compile on 2.13 instead of silently changing behaviour 🙂
- I believe that we should be able to use sequential and parallel collection under a shared interface, to transparently switch between both worlds.
- As far as I can see
.iterator
is sequential by design; this makesIterableOnce
a rather dangerous interface for parallel collections to inherit because it allows to accidentally turn a parallel collection into a sequential one.
I guess parallel collections should implement some base trait of Scala 2.13 collections;,if that's not possible I think I'd prefer if it did implement no interface at all, not even IterableOnce
, and lived entirely in a world on its own; then it's at least not possible to misuse parallel collections.
Cheers