Skip to content
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

Closed
wants to merge 1 commit into from

Conversation

joshlemer
Copy link
Member

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 reuse ArraySeq#unsafeArray's, and then at the time of updating, we special-case the cloning of display0 so that instead of calling display0.clone() we manually copy the elements into an Array[AnyRef] which will be safe to write any element into.

@scala-jenkins scala-jenkins added this to the 2.13.1 milestone Jul 28, 2019
@diesalbla diesalbla added the library:collections PRs involving changes to the standard collection library label Jul 28, 2019
v.display0 = as.unsafeArray match {
case array: Array[AnyRef] => array
case array =>
val display0 = new Array[AnyRef](array.length)
Copy link
Contributor

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]

Copy link
Member Author

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.

Copy link
Contributor

@NthPortal NthPortal Jul 29, 2019

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]]

Copy link
Member Author

@joshlemer joshlemer Jul 29, 2019

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

Copy link
Contributor

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]

Copy link
Member Author

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 to IterableOnce
  • the wrapper's .iterator

Copy link
Member

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.

@joshlemer joshlemer force-pushed the vector-updated-fix branch 2 times, most recently from 3295233 to 5a00a14 Compare July 31, 2019 21:05
@szeiger
Copy link
Member

szeiger commented Aug 29, 2019

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] = {
Copy link
Member Author

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] = {
Copy link
Member Author

@joshlemer joshlemer Aug 29, 2019

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 szeiger modified the milestones: 2.13.1, 2.13.2 Aug 30, 2019
@joshlemer
Copy link
Member Author

@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.

@joshlemer joshlemer marked this pull request as ready for review September 3, 2019 13:41
Copy link
Member

@szeiger szeiger left a 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)
Copy link
Member

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.

@szeiger
Copy link
Member

szeiger commented Sep 4, 2019

MiMa errors look safe for whitelisting:

[error]  * method copyRange(Array[java.lang.Object],java.lang.Class,Int,Int)Array[java.lang.Object] in class scala.collection.immutable.VectorBuilder does not have a correspondent in other version
[error]    filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.VectorBuilder.copyRange")
[error]  * method copyRange(Array[java.lang.Object],java.lang.Class,Int,Int)Array[java.lang.Object] in class scala.collection.immutable.VectorIterator does not have a correspondent in other version
[error]    filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.VectorIterator.copyRange")
[error]  * method copyRange(Array[java.lang.Object],java.lang.Class,Int,Int)Array[java.lang.Object] in class scala.collection.immutable.Vector does not have a correspondent in other version
[error]    filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.Vector.copyRange")
[error]  * method copyRange(Array[java.lang.Object],java.lang.Class,Int,Int)Array[java.lang.Object] in interface scala.collection.immutable.VectorPointer does not have a correspondent in other version
[error]    filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.VectorPointer.copyRange")

@joshlemer
Copy link
Member Author

Superseded by #8534

@joshlemer joshlemer closed this Dec 12, 2019
@SethTisue SethTisue removed this from the 2.13.2 milestone Dec 12, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
library:collections PRs involving changes to the standard collection library
Projects
None yet
7 participants