- Notifications
You must be signed in to change notification settings - Fork 3.1k
Vectors smaller than 32 allocate or reuse small arrays when possible #7743
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
Conversation
ef7b1bd
to f8c01a6
Compare Unfortunately I am seeing about a 40% slowdown in this code: @Benchmark def appended0To33(bh: Blackhole): Unit = { var v: Vector[Int] = Vector.empty[Int] var i = 0 while (i < 33) { v = v.appended(i) i += 1 } bh.consume(v) } hoping to avoid that.. |
❗ (tentatively) Fixed it, turned that around to a 50-70% speedup over 2.13.x, and on par with ArraySeq
in the more general case... @Param(Array("1", "10", "100", "1000", "10000")) var size: Int = _ @Benchmark def appended0ToN(bh: Blackhole): Unit = { var v: Vector[Int] = Vector.empty[Int] var i = 0 while (i < size) { v = v.appended(i) i += 1 } bh.consume(v) }
|
821065a
to 07f352f
Compare s.display0(lo) = value.asInstanceOf[AnyRef] | ||
val thisLength = length | ||
val result = | ||
if (depth == 1 && thisLength < 32) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This branch and the Vector.single branch are the only changed branches, the reset of this method is just indentation
} else { | ||
val shift = startIndex & ~((1 << (5 * (depth - 1))) - 1) | ||
val shiftBlocks = startIndex >>> (5 * (depth - 1)) | ||
} else if (thisLength > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly indentation, plus changing condition to have a bit more obvious of logic:
old:
if (startIndex != endIndex)
new:
if (thisLength > 0) // thisLength already computed anyways
07f352f
to 2277048
Compare 2277048
to 0ca94c6
Compare System.arraycopy(display0, startIndex, newDisplay0, 1, thisLength) | ||
newDisplay0(0) = value.asInstanceOf[AnyRef] | ||
s.display0 = newDisplay0 | ||
s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 already Covered by the releaseFence
below.
System.arraycopy(display0, startIndex, newDisplay0, 0, thisLength) | ||
newDisplay0(thisLength) = value.asInstanceOf[AnyRef] | ||
s.display0 = newDisplay0 | ||
s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 already Covered by the releaseFence below.
Just pushed a commit with the fences |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking forward to these slimmer Vectors!
…ating from varargs, reuse underlying array
67752df
to fe80a23
Compare rebased (trivial merge conflict involving imports), let's merge once CI likes it |
yeah, it's awesome this got in, thanks Josh |
One critique often made against Vectors is that small Vectors consume a lot of memory (See Li Haoyi's post here ). Previous to this PR, Vectors of sizes in range [1, 31] would always round up and allocate an Array of size 32. Now, when creating a Vector with known size < 32, only exactly that size of an Array is allocated.
As well, when creating from varargs of
AnyRef
s (collection.immutable.ArraySeq[T <: AnyRef]
) of size <= 32, we reuse the underlyingArray[AnyRef]
.Performance of creating a Vector from varargs is greatly improved, for both AnyRef and AnyVal types.
Benchmarks
Code:
GC-profiled benchmark:
Edit:
Some benchmark for
Vector.from(ArrayBuffer)