Most operations on a vector
are effectively constant because of its trie
representation. However, I cannot figure out what the performance profile is of the splitAt
implementation.
It is defined in the library as:
override /*IterableLike*/ def splitAt(n: Int): (Vector[A], Vector[A]) = (take(n), drop(n))
The take
function has the following definition:
override def take(n: Int): Vector[A] = {
if (n <= 0)
Vector.empty
else if (startIndex + n < endIndex)
dropBack0(startIndex + n)
else
this
}
And the dropBack0
has the following definition:
private def dropBack0(cutIndex: Int): Vector[A] = {
val blockIndex = (cutIndex - 1) & ~31
val xor = startIndex ^ (cutIndex - 1)
val d = requiredDepth(xor)
val shift = (startIndex & ~((1 << (5*d))-1))
val s = new Vector(startIndex-shift, cutIndex-shift, blockIndex-shift)
s.initFrom(this)
s.dirty = dirty
s.gotoPosWritable(focus, blockIndex, focus ^ blockIndex)
s.preClean(d)
s.cleanRightEdge(cutIndex-shift)
s
}
As you can see dropBack0
is doing some pretty complicated surgery.
Does splitAt
have effectively constant performance or is it worse? It seems to be effectively constant.
It is effectively constant. Vector is a tree with branching factor 32. take
and drop
operations are performed in o(log32N * 32). As the height of the tree can't be more than 5, number of operations for take
, drop
or update
in worst case will be 5 * 32 = 160.
Yes, if you follow each method called in dropBack0
, all of them require constant or effectively constant (maximum array size 32) time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With