Skip to content

Optimize immutable.{TreeSet, TreeMap} by backporting the 2.13.x RedBlackTree improvements#8749

Merged
retronym merged 1 commit intoscala:2.12.xfrom
mkeskells:2.12.x_RedBlackBackport
Mar 24, 2020
Merged

Optimize immutable.{TreeSet, TreeMap} by backporting the 2.13.x RedBlackTree improvements#8749
retronym merged 1 commit intoscala:2.12.xfrom
mkeskells:2.12.x_RedBlackBackport

Conversation

@mkeskells
Copy link
Contributor

@mkeskells mkeskells commented Feb 24, 2020

Backport #7284 and related changes from 2.13.x to make immutable.{TreeMap,TreeSet} more efficient.

Adapt what is needed for the API changes. This means that 2.12 improvements can be more easily forward ported, as the APIs are more aligned

@scala-jenkins scala-jenkins added this to the 2.12.12 milestone Feb 24, 2020
@SethTisue SethTisue changed the title backport 2.13 BedBlackTree to 2.12 backport 2.13 RedBlackTree to 2.12 Feb 25, 2020
@retronym
Copy link
Member

As discussed, we can probably retain forwards and backwards serialization compatibility here by

  • backporting the new implementation into new class names
  • we can remove all methods from the legacy classes, they just need the same field layout and same automatic serialVersionUID.
  • Use the old classes as serialization proxies for the new implementation.

@mkeskells
Copy link
Contributor Author

benchmark for simple backport of RedBlackTree, without TreeSet/TreeMap

@szeiger
Copy link
Contributor

szeiger commented Feb 27, 2020

Do we need the added complexity of the serialization proxies? I don't see any incompatible changes in the classes that make up the tree. Some methods have changes, which would affect the computed version, but the fields are the same. Wouldn't it be enough to add the right serialVersionUID to the new implementation?

@retronym
Copy link
Member

retronym commented Feb 28, 2020

Oh right, we just need to restore extends Serializable to RedBlackTree.Tree. That isn't needed in 2.13.x because TreeMap and TreeSet themselves use serialization proxies.

The first commit passes the serialization tests with that small change.

In this review branch, I've then dropped the serialization proxy changes. They do have some benefits (more compact serialized form), but that could be discussed in a separate PR.

I cherry picked the last commit.

I added MiMa exceptions, but there are some changes we can't make compatibly:

[error]  * method --(scala.collection.GenTraversableOnce)scala.collection.immutable.TreeMap in class scala.collection.immutable.TreeMap does not have a correspondent in other version
[error]    filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.TreeMap.--")
[error]  * method keySet()scala.collection.immutable.TreeSet in class scala.collection.immutable.TreeMap does not have a correspondent in other version
[error]    filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.TreeMap.keySet")
[error]  * method filter(scala.Function1)scala.collection.immutable.TreeMap in class scala.collection.immutable.TreeMap does not have a correspondent in other version
[error]    filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.TreeMap.filter")
[error]  * the type hierarchy of class scala.collection.immutable.RedBlackTree#KeysIterator is different in other version. Missing types {scala.collection.AbstractIterator}
[error]    filter with: ProblemFilters.exclude[MissingTypesProblem]("scala.collection.immutable.RedBlackTree$KeysIterator")
[error]  * method tree()scala.collection.immutable.RedBlackTree#Tree in class scala.collection.immutable.TreeSet does not have a correspondent in other version
[error]    filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.TreeSet.tree")
[error]  * method ++(scala.collection.GenTraversableOnce)scala.collection.immutable.TreeSet in class scala.collection.immutable.TreeSet does not have a correspondent in other version
[error]    filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.TreeSet.++")
[error]  * method --(scala.collection.GenTraversableOnce)scala.collection.immutable.TreeSet in class scala.collection.immutable.TreeSet does not have a correspondent in other version
[error]    filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.TreeSet.--")
[error]  * method intersect(scala.collection.GenSet)scala.collection.immutable.TreeSet in class scala.collection.immutable.TreeSet does not have a correspondent in other version
[error]    filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.TreeSet.intersect")
[error]  * method diff(scala.collection.GenSet)scala.collection.immutable.TreeSet in class scala.collection.immutable.TreeSet does not have a correspondent in other version
[error]    filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.TreeSet.diff")
[error]  * method filter(scala.Function1)scala.collection.immutable.TreeSet in class scala.collection.immutable.TreeSet does not have a correspondent in other version
[error]    filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.TreeSet.filter")

They need to be pulled up into a type-case the overriden method in the 2.12.x.

@szeiger
Copy link
Contributor

szeiger commented Feb 28, 2020

[error] * method keySet()scala.collection.immutable.TreeSet in class scala.collection.immutable.TreeMap does not have a correspondent in other version

This is due to the change in return type from SortedSet to TreeSet. We need to stick to SortedSet in 2.12.x.

[error] * method tree()scala.collection.immutable.RedBlackTree#Tree in class scala.collection.immutable.TreeSet does not have a correspondent in other version

This one is package-private and can be ignored.

@mkeskells
Copy link
Contributor Author

see retronym#86 and retronym#85

@retronym retronym force-pushed the 2.12.x_RedBlackBackport branch from ad0414a to 5b8bb6b Compare March 2, 2020 00:33
@retronym
Copy link
Member

retronym commented Mar 2, 2020

@mkeskells I've pushed our collective changes to make this binary compatible. I have also removed the serialization proxy.

