Skip to content

Commit 4234b34

Browse files
committed
SI-7725 - Vector concatenation is unreasonably slow
Rewrote ++ to use append or prepend when adding small collections to the end or beginning of vectors. This solves the extra-O(n) problem for addition of single elements reported in SI_7725. Renamed LgConcatFaster to Log2ConcatFaster (more widely recognizable).
1 parent 884e1ce commit 4234b34

File tree

1 file changed

+26
-3
lines changed

1 file changed

+26
-3
lines changed

src/library/scala/collection/immutable/Vector.scala

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ object Vector extends IndexedSeqFactory[Vector] {
2424
ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]
2525
private[immutable] val NIL = new Vector[Nothing](0, 0, 0)
2626
override def empty[A]: Vector[A] = NIL
27+
28+
// Constants governing concat strategy for performance
29+
private final val Log2ConcatFaster = 5
30+
private final val TinyAppendFaster = 2
2731
}
2832

2933
// in principle, most members should be private. however, access privileges must
@@ -205,10 +209,29 @@ override def companion: GenericCompanion[Vector] = Vector
205209
override /*IterableLike*/ def splitAt(n: Int): (Vector[A], Vector[A]) = (take(n), drop(n))
206210

207211

208-
// concat (stub)
209-
212+
// concat (suboptimal but avoids worst performance gotchas)
210213
override def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[Vector[A], B, That]): That = {
211-
super.++(that.seq)
214+
if (bf eq IndexedSeq.ReusableCBF) {
215+
import Vector.{Log2ConcatFaster, TinyAppendFaster}
216+
if (that.isEmpty) this.asInstanceOf[That]
217+
else {
218+
val again = if (!that.isTraversableAgain) that.toVector else that
219+
again.size match {
220+
// Often it's better to append small numbers of elements (or prepend if RHS is a vector)
221+
case n if n <= TinyAppendFaster || n < (this.size >> Log2ConcatFaster) =>
222+
var v: Vector[B] = this
223+
for (x <- again) v = v :+ x
224+
v.asInstanceOf[That]
225+
case n if this.size < (n >> Log2ConcatFaster) && again.isInstanceOf[Vector[_]] =>
226+
var v = again.asInstanceOf[Vector[B]]
227+
val ri = this.reverseIterator
228+
while (ri.hasNext) v = ri.next +: v
229+
v.asInstanceOf[That]
230+
case _ => super.++(that)
231+
}
232+
}
233+
}
234+
else super.++(that.seq)
212235
}
213236

214237

0 commit comments

Comments
 (0)