10000 Optimizations in Namer and Typer by retronym · Pull Request #5875 · scala/scala · GitHub
[go: up one dir, main page]

Skip to content

Optimizations in Namer and Typer #5875

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

Merged
merged 12 commits into from
May 25, 2017
Merged

Optimizations in Namer and Typer #5875

merged 12 commits into from
May 25, 2017

Conversation

retronym
Copy link
Member

These amount to a 3% performance improvement.

@scala-jenkins scala-jenkins added this to the 2.12.3 milestone Apr 29, 2017
@retronym retronym requested a review from adriaanm April 29, 2017 00:13
@retronym
Copy link
Member Author
retronym commented Apr 29, 2017

https://scala-ci.typesafe.com/job/scala-2.12.x-validate-test/4897/consoleText

ok  628 - run/repl-paste-raw-c.scala              
Slave went offline during the build
ERROR: Connection was broken: java.io.IOException: Unexpected termination of the channel
	at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:50)
Caused by: java.io.EOFException
	at java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2353)
	at java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:2822)
	at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:804)
	at java.io.ObjectInputStream.<init>(ObjectInputStream.java:301)
	at hudson.remoting.ObjectInputStreamEx.<init>(ObjectInputStreamEx.java:48)
	at hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:34)
	at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:48)

Build step 'Execute shell' marked build as failure
erreicht: 2980631
FATAL: channel is already closed
hudson.remoting.ChannelClosedException: channel is already closed
	at hudson.remoting.Channel.send(Channel.java:578)
	at hudson.remoting.Request.call(Request.java:130)
	at hudson.remoting.Channel.call(Channel.java:780)
	at hudson.Launcher$RemoteLauncher.kill(Launcher.java:953)
	at hudson.model.AbstractBuild$AbstractBuildExecution.run(AbstractBuild.java:540)
	at hudson.model.Run.execute(Run.java:1738)
	at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:43)
	at hudson.model.ResourceController.execute(ResourceController.java:98)
	at hudson.model.Executor.run(Executor.java:410)
Caused by: java.io.IOException: Unexpected termination of the channel
	at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:50)
Caused by: java.io.EOFException
	at java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2353)
	at java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:2822)
	at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:804)
	at java.io.ObjectInputStream.<init>(ObjectInputStream.java:301)
	at hudson.remoting.ObjectInputStreamEx.<init>(ObjectInputStreamEx.java:48)
	at hudson.remoting.AbstractSynchronousByteArrayCommandTransport.read(AbstractSynchronousByteArrayCommandTransport.java:34)
	at hudson.remoting.SynchronousCommandTransport$ReaderThread.run(SynchronousCommandTransport.java:48)
ERROR: Step ‘Archive the artifacts’ failed: no workspace for scala-2.12.x-validate-test #4897
Notifying endpoint 'HTTP:http://scala-ci.typesafe.com:8888/jenkins'
Notifying endpoint 'HTTP:https://scala-ci.typesafe.com/benchq/webhooks/jenkins'
Finished: FAILURE

@retronym
Copy link
Member Author

/rebuild

Copy link
Contributor
@adriaanm adriaanm left a comment

Choose a reason for hiding this comment

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

Nice to see cleaner & faster go hand in hand. A few suggestions, but otherwise looks great!

@@ -588,9 +590,12 @@ trait Implicits {
// We can only rule out a subtype relationship if the left hand
// side is a class, else we may not know enough.
case tr1 @ TypeRef(_, sym1, _) if sym1.isClass =>
def hasMember(tp: Type, name: Name) = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we narrow this helper method's scope a bit? typeRefHasNoMember? I fear a future refactoring could pull this out and miss that it's only valid under certain assumptions.

def pop(): Unit = ()
def inMode(mode: Mode, tree: Tree): Tree = tree
}
class CheckingCheckDead() extends CheckDead {
Copy link
Contributor

Choose a reason for hiding this comment

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

final?

// for use with silent type checking to when we can't have results with undetermined type params
// note that this captures the context var
val isMonoContext = (_: Any) => context.undetparams.isEmpty
private var _checkDead: CheckDead = _
Copy link
Contributor

Choose a reason for hiding this comment

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

private[this]?

@@ -196,11 +197,13 @@ abstract class SymbolTable extends macros.Universe
final def pushPhase(ph: Phase): Phase = {
val current = phase
phase = ph
phStack ::= ph
phStack(phStackIndex) = ph
Copy link
Contributor

Choose a reason for hiding this comment

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

bounds check, just in case?

Copy link
Member Author

Choose a reason for hiding this comment

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

I was happy to let an ArrayIndexOutOfBoundException occur in that case rather than adding explicit code here to handle it.

Copy link
Member Author
@retronym retronym May 2, 2017

Choose a reason for hiding this comment

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

The alternative would be to use an ArrayBuffer, which doesn't require a hard limit to the depth of recursive atPhase-s. But I was being a bit paranoid with performance.

@@ -171,11 +171,12 @@ abstract class SymbolTable extends macros.Universe
final val NoRunId = 0

// sigh, this has to be public or enteringPhase doesn't inline.
var phStack: List[Phase] = Nil
var phStack: Array[Phase] = new Array(128)
Copy link
Contributor

Choose a reason for hiding this comment

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

[nitpick] Only store phase name?

Copy link
Member Author
@retronym retronym May 2, 2017

Choose a reason for hiding this comment

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

Storage wise, there isn't a difference, both are just already allocated Object refs.

If we store the phase name, we have to eagerly call the virtual Phase.name in phase_=, even though under normal circumstances we don't use the result.

In general, It would be good to avoid retaining Phase-s as fields in Global so they are eligible for GC after the Run that spawned them has been collected. But we know we'll end up with an empty phase stack here at the end of the run, so it doesn't make a practical difference.

retronym added 12 commits May 24, 2017 08:59
Non local returns aren't eliminated after inlined in 2.11 or 2.12

```
⚡ scala
Welcome to Scala 2.12.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_112).
Type in expressions for evaluation. Or try :help.

scala> @inlune def foo(a: => Any) = if ("".isEmpty) a else ""
<console>:11: error: not found: type inlune
       @inlune def foo(a: => Any) = if ("".isEmpty) a else ""
        ^

scala> @inline def foo(a: => Any) = if ("".isEmpty) a else ""
foo: (a: => Any)Any

scala> class InlineReturn { def test: Any = foo(return "") }
defined class InlineReturn

scala> :javap -c InlineReturn#test
  public java.lang.Object test();
    Code:
       0: new           #4                  // class java/lang/Object
       3: dup
       4: invokespecial #32                 // Method java/lang/Object."<init>":()V
       7: astore_1
       8: getstatic     #36                 // Field $line4/$read$$iw$$iw$.MODULE$:L$line4/$read$$iw$$iw$;
      11: aload_1
      12: invokedynamic #59,  0             // InvokeDynamic #0:apply:(Ljava/lang/Object;)Lscala/Function0;
      17: invokevirtual #63                 // Method $line4/$read$$iw$$iw$.foo:(Lscala/Function0;)Ljava/lang/Object;
      20: goto          44
      23: astore_2
      24: aload_2
      25: invokevirtual #66                 // Method scala/runtime/NonLocalReturnControl.key:()Ljava/lang/Object;
      28: aload_1
      29: if_acmpne     39
      32: aload_2
      33: invokevirtual #69                 // Method scala/runtime/NonLocalReturnControl.value:()Ljava/lang/Object;
      36: goto          41
      39: aload_2
      40: athrow
      41: goto          44
      44: areturn
    Exception table:
       from    to  target type
           8    20    23   Class scala/runtime/NonLocalReturnControl
```

```
⚡ ~/scala/2.11.8/bin/scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_112).
Type in expressions for evaluation. Or try :help.

scala> @inline def foo(a: => Any) = if ("".isEmpty) a else ""
foo: (a: => Any)Any

scala> class InlineReturn { def test: Any = foo(return "") }
defined class InlineReturn

scala> :javap -c InlineReturn#test
  public java.lang.Object test();
    Code:
       0: new           #4                  // class java/lang/Object
       3: dup
       4: invokespecial #13                 // Method java/lang/Object."<init>":()V
       7: astore_1
       8: getstatic     #19                 // Field .MODULE$:L;
      11: new           #21                 // class InlineReturn$$anonfun$test$1
      14: dup
      15: aload_0
      16: aload_1
      17: invokespecial #24                 // Method InlineReturn$$anonfun$test$1."<init>":(LInlineReturn;Ljava/lang/Object;)V
      20: invokevirtual #28                 // Method .foo:(Lscala/Function0;)Ljava/lang/Object;
      23: goto          39
      26: astore_2
      27: aload_2
      28: invokevirtual #31                 // Method scala/runtime/NonLocalReturnControl.key:()Ljava/lang/Object;
      31: aload_1
      32: if_acmpne     40
      35: aload_2
      36: invokevirtual #34                 // Method scala/runtime/NonLocalReturnControl.value:()Ljava/lang/Object;
      39: areturn
      40: aload_2
      41: athrow
    Exception table:
       from    to  target type
           8    26    26   Class scala/runtime/NonLocalReturnControl

scala> :quit
```
We can avoid the call altogether in some cases.
Rather than doing this generically with a traversal in `atPos`.
This method is hot enough to be as frugal as possible.
As it turns out, all our fast track macros are defined as
`def foo = macro ???`, rather than `def foo = ???`. We can
assume that the MACRO flag is authoritative.
When using the primary constructor paramater trees as prototypes for
the parameter trees of the apply method, restrict tree duplication and
reset attrs to the primary constructor, rather than the entire case
class body.
We're only interested in whether or not a candidate implicit
type has a member with a given name, which we can determine
cheaply by checking if any ancestor has a so-named decl.

We avoid both the as-seen-froms that FindMember uses to
differentiate overriding and overloading, and creation of
overloaded symbols.
…bility

Avoids lots of calls to `isFunctionSymbol`.
If we're already in the requested phase, avoid
the bookkeeping in atPhaseStack.
Keep the phase stack (which is just used for logging)
in a pre-allocated array, rather than in a List.
Use an iterator to inspect annotations of owners, rather than
creating a list of owners.
Contrary to the comment, it wasn't capturing the the context var
at typer initialization, which would not have been correct anyway.

The inlining here is motivated by avoiding allocation of lambda in
every Typer. Even when we're using this value in eta expansion,
SilentResult.filter will actually be inlined to avoid the need for a
lambda at all.
As per review suggestions
@retronym
Copy link
Member Author

I've resolved the merge conflict by dropping the commit "Avoid overhead of dead code checking unless -Ywarn-dead-code is set" from this PR. I'll resubmit it separately after reworking it to use context.warning rather than reporter.warning.

@adriaanm adriaanm merged commit f35ab66 into scala:2.12.x May 25, 2017
@adriaanm adriaanm added the performance the need for speed. usually compiler performance, sometimes runtime performance. label May 25, 2017
retronym added a commit to retronym/scala that referenced this pull request Apr 26, 2018
More of the same as scala#5875

The change to default getters reduced tree churn by 6x in a real world
project.
retronym added a commit to retronym/scala that referenced this pull request May 2, 2018
More of the same as scala#5875

The change to default getters reduced tree churn by 6x in a real world
project.
retronym added a commit to retronym/scala that referenced this pull request May 2, 2018
More of the same as scala#5875

The change to default getters reduced tree churn by 6x in a real world
project.
retronym added a commit to retronym/scala that referenced this pull request May 2, 2018
More of the same as scala#5875

The change to default getters reduced tree churn by 6x in a real world
project.
retronym added a commit to retronym/scala that referenced this pull request May 2, 2018
More of the same as scala#5875

The change to default getters reduced tree churn by 6x in a real world
project.
retronym added a commit to retronym/scala that referenced this pull request May 3, 2018
More of the same as scala#5875

The change to default getters reduced tree churn by 6x in a real world
project.
retronym added a commit to retronym/scala that referenced this pull request May 4, 2018
More of the same as scala#5875

The change to default getters reduced tree churn by 6x in a real world
project.
retronym added a commit to retronym/scala that referenced this pull request May 4, 2018
More of the same as scala#5875

The change to default getters reduced tree churn by 6x in a real world
project.
retronym added a commit to retronym/scala that referenced this pull request May 8, 2018
More of the same as scala#5875

The change to default getters reduced tree churn by 6x in a real world
project.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance the need for speed. usually compiler performance, sometimes runtime performance.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0