Skip to content

Optimize some application-related operations #9875

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 4 commits into from
Sep 26, 2020

Conversation

odersky
Copy link
Contributor

@odersky odersky commented Sep 25, 2020

No description provided.

@odersky
Copy link
Contributor Author

odersky commented Sep 25, 2020

test performance please

@dottybot
Copy link
Member

performance test scheduled: 3 job(s) in queue, 1 running.

@dottybot
Copy link
Member

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/9875/ to see the changes.

Benchmarks is based on merging with master (f4528ce)

For typer/*.scala, we had 3.4M calls to widen, of which
almost 3M were to the three cases that are now handled specially.
@odersky
Copy link
Contributor Author

odersky commented Sep 25, 2020

test performance please

@dottybot
Copy link
Member

performance test scheduled: 3 job(s) in queue, 1 running.

@dottybot
Copy link
Member

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/9875/ to see the changes.

Benchmarks is based on merging with master (f4528ce)

case tp1: TypeParamRef if ctx.typerState.constraint.contains(tp1) =>
asContextFunctionType(TypeComparer.bounds(tp1).hiBound)
case tp1 =>
if (isFunctionType(tp1) && tp1.typeSymbol.name.isContextFunction) tp1
if tp1.typeSymbol.name.isContextFunction && isFunctionType(tp1) then tp1
Copy link
Contributor

Choose a reason for hiding this comment

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

In the old days, we would move the condition to pattern guard in order to avoid parens. Now we will do it to avoid then.

Copy link
Contributor

@liufengyun liufengyun left a comment

Choose a reason for hiding this comment

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

LGTM 👍

@odersky
Copy link
Contributor Author

odersky commented Sep 26, 2020

This was surprisingly effective! Still not quite sure why.

widen is certainly a hotspot, called 3.4M times for typer/*.scala, which makes it the hottest method except for getter-like things. The new implementation gets common cases out of the way before doing essentially what the old one did. The old implementation did

  • two virtual calls which should be predictable by the JIT and that resolve to just returning this in the common case
  • a type test against a trait

I have the impression that type tests against traits are quite inefficient. Can somebody confirm or refute this?

@liufengyun
Copy link
Contributor

I did some benchmark, which seems to validate the following claim:

I have the impression that type tests against traits are quite inefficient. Can somebody confirm or refute this?

  trait TA
  trait TB extends TA
  trait TC
  class A
  class B extends A with TC with TB
  class C extends A with TC with TB
  val b = new B
  val c = new C

  var objs: Array[A] = Array(b, c)

  @Benchmark
  def typeTestTrait(): Boolean = {
    var res = false
    var count = 0
    while count < 1000 do
      res = objs(count % 2).isInstanceOf[TA]
      objs(0) = b
      count += 1
    res
  }

  @Benchmark
  def typeTestClass(): Boolean = {
    var res = false
    var count = 0
    while count < 1000 do
      res = objs(count % 2).isInstanceOf[B]
      objs(0) = b
      count += 1
    res
  }
  • typeTestClass : 1109.409 ns/op
  • typeTestTrait: 1829.782 ns/op

@odersky
Copy link
Contributor Author

odersky commented Sep 26, 2020

@liufengyun That's actually not so bad for the trait type tests. But there could be other variables to consider, like how many traits are inherited and in what order. I believe trait type test comes down to a linear scan through implemented traits.

@odersky
Copy link
Contributor Author

odersky commented Sep 26, 2020

test performance please

@dottybot
Copy link
Member

performance test scheduled: 1 job(s) in queue, 0 running.

@odersky odersky marked this pull request as draft September 26, 2020 09:55
@odersky
Copy link
Contributor Author

odersky commented Sep 26, 2020

I am going to try to avoid having to widen before typeSymbol. This removes a common pitfall and should also help performance.

@dottybot
Copy link
Member

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/9875/ to see the changes.

Benchmarks is based on merging with master (f4528ce)

@odersky odersky force-pushed the optimize-applications branch from 9a0199d to 18dc149 Compare September 26, 2020 11:23
@odersky
Copy link
Contributor Author

odersky commented Sep 26, 2020

test performance please

@odersky odersky closed this Sep 26, 2020
@dottybot
Copy link
Member

performance test scheduled: 1 job(s) in queue, 0 running.

@odersky
Copy link
Contributor Author

odersky commented Sep 26, 2020

The last performance test reverted all of the gains made for dotc (it kept some of the smaller gains in the other tests). So was this just noise? Reverting to original state and testing again.

@odersky odersky reopened this Sep 26, 2020
@michelou
Copy link
Contributor

@liufengyun In your benchmark shouldn't you use "isInstanceOf[TA]" against "isInstanceOf[A]" instead of "isInstanceOf[TA]" against "isInstanceOf[B]" (same chain length) ?!

@dottybot
Copy link
Member

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/9875/ to see the changes.

Benchmarks is based on merging with master (f4528ce)

@odersky
Copy link
Contributor Author

odersky commented Sep 26, 2020

test performance please

@dottybot
Copy link
Member

performance test scheduled: 1 job(s) in queue, 0 running.

@liufengyun
Copy link
Contributor

@liufengyun In your benchmark shouldn't you use "isInstanceOf[TA]" against "isInstanceOf[A]" instead of "isInstanceOf[TA]" against "isInstanceOf[B]" (same chain length) ?!

@michelou That does not make much difference in the performance numbers. I guess JVM uses clever encoding of class tags to make class testing faster.

@dottybot
Copy link
Member

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/9875/ to see the changes.

Benchmarks is based on merging with master (f4528ce)

Use overrides as for stripTypeVar and stripAnnots, but combine the two
in one method
@odersky odersky force-pushed the optimize-applications branch from fa88828 to 5396e71 Compare September 26, 2020 15:31
@odersky
Copy link
Contributor Author

odersky commented Sep 26, 2020

test performance please

@dottybot
Copy link
Member

performance test scheduled: 1 job(s) in queue, 0 running.

@dottybot
Copy link
Member

Performance test finished successfully:

Visit http://dotty-bench.epfl.ch/9875/ to see the changes.

Benchmarks is based on merging with master (f4528ce)

@odersky odersky marked this pull request as ready for review September 26, 2020 16:53
@odersky
Copy link
Contributor Author

odersky commented Sep 26, 2020

After trying many variants I conclude that the initial gain was mostly noise; but there are some small wins nevertheless

@odersky odersky merged commit 8a9e0e9 into scala:master Sep 26, 2020
@odersky odersky deleted the optimize-applications branch September 26, 2020 16:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants