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

Micro optimise @volatile lazy vals #5478

Merged
merged 2 commits into from Nov 21, 2018

Conversation

Projects
None yet
2 participants
@allanrenucci
Copy link
Member

allanrenucci commented Nov 20, 2018

Given a lazy field of the form:

class Foo {
  @volatile lazy val bar: Int = <RHS>
}

The compiler will now generate code equivalent to:

class Foo {
  import dotty.runtime.LazyVals
  var value_0: Int = _
  var bitmap: Long = 0L
  val bitmap_offset: Long = LazyVals.getOffset(classOf[LazyCell], "bitmap")

  def bar(): Int = {
    while (true) {
      val flag = LazyVals.get(this, bitmap_offset)
      val state = LazyVals.STATE(flag, <field-id>)

      if (state == <state-3>) {
        return value_0
      } else if (state == <state-0>) {
        if (LazyVals.CAS(this, bitmap_offset, flag, <state-1>, <field-id>)) {
          try {
            val result = <RHS>
            value_0 = result
            LazyVals.setFlag(this, bitmap_offset, <state-3>, <field-id>)
            return result
          }
          catch {
            case ex =>
              LazyVals.setFlag(this, bitmap_offset, <state-0>, <field-id>)
              throw ex
          }
        }
      } else /* if (state == <state-1> || state == <state-2>) */ {
        LazyVals.wait4Notification(this, bitmap_offset, flag, <field-id>)
      }
    }
  }
}

@allanrenucci allanrenucci force-pushed the dotty-staging:lazy-vals-micro-opt branch 3 times, most recently from 5be0acd to 8d91a82 Nov 20, 2018

@allanrenucci

This comment has been minimized.

Copy link
Member

allanrenucci commented Nov 20, 2018

This reduces the gap for initialised access performance:

 Benchmark        Mode  Cnt  Score   Error  Units
 measureBaseline  avgt   10  2.361 ± 0.069  ns/op
 measureScala2    avgt   10  2.385 ± 0.053  ns/op
-measureVolatile  avgt   10  4.155 ± 0.192  ns/op
+measureVolatile  avgt   10  3.160 ± 0.107  ns/op

Slightly improve uncontended initialisation performance:

 Benchmark        Mode  Cnt   Score   Error  Units
measureBaseline  avgt   10   5.802 ± 0.133  ns/op
measureScala2    avgt   10  23.221 ± 0.495  ns/op
-measureVolatile  avgt   10  24.082 ± 0.376  ns/op
+measureVolatile  avgt   10  22.238 ± 0.853  ns/op

And it doesn't hurt multi-threaded contended initialisation

@sjrd

This comment has been minimized.

Copy link
Member

sjrd commented Nov 20, 2018

Have you tried the variant where the slow path is moved to a separate method?

@allanrenucci

This comment has been minimized.

Copy link
Member

allanrenucci commented Nov 20, 2018

Have you tried the variant where the slow path is moved to a separate method?

Not yet. But this PR simplifies the code generated (and the implementation). So I would say, let's get this in and then we can experiment in separate PRs.

@allanrenucci allanrenucci requested a review from sjrd Nov 20, 2018

@sjrd
Copy link
Member

sjrd left a comment

Two tiny details.

@allanrenucci allanrenucci force-pushed the dotty-staging:lazy-vals-micro-opt branch from 8d91a82 to d396746 Nov 21, 2018

Comments addressed

@allanrenucci allanrenucci force-pushed the dotty-staging:lazy-vals-micro-opt branch from d396746 to e8bd565 Nov 21, 2018

@sjrd

sjrd approved these changes Nov 21, 2018

@sjrd sjrd merged commit 3730250 into lampepfl:master Nov 21, 2018

2 checks passed

CLA User signed CLA
Details
continuous-integration/drone/pr the build was successful
Details

@sjrd sjrd deleted the dotty-staging:lazy-vals-micro-opt branch Nov 21, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment