Skip to content

Micro optimise @volatile lazy vals #5478

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 2 commits into from
Nov 21, 2018

Conversation

allanrenucci
Copy link
Contributor

@allanrenucci 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 lazy-vals-micro-opt branch 3 times, most recently from 5be0acd to 8d91a82 Compare November 20, 2018 16:09
@allanrenucci
Copy link
Contributor Author

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
Copy link
Member

sjrd commented Nov 20, 2018

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

@allanrenucci
Copy link
Contributor Author

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.

sjrd
sjrd previously requested changes Nov 20, 2018
Copy link
Member

@sjrd sjrd left a comment

Choose a reason for hiding this comment

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

Two tiny details.

@allanrenucci allanrenucci dismissed sjrd’s stale review November 21, 2018 11:01

Comments addressed

@sjrd sjrd merged commit 3730250 into scala:master Nov 21, 2018
@sjrd sjrd deleted the lazy-vals-micro-opt branch November 21, 2018 16:36
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.

2 participants