@retronym retronym force-pushed the 2.12.x_RedBlackBackport branch from 5b8bb6b to 2bd6186 Compare March 2, 2020 01:47
with MapLike[A, B, TreeMap[A, B]]
with Serializable
with HasForeachEntry[A, B] {
private[immutable] val tree: RB.Tree[A, B] = _tree
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this different from making it a val directly in the parameter list?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The has changed since you commented to private def tree0: RB.Tree[A, B] = tree.

The problem we have is that class C(x: A) and class C(private val x: A); object C { (_:C).x } are not serialization compatible because the reference from object C caused the compiler to emit the backing field for that val with a mangled name.

@sjrd
Copy link
Member

sjrd commented Mar 2, 2020

Hum ... I can't help but wonder: is this PR worth it? What is the benefit? Does it deserve to be backported to 2.12 so late in its cycle?

Comment on lines +209 to +215
private def sameCBF(bf: CanBuildFrom[_,_,_]): Boolean = {
bf match {
case tsb: TreeSet.TreeSetBuilder[_] if tsb.ordering == ordering => true
case _ => false
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to take WrappedCanBuildFrom (created by breakOut) into account:

  private def sameCBF(bf: CanBuildFrom[_,_,_]): Boolean = {
    bf match {
      case tsb: TreeSet.TreeSetBuilder[_] if tsb.ordering == ordering => true
      case w: WrappedCanBuildFrom[_,_,_] =>
        sameCBF(w.unwrapped)
      case _ => false
    }
  }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well spotted!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added when I rebased to avoid conflicts. This was the only change other than the conflicts

@lrytz
Copy link
Member

lrytz commented Mar 2, 2020

Hum ... I can't help but wonder: is this PR worth it? What is the benefit? Does it deserve to be backported to 2.12 so late in its cycle?

We are participating in this effort because the improvements in RB-Trees are relevant to a customer of ours. The changes are subject to the ordinary compatiblity constraints, and will benefit all 2.12 users. We're happy to discuss any particular concerns of course!

@mkeskells mkeskells force-pushed the 2.12.x_RedBlackBackport branch from 2bd6186 to 9495588 Compare March 2, 2020 19:10
  - Backports the new internal `RedBlackTree`.
  - In 2.12.x `Tree` must still extend `Serializable` as we don't
    use proxy based serialization for `TreeMap` / `TreeSet`.
  - Take advantage of the new implementation when the operand to
    `++`, `filter`, etc is the same collection type with the same
    `Ordering`. Many of these changes require us to modify the
    inherited implementation to add a type-case to be binary
    compatibile.
@mkeskells mkeskells force-pushed the 2.12.x_RedBlackBackport branch from 9495588 to b87dd51 Compare March 2, 2020 19:19
@SethTisue
Copy link
Member

SethTisue commented Mar 2, 2020

What is the benefit?

The performance comparisons at #7284 (comment) are eye-popping.

@SethTisue SethTisue changed the title backport 2.13 RedBlackTree to 2.12 backport 2.13 immutable RedBlackTree to 2.12 Mar 2, 2020
@SethTisue SethTisue added performance the need for speed. usually compiler performance, sometimes runtime performance. library:collections PRs involving changes to the standard collection library release-notes worth highlighting in next release notes labels Mar 2, 2020
@retronym retronym merged commit b63fefc into scala:2.12.x Mar 24, 2020
@retronym
Copy link
Member

Thanks for your efforts and patience with this one, @mkeskells !

@mkeskells
Copy link
Contributor Author

Thanks for your efforts and patience with this one, @mkeskells !

NP

sjrd added a commit to sjrd/scala-js that referenced this pull request Mar 30, 2020
It hasn't been necessary since 2.13.2, because our fix was merged
upstream in that version.

This commit is an update of 36ea85c
which had already removed the override for 2.13.x.

This change is necessary now because 2.12.12+ has a completely
different implementation, backported from 2.13.x in the upstream
pull requests scala/scala#8749, which is
not compatible with the classes that use `RedBlackTree`.
sjrd added a commit to sjrd/scala-js that referenced this pull request Mar 30, 2020
It hasn't been necessary since 2.12.2, because our fix was merged
upstream in that version.

This commit is an update of 36ea85c
which had already removed the override for 2.13.x.

This change is necessary now because 2.12.12+ has a completely
different implementation, backported from 2.13.x in the upstream
pull requests scala/scala#8749, which is
not compatible with the classes that use `RedBlackTree`.
@retronym retronym mentioned this pull request Jul 2, 2020
65 tasks
@retronym retronym changed the title backport 2.13 immutable RedBlackTree to 2.12 Optimize `immutable.{TreeSet, TreeMap} by backporting the 2.13.x RedBlackTree improvements Jul 2, 2020
@retronym retronym changed the title Optimize `immutable.{TreeSet, TreeMap} by backporting the 2.13.x RedBlackTree improvements Optimize immutable.{TreeSet, TreeMap} by backporting the 2.13.x RedBlackTree improvements Jul 2, 2020
@retronym retronym changed the title Optimize immutable.{TreeSet, TreeMap} by backporting the 2.13.x RedBlackTree improvements Optimize immutable.{TreeSet, TreeMap} by backporting the 2.13.x RedBlackTree improvements Jul 2, 2020
hamzaremmal pushed a commit to hamzaremmal/scala3 that referenced this pull request May 2, 2025
…kport

backport 2.13 immutable RedBlackTree to 2.12
hamzaremmal pushed a commit to scala/scala3 that referenced this pull request May 7, 2025
…kport

backport 2.13 immutable RedBlackTree to 2.12
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 performance the need for speed. usually compiler performance, sometimes runtime performance. release-notes worth highlighting in next release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants