Skip to content

Commit

Permalink
Factor out strict implementations of transformation operations.
Browse files Browse the repository at this point in the history
Add StrictOptimizedMapOps and StrictOptimizedSortedMapOps.

Only operations that were implemented more than once have been factored
out. Some other operations could be factored out so that they could
be reused by custom collection implementations.

Fixes scala/collection-strawman#325
  • Loading branch information
julienrf committed Jul 4, 2018
1 parent 5450358 commit deb94ea
Show file tree
Hide file tree
Showing 19 changed files with 219 additions and 95 deletions.
4 changes: 3 additions & 1 deletion src/library/scala/collection/Seq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,9 @@ trait SeqOps[+A, +CC[_], +C] extends Any

// Make `concat` an alias for `appendedAll` so that it benefits from performance
// overrides of this method
@`inline` final override def concat[B >: A](suffix: Iterable[B]): CC[B] = appendedAll(suffix)
// TODO https://github.com/scala/bug/issues/10853 Uncomment final
@deprecatedOverriding("This method should be final, but is not due to scala/bug#10853", "2.13.0")
@`inline` /*final*/ override def concat[B >: A](suffix: Iterable[B]): CC[B] = appendedAll(suffix)

/** Produces a new sequence which contains all elements of this $coll and also all elements of
* a given sequence. `xs union ys` is equivalent to `xs ++ ys`.
Expand Down
2 changes: 1 addition & 1 deletion src/library/scala/collection/SortedMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ trait SortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _],
}

object SortedMapOps {
private final val ordMsg = "No implicit Ordering[${K2}] found to build a SortedMap[${K2}, ${V2}]. You may want to upcast to a Map[${K}, ${V}] first by calling `unsorted`."
private[collection] final val ordMsg = "No implicit Ordering[${K2}] found to build a SortedMap[${K2}, ${V2}]. You may want to upcast to a Map[${K}, ${V}] first by calling `unsorted`."

/** Specializes `MapWithFilter` for sorted Map collections
*
Expand Down
86 changes: 75 additions & 11 deletions src/library/scala/collection/StrictOptimizedIterableOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import scala.annotation.unchecked.uncheckedVariance
import scala.language.higherKinds

/**
* Trait that overrides operations to take advantage of strict builders.
* Trait that overrides iterable operations to take advantage of strict builders.
*
* @tparam A Elements type
* @tparam CC Collection type constructor
* @tparam C Collection type
*/
trait StrictOptimizedIterableOps[+A, +CC[_], +C]
Expand Down Expand Up @@ -70,26 +71,71 @@ trait StrictOptimizedIterableOps[+A, +CC[_], +C]
// the view-based implementations, but they turn out to be slightly faster because
// a couple of indirection levels are removed

