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
[11600] Vectors safely clone display0 arrays on updates #8276
Conversation
v.display0 = as.unsafeArray match { | ||
case array: Array[AnyRef] => array | ||
case array => | ||
val display0 = new Array[AnyRef](array.length) |
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.
probably simpler to call toArray
rather than manually copying
case _ => as.toArray[AnyRef]
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.
sadly that won't compile because we don't have that E <: AnyRef
, so def toArray[B >: E]
won't work.
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.
as.toArray[Any].asInstanceOf[Array[AnyRef]]
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.
Hmm looked into that, seems it goes through a lot many intermediate method calls, with additional bounds checks and math checks, to eventually just do this exact thing, so I reckon this is faster but I could benchmark
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.
you might also be able to do Array.from[AnyRef]
or Array.from[Any]
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.
That code path would make two additional allocs:
- the implicitly created wrapper around the
Array
that allows it to conform toIterableOnce
- the wrapper's
.iterator
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.
Array.copyAs
is the right method for this task. Maybe the manually inlined version here is a bit faster.
3295233
to
5a00a14
Compare
5a00a14
to
7ea8315
Compare
I'm not too familiar with the details of the Vector problem but as far as I can tell this is pretty much ready to go, except for the MiMa errors which can be safely whitelisted. @joshlemer Should we merge this for 2.13.1 or merge #8194 for now and keep this one around for later? |
@@ -1146,7 +1158,7 @@ private[immutable] trait VectorPointer[+T] { | |||
* If `destination` array is not null, original contents of array(index) will be copied to it, and it will be returned. | |||
* Otherwise array(index).clone() is returned | |||
*/ | |||
private[immutable] final def nullSlotAndCopy[T <: AnyRef](array: Array[Array[T]], index: Int, destination: Array[T] = null): Array[T] = { | |||
private[immutable] final def nullSlotAndCopy[T0 <: AnyRef](array: Array[Array[T0]], index: Int, destination: Array[T0] = null): Array[T0] = { |
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.
The change T
-> T0
is simply to avoid shadowing which is going on between this T
and the T
in VectorPointer[T]
@@ -1156,6 +1168,14 @@ private[immutable] trait VectorPointer[+T] { | |||
} | |||
} | |||
|
|||
private final def nullSlotAndCopy0(array: Array[Array[AnyRef]], index: Int, destination: Array[AnyRef] = null): Array[AnyRef] = { |
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.
TODO: Include Scaladocs/Comments to explain that this is a specialized form of nullSlotAndCopy
specialized for display0
which exists solely for avoiding use of array.clone()
which may be a shared array with element type != java.lang.Object
@szeiger I do think that this is the better solution, between the two, but definitely think someone with an eye for the Vector impl should give the code a look-over before merging. |
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.
@Ichoran Can you take a look at this PR?
v.display0 = as.unsafeArray match { | ||
case array: Array[AnyRef] => array | ||
case array => | ||
val display0 = new Array[AnyRef](array.length) |
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.
Array.copyAs
is the right method for this task. Maybe the manually inlined version here is a bit faster.
MiMa errors look safe for whitelisting:
|
Superseded by #8534 |
This is an alternative approach to #8194 which fixes scala/bug#11600.
This approach is different because in this PR we do not abandon the reusing of
ArraySeq#unsafeArray
's, we instead continue to reuseArraySeq#unsafeArray
's, and then at the time of updating, we special-case the cloning ofdisplay0
so that instead of callingdisplay0.clone()
we manually copy the elements into anArray[AnyRef]
which will be safe to write any element into.