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

Symbols: share the set of Attachments #8718

Merged
merged 2 commits into from
Feb 17, 2020

Conversation

diesalbla
Copy link
Contributor

@diesalbla diesalbla commented Feb 15, 2020

In some profiles, we noticed that the call to clone.updateAttachment was generating up to 10 MiB of lambda objects. Since this seems to be non-reentrant, we can save those allocations by using a single Function1 object with a mutable state.
Screenshot 2020-02-15 at 15 10 32

In some profiles, we noticed that the call to `clone.updateAttachment`
was generating up to 10 MiB of lambda objects.
Since this seems to be reentrant, we can save those allocations by
using a single Function1 object with a mutable state.
@scala-jenkins scala-jenkins added this to the 2.13.3 milestone Feb 15, 2020
@diesalbla diesalbla force-pushed the symbols_update_attachment_lambda branch from 1975e40 to 8c0fc27 Compare February 15, 2020 15:02
@diesalbla diesalbla added the performance:do_not_allocate Changes to avoid object allocations label Feb 15, 2020
@retronym
Copy link
Member

retronym commented Feb 16, 2020

I'd prefer to take this in a slightly different direction which would avoid allocation of intermediate NonEmptyAttachments and immutable.Set instances when cloning symbols that contain non-empty attachment sets.

diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index ce0b975f7c..b834232eba 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -2025,7 +2025,8 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
           setInfo (this.info cloneInfo clone)
           setAnnotations this.annotations
       )
-      this.attachments.all.foreach(clone.updateAttachment)
+      assert(clone.attachments.isEmpty)
+      clone.setAttachments(this.attachments.cloneAttachments)
       if (clone.thisSym != clone)
         clone.typeOfThis = (clone.typeOfThis cloneInfo clone)

diff --git a/src/reflect/scala/reflect/macros/Attachments.scala b/src/reflect/scala/reflect/macros/Attachments.scala
index 157d142f2e..ce87107e89 100644
--- a/src/reflect/scala/reflect/macros/Attachments.scala
+++ b/src/reflect/scala/reflect/macros/Attachments.scala
@@ -76,6 +76,7 @@ abstract class Attachments { self =>
   }

   def isEmpty: Boolean = true
+  def cloneAttachments: Attachments { type Pos = self.Pos } = this
 }

 private object Attachments {
@@ -90,4 +91,5 @@ private final class NonemptyAttachments[P >: Null](override val pos: P, override
   type Pos = P
   def withPos(newPos: Pos) = new NonemptyAttachments(newPos, all)
   override def isEmpty: Boolean = false
+  override def cloneAttachments: Attachments { type Pos = P } = new NonemptyAttachments[P](pos, all)
 }

Copy link
Member

@retronym retronym left a comment

Choose a reason for hiding this comment

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

Consider alternative of reusing the underlying immutable.Set when cloning a symbol with non-empty attachments.

@diesalbla
Copy link
Contributor Author

Just for my information: are those NonEmptyAttachments and immutable.Set instances created by the Lambda? What I could read on the profile was 10 MiB of Lambda objects, which is why it made sense to reuse them with a mutable instances.

Are those changes you mentioned complementary or opposite to extracting the Lambda itself?

@retronym
Copy link
Member

The common case is that the attachment set is empty. In that case, the only allocation here is the Lambda.

But for symbols with a non-empty set of attachments, the current means of cloning is inefficient -- it creates a copy of the the underlying set element by element (discarding the wrapper class NonEmptyAttachments each step!).

But the underlying Set is immutable, so could just be structurally shared as the basis of the cloned symbols attachment set.

When we refactor the code to do this, we no longer call foreach so we don't need the lambda at all.

@retronym retronym closed this Feb 16, 2020
@retronym retronym reopened this Feb 16, 2020
var theClone: TypeOfClonedSymbol = null
def apply(x: Any): Unit = theClone.updateAttachment(x)
}

Copy link
Member

Choose a reason for hiding this comment

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

For future reference, I realised that this was defined at the wrong level -- it should have been a member of Symbols not of Symbol.

@diesalbla
Copy link
Contributor Author

/rebuild

@retronym
Copy link
Member

retronym commented Feb 16, 2020

Needs a binary compatibility exception: ProblemFilters.exclude[DirectMissingMethodProblem]("scala.reflect.macros.Attachments.cloneAttachments") in build.sbt

@diesalbla diesalbla force-pushed the symbols_update_attachment_lambda branch from 81d58cb to 31ba471 Compare February 16, 2020 22:36
@diesalbla diesalbla changed the title Extract the call to updateAttachment as a lambda. Symbols: shared the set of attachments, avoid lambda allocations Feb 16, 2020
@diesalbla diesalbla changed the title Symbols: shared the set of attachments, avoid lambda allocations Symbols: shared the set of attachments, avoid lambda Feb 16, 2020
@diesalbla diesalbla changed the title Symbols: shared the set of attachments, avoid lambda Symbols: share the set of Attachments Feb 16, 2020
It turns out that we may be able to avoid the foreach and the lambda
altogether, and instead use a cloneAttachments function.
@diesalbla diesalbla force-pushed the symbols_update_attachment_lambda branch from 31ba471 to 7ca7131 Compare February 17, 2020 01:10
@retronym
Copy link
Member

/synch

@retronym
Copy link
Member

/nothingtoseehere

@retronym retronym merged commit 516472d into scala:2.13.x Feb 17, 2020
@retronym retronym modified the milestones: 2.13.3, 2.13.2 Feb 17, 2020
@diesalbla diesalbla deleted the symbols_update_attachment_lambda branch June 20, 2021 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance:do_not_allocate Changes to avoid object allocations
Projects
None yet
3 participants