override def map[B](f: A => B): CC[B] = {
val b = iterableFactory.newBuilder[B]
override def map[B](f: A => B): CC[B] =
strictOptimizedMap(iterableFactory.newBuilder, f)

/**
* @param b Builder to use to build the resulting collection
* @param f Element transformation function
* @tparam B Type of elements of the resulting collection (e.g. `String`)
* @tparam C2 Type of the resulting collection (e.g. `List[String]`)
* @return The resulting collection
*/
@inline protected[this] final def strictOptimizedMap[B, C2](b: mutable.Builder[B, C2], f: A => B): C2 = {
val it = iterator
while (it.hasNext) {
b += f(it.next())
}
b.result()
}

override def flatMap[B](f: A => IterableOnce[B]): CC[B] = {
val b = iterableFactory.newBuilder[B]
override def flatMap[B](f: A => IterableOnce[B]): CC[B] =
strictOptimizedFlatMap(iterableFactory.newBuilder, f)

/**
* @param b Builder to use to build the resulting collection
* @param f Element transformation function
* @tparam B Type of elements of the resulting collection (e.g. `String`)
* @tparam C2 Type of the resulting collection (e.g. `List[String]`)
* @return The resulting collection
*/
@inline protected[this] final def strictOptimizedFlatMap[B, C2](b: mutable.Builder[B, C2], f: A => IterableOnce[B]): C2 = {
val it = iterator
while (it.hasNext) {
b ++= f(it.next())
}
b.result()
}

override def collect[B](pf: PartialFunction[A, B]): CC[B] = {
val b = iterableFactory.newBuilder[B]
override def concat[B >: A](suffix: Iterable[B]): CC[B] =
strictOptimizedConcat(suffix, iterableFactory.newBuilder[B])

/**
* @param that Elements to concatenate to this collection
* @param b Builder to use to build the resulting collection
* @tparam B Type of elements of the resulting collections (e.g. `Int`)
* @tparam C2 Type of the resulting collection (e.g. `List[Int]`)
* @return The resulting collection
*/
@inline protected[this] final def strictOptimizedConcat[B >: A, C2](that: Iterable[B], b: mutable.Builder[B, C2]): C2 = {
val it1 = iterator
val it2 = that.iterator
b ++= it1
b ++= it2
b.result()
}

override def collect[B](pf: PartialFunction[A, B]): CC[B] =
strictOptimizedCollect(iterableFactory.newBuilder, pf)

/**
* @param b Builder to use to build the resulting collection
* @param pf Element transformation partial function
* @tparam B Type of elements of the resulting collection (e.g. `String`)
* @tparam C2 Type of the resulting collection (e.g. `List[String]`)
* @return The resulting collection
*/
@inline protected[this] final def strictOptimizedCollect[B, C2](b: mutable.Builder[B, C2], pf: PartialFunction[A, B]): C2 = {
val it = iterator
while (it.hasNext) {
val elem = it.next()
Expand All @@ -100,17 +146,35 @@ trait StrictOptimizedIterableOps[+A, +CC[_], +C]
b.result()
}

override def flatten[B](implicit toIterableOnce: A => IterableOnce[B]): CC[B] = {
val b = iterableFactory.newBuilder[B]
override def flatten[B](implicit toIterableOnce: A => IterableOnce[B]): CC[B] =
strictOptimizedFlatten(iterableFactory.newBuilder)

/**
* @param b Builder to use to build the resulting collection
* @param toIterableOnce Evidence that `A` can be seen as an `IterableOnce[B]`
* @tparam B Type of elements of the resulting collection (e.g. `Int`)
* @tparam C2 Type of the resulting collection (e.g. `List[Int]`)
* @return The resulting collection
*/
@inline protected[this] final def strictOptimizedFlatten[B, C2](b: mutable.Builder[B, C2])(implicit toIterableOnce: A => IterableOnce[B]): C2 = {
val it = iterator
while (it.hasNext) {
b ++= toIterableOnce(it.next())
}
b.result()
}

override def zip[B](that: Iterable[B]): CC[(A @uncheckedVariance, B)] = {
val b = iterableFactory.newBuilder[(A, B)]
override def zip[B](that: Iterable[B]): CC[(A @uncheckedVariance, B)] =
strictOptimizedZip(that, iterableFactory.newBuilder[(A, B)])

/**
* @param that Collection to zip with this collection
* @param b Builder to use to build the resulting collection
* @tparam B Type of elements of the second collection (e.g. `String`)
* @tparam C2 Type of the resulting collection (e.g. `List[(Int, String)]`)
* @return The resulting collection
*/
@inline protected[this] final def strictOptimizedZip[B, C2](that: Iterable[B], b: mutable.Builder[(A, B), C2]): C2 = {
val it1 = iterator
val it2 = that.iterator
while (it1.hasNext && it2.hasNext) {
Expand Down
29 changes: 29 additions & 0 deletions src/library/scala/collection/StrictOptimizedMapOps.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package scala.collection

import scala.language.higherKinds

/**
* Trait that overrides map operations to take advantage of strict builders.
*
* @tparam K Type of keys
* @tparam V Type of values
* @tparam CC Collection type constructor
* @tparam C Collection type
*/
trait StrictOptimizedMapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C]
extends MapOps[K, V, CC, C]
with StrictOptimizedIterableOps[(K, V), Iterable, C] {

override def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] =
strictOptimizedMap(mapFactory.newBuilder, f)

override def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): CC[K2, V2] =
strictOptimizedFlatMap(mapFactory.newBuilder, f)

override def concat[V2 >: V](suffix: Iterable[(K, V2)]): CC[K, V2] =
strictOptimizedConcat(suffix, mapFactory.newBuilder)

override def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)]): CC[K2, V2] =
strictOptimizedCollect(mapFactory.newBuilder, pf)

}
12 changes: 4 additions & 8 deletions src/library/scala/collection/StrictOptimizedSeqOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import scala.language.higherKinds
*/
trait StrictOptimizedSeqOps [+A, +CC[_], +C]
extends Any
with SeqOps[A, CC, C]
with StrictOptimizedIterableOps[A, CC, C] {
with StrictOptimizedIterableOps[A, CC, C]
with SeqOps[A, CC, C] {

override def distinctBy[B](f: A => B): C = {
val builder = newSpecificBuilder
Expand Down Expand Up @@ -45,12 +45,8 @@ trait StrictOptimizedSeqOps [+A, +CC[_], +C]
b.result()
}

override def appendedAll[B >: A](suffix: Iterable[B]): CC[B] = {
val b = iterableFactory.newBuilder[B]
b ++= this
b ++= suffix
b.result()
}
override def appendedAll[B >: A](suffix: Iterable[B]): CC[B] =
strictOptimizedConcat(suffix, iterableFactory.newBuilder)

override def prependedAll[B >: A](prefix: Iterable[B]): CC[B] = {
val b = iterableFactory.newBuilder[B]
Expand Down
17 changes: 17 additions & 0 deletions src/library/scala/collection/StrictOptimizedSetOps.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package scala.collection

/**
* Trait that overrides set operations to take advantage of strict builders.
*
* @tparam A Elements type
* @tparam CC Collection type constructor
* @tparam C Collection type
*/
trait StrictOptimizedSetOps[A, +CC[_], +C <: SetOps[A, CC, C]]
extends SetOps[A, CC, C]
with StrictOptimizedIterableOps[A, CC, C] {

override def concat(that: Iterable[A]): C =
strictOptimizedConcat(that, newSpecificBuilder)

}
29 changes: 29 additions & 0 deletions src/library/scala/collection/StrictOptimizedSortedMapOps.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package scala.collection

import scala.annotation.implicitNotFound

/**
* Trait that overrides sorted map operations to take advantage of strict builders.
*
* @tparam K Type of keys
* @tparam V Type of values
* @tparam CC Collection type constructor
* @tparam C Collection type
*/
trait StrictOptimizedSortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _], +C <: SortedMapOps[K, V, CC, C]]
extends SortedMapOps[K, V, CC, C]
with StrictOptimizedMapOps[K, V, Map, C] {

override def map[K2, V2](f: ((K, V)) => (K2, V2))(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] =
strictOptimizedMap(sortedMapFactory.newBuilder, f)

override def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)])(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] =
strictOptimizedFlatMap(sortedMapFactory.newBuilder, f)

override def concat[V2 >: V](xs: Iterable[(K, V2)]): CC[K, V2] =
strictOptimizedConcat(xs, sortedMapFactory.newBuilder)

override def collect[K2, V2](pf: PartialFunction[(K, V), (K2, V2)])(implicit @implicitNotFound(SortedMapOps.ordMsg) ordering: Ordering[K2]): CC[K2, V2] =
strictOptimizedCollect(sortedMapFactory.newBuilder, pf)

}
61 changes: 20 additions & 41 deletions src/library/scala/collection/StrictOptimizedSortedSetOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,48 +6,27 @@ import scala.annotation.implicitNotFound
import scala.annotation.unchecked.uncheckedVariance
import scala.language.higherKinds

/**
* Trait that overrides sorted set operations to take advantage of strict builders.
*
* @tparam A Elements type
* @tparam CC Collection type constructor
* @tparam C Collection type
*/
trait StrictOptimizedSortedSetOps[A, +CC[X] <: SortedSet[X], +C <: SortedSetOps[A, CC, C]]
extends SortedSetOps[A, CC, C]
with StrictOptimizedIterableOps[A, Set, C] {

override def map[B](f: A => B)(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = {
val b = sortedIterableFactory.newBuilder[B]
val it = iterator
while (it.hasNext) {
b += f(it.next())
}
b.result()
}

override def flatMap[B](f: A => IterableOnce[B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = {
val b = sortedIterableFactory.newBuilder[B]
val it = iterator
while (it.hasNext) {
b ++= f(it.next())
}
b.result()
}

override def zip[B](that: Iterable[B])(implicit @implicitNotFound(SortedSetOps.zipOrdMsg) ev: Ordering[(A @uncheckedVariance, B)]): CC[(A @uncheckedVariance, B)] = { // sound bcs of VarianceNot
val b = sortedIterableFactory.newBuilder[(A, B)]
val it1 = iterator
val it2 = that.iterator
while (it1.hasNext && it2.hasNext) {
b += ((it1.next(), it2.next()))
}
b.result()
}

override def collect[B](pf: PartialFunction[A, B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] = {
val b = sortedIterableFactory.newBuilder[B]
val it = iterator
while (it.hasNext) {
val elem = it.next()
if (pf.isDefinedAt(elem)) {
b += pf.apply(elem)
}
}
b.result()
}
with StrictOptimizedSetOps[A, Set, C] {

override def map[B](f: A => B)(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] =
strictOptimizedMap(sortedIterableFactory.newBuilder, f)

override def flatMap[B](f: A => IterableOnce[B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] =
strictOptimizedFlatMap(sortedIterableFactory.newBuilder, f)

override def zip[B](that: Iterable[B])(implicit @implicitNotFound(SortedSetOps.zipOrdMsg) ev: Ordering[(A @uncheckedVariance, B)]): CC[(A @uncheckedVariance, B)] =
strictOptimizedZip(that, sortedIterableFactory.newBuilder[(A, B)])

override def collect[B](pf: PartialFunction[A, B])(implicit @implicitNotFound(SortedSetOps.ordMsg) ev: Ordering[B]): CC[B] =
strictOptimizedCollect(sortedIterableFactory.newBuilder, pf)

}
15 changes: 8 additions & 7 deletions src/library/scala/collection/immutable/BitSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ sealed abstract class BitSet
with collection.BitSet
with SortedSetOps[Int, SortedSet, BitSet]
with collection.BitSetOps[BitSet]
with StrictOptimizedIterableOps[Int, Set, BitSet] {
with StrictOptimizedIterableOps[Int, Set, BitSet]
with StrictOptimizedSortedSetOps[Int, SortedSet, BitSet] {

def bitSetFactory = BitSet

Expand All @@ -49,17 +50,17 @@ sealed abstract class BitSet
*/
protected def updateWord(idx: Int, w: Long): BitSet

override def map(f: Int => Int): BitSet = super[BitSet].map(f)
override def map(f: Int => Int): BitSet = strictOptimizedMap(newSpecificBuilder, f)
override def map[B](f: Int => B)(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] =
super[SortedSetOps].map(f)
super[StrictOptimizedSortedSetOps].map(f)

override def flatMap(f: Int => IterableOnce[Int]): BitSet = super[BitSet].flatMap(f)
override def flatMap(f: Int => IterableOnce[Int]): BitSet = strictOptimizedFlatMap(newSpecificBuilder, f)
override def flatMap[B](f: Int => IterableOnce[B])(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] =
super[SortedSetOps].flatMap(f)
super[StrictOptimizedSortedSetOps].flatMap(f)

override def collect(pf: PartialFunction[Int, Int]): BitSet = super[BitSet].collect(pf)
override def collect(pf: PartialFunction[Int, Int]): BitSet = strictOptimizedCollect(newSpecificBuilder, pf)
override def collect[B](pf: scala.PartialFunction[Int, B])(implicit @implicitNotFound(collection.BitSet.ordMsg) ev: Ordering[B]): SortedSet[B] =
super[SortedSetOps].collect(pf)
super[StrictOptimizedSortedSetOps].collect(pf)

// necessary for disambiguation
override def zip[B](that: scala.Iterable[B])(implicit @implicitNotFound(collection.BitSet.zipOrdMsg) ev: Ordering[(Int, B)]): SortedSet[(Int, B)] =
Expand Down
12 changes: 6 additions & 6 deletions src/library/scala/collection/immutable/ChampHashMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ package scala
package collection.immutable

import java.io.{ObjectInputStream, ObjectOutputStream}

import collection.{Iterator, MapFactory, StrictOptimizedIterableOps}
import collection.Hashing.computeHash
import scala.annotation.unchecked.{uncheckedVariance => uV}
import java.lang.Integer.bitCount
import java.lang.System.arraycopy

import scala.annotation.unchecked.{uncheckedVariance => uV}
import scala.collection.Hashing.computeHash
import scala.collection.mutable.{Builder, ImmutableBuilder}
import scala.collection.{Iterator, MapFactory, StrictOptimizedIterableOps, StrictOptimizedMapOps}

/** This class implements immutable maps using a Compressed Hash-Array Mapped Prefix-tree.
* See paper https://michael.steindorfer.name/publications/oopsla15.pdf for more details.
Expand All @@ -26,7 +25,8 @@ import scala.collection.mutable.{Builder, ImmutableBuilder}
final class ChampHashMap[K, +V] private[immutable] (val rootNode: MapNode[K, V], val cachedJavaKeySetHashCode: Int, val cachedSize: Int)
extends AbstractMap[K, V]
with MapOps[K, V, ChampHashMap, ChampHashMap[K, V]]
with StrictOptimizedIterableOps[(K, V), Iterable /* ChampHashMap */, ChampHashMap[K, V]] {
with StrictOptimizedIterableOps[(K, V), Iterable, ChampHashMap[K, V]]
with StrictOptimizedMapOps[K, V, ChampHashMap, ChampHashMap[K, V]] {

override def mapFactory: MapFactory[ChampHashMap] = ChampHashMap

Expand Down Expand Up @@ -162,8 +162,8 @@ private[immutable] sealed abstract class MapNode[K, +V] extends MapNodeSource[K,

private class BitmapIndexedMapNode[K, +V](val dataMap: Int, val nodeMap: Int, val content: Array[Any]) extends MapNode[K, V] {

import Node._
import MapNode._
import Node._

/*
assert(checkInvariantContentIsWellTyped())
Expand Down
Loading

0 comments on commit deb94ea

Please sign in to comment.