Skip to content

Commit 5bd67bb

Browse files
authored
New lazy vals implementation (#15296)
Cleaned up version of #14545 Implementing the new lazy vals scheme mentioned in #6979, fixing #7140 New PR to avoid force pushing to main branch of the previous author. Fixes #15149 as well Applied #14780
2 parents 1ae85eb + f62ffd8 commit 5bd67bb

33 files changed

+1708
-50
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package dotty.tools.benchmarks.lazyvals
2+
3+
import org.openjdk.jmh.annotations._
4+
import LazyVals.LazyHolder
5+
import org.openjdk.jmh.infra.Blackhole
6+
import java.util.concurrent.TimeUnit
7+
import java.util.concurrent.{Executors, ExecutorService}
8+
9+
@BenchmarkMode(Array(Mode.AverageTime))
10+
@Fork(2)
11+
@Threads(1)
12+
@Warmup(iterations = 5)
13+
@Measurement(iterations = 5)
14+
@OutputTimeUnit(TimeUnit.MILLISECONDS)
15+
@State(Scope.Benchmark)
16+
class ContendedInitialization {
17+
18+
@Param(Array("2000000", "5000000"))
19+
var size: Int = _
20+
21+
@Param(Array("2", "4", "8"))
22+
var nThreads: Int = _
23+
24+
var executor: ExecutorService = _
25+
26+
@Setup
27+
def prepare: Unit = {
28+
executor = Executors.newFixedThreadPool(nThreads)
29+
}
30+
31+
@TearDown
32+
def cleanup: Unit = {
33+
executor.shutdown()
34+
executor = null
35+
}
36+
37+
@Benchmark
38+
def measureContended(bh: Blackhole): Unit = {
39+
val array = Array.fill(size)(new LazyHolder)
40+
val task: Runnable = () =>
41+
for (elem <- array) bh.consume(elem.value)
42+
43+
val futures =
44+
for (_ <- 0 until nThreads) yield
45+
executor.submit(task)
46+
47+
futures.foreach(_.get())
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package dotty.tools.benchmarks.lazyvals
2+
3+
import org.openjdk.jmh.annotations._
4+
import LazyVals.LazyHolder
5+
import org.openjdk.jmh.infra.Blackhole
6+
import java.util.concurrent.TimeUnit
7+
8+
@BenchmarkMode(Array(Mode.AverageTime))
9+
@Fork(2)
10+
@Threads(1)
11+
@Warmup(iterations = 5)
12+
@Measurement(iterations = 5)
13+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
14+
@State(Scope.Benchmark)
15+
class InitializedAccess {
16+
17+
var holder: LazyHolder = _
18+
19+
@Setup
20+
def prepare: Unit = {
21+
holder = new LazyHolder
22+
holder.value
23+
}
24+
25+
@Benchmark
26+
def measureInitialized(bh: Blackhole) = {
27+
bh.consume(holder)
28+
bh.consume(holder.value)
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package dotty.tools.benchmarks.lazyvals
2+
3+
import org.openjdk.jmh.annotations._
4+
import LazyVals.LazyAnyHolder
5+
import org.openjdk.jmh.infra.Blackhole
6+
import java.util.concurrent.TimeUnit
7+
8+
@BenchmarkMode(Array(Mode.AverageTime))
9+
@Fork(2)
10+
@Threads(1)
11+
@Warmup(iterations = 5)
12+
@Measurement(iterations = 5)
13+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
14+
@State(Scope.Benchmark)
15+
class InitializedAccessAny {
16+
17+
var holder: LazyAnyHolder = _
18+
19+
@Setup
20+
def prepare: Unit = {
21+
holder = new LazyAnyHolder
22+
holder.value
23+
}
24+
25+
@Benchmark
26+
def measureInitialized(bh: Blackhole) = {
27+
bh.consume(holder)
28+
bh.consume(holder.value)
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package dotty.tools.benchmarks.lazyvals
2+
3+
import org.openjdk.jmh.annotations._
4+
import LazyVals.LazyGenericHolder
5+
import org.openjdk.jmh.infra.Blackhole
6+
import java.util.concurrent.TimeUnit
7+
8+
@BenchmarkMode(Array(Mode.AverageTime))
9+
@Fork(2)
10+
@Threads(1)
11+
@Warmup(iterations = 5)
12+
@Measurement(iterations = 5)
13+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
14+
@State(Scope.Benchmark)
15+
class InitializedAccessGeneric {
16+
17+
var holder: LazyGenericHolder[String] = _
18+
19+
@Setup
20+
def prepare: Unit = {
21+
holder = new LazyGenericHolder[String]("foo")
22+
holder.value
23+
}
24+
25+
@Benchmark
26+
def measureInitialized(bh: Blackhole) = {
27+
bh.consume(holder)
28+
bh.consume(holder.value)
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package dotty.tools.benchmarks.lazyvals
2+
3+
import org.openjdk.jmh.annotations._
4+
import LazyVals.LazyHolder
5+
import org.openjdk.jmh.infra.Blackhole
6+
import java.util.concurrent.TimeUnit
7+
8+
@BenchmarkMode(Array(Mode.AverageTime))
9+
@Fork(2)
10+
@Threads(1)
11+
@Warmup(iterations = 5)
12+
@Measurement(iterations = 5)
13+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
14+
@State(Scope.Benchmark)
15+
class InitializedAccessMultiple {
16+
17+
var holders: Array[LazyHolder] = _
18+
19+
@Setup
20+
def prepare: Unit = {
21+
holders = Array.fill(100){ new LazyHolder }
22+
}
23+
24+
@Benchmark
25+
def measureInitialized(bh: Blackhole) = {
26+
var i = 0
27+
while(i < 100) {
28+
val currentHolder = holders(i)
29+
bh.consume(currentHolder)
30+
bh.consume(currentHolder.value)
31+
i = i + 1
32+
}
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package dotty.tools.benchmarks.lazyvals
2+
3+
import org.openjdk.jmh.annotations._
4+
import LazyVals.LazyStringHolder
5+
import org.openjdk.jmh.infra.Blackhole
6+
import java.util.concurrent.TimeUnit
7+
8+
@BenchmarkMode(Array(Mode.AverageTime))
9+
@Fork(2)
10+
@Threads(1)
11+
@Warmup(iterations = 5)
12+
@Measurement(iterations = 5)
13+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
14+
@State(Scope.Benchmark)
15+
class InitializedAccessString {
16+
17+
var holder: LazyStringHolder = _
18+
19+
@Setup
20+
def prepare: Unit = {
21+
holder = new LazyStringHolder
22+
holder.value
23+
}
24+
25+
@Benchmark
26+
def measureInitialized(bh: Blackhole) = {
27+
bh.consume(holder)
28+
bh.consume(holder.value)
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package dotty.tools.benchmarks.lazyvals
2+
import java.util.concurrent.CountDownLatch
3+
object LazyVals {
4+
5+
trait Foo
6+
class Bar1 extends Foo
7+
class Bar2 extends Foo
8+
class Bar3 extends Foo
9+
class Bar4 extends Foo
10+
class Bar5 extends Bar4
11+
12+
class LazyStringHolder {
13+
14+
lazy val value: String = {
15+
System.nanoTime() % 5 match {
16+
case 0 => "abc"
17+
case 1 => "def"
18+
case 2 => "ghi"
19+
case 3 => "jkl"
20+
case 4 => "mno"
21+
}
22+
}
23+
}
24+
25+
class LazyHolder {
26+
27+
lazy val value: List[Int] = {
28+
System.nanoTime() % 5 match {
29+
case 0 => 1 :: 2 :: Nil
30+
case 1 => Nil
31+
case 2 => 1 :: Nil
32+
case 3 => Nil
33+
case 4 => 1 :: 2 :: 3 :: Nil
34+
}
35+
}
36+
}
37+
38+
class LazyGenericHolder[A](v: => A) {
39+
lazy val value: A = v
40+
}
41+
42+
class LazyAnyHolder {
43+
lazy val value: Any = {
44+
System.nanoTime() % 5 match {
45+
case 0 => new Bar1
46+
case 1 => new Bar2
47+
case 2 => new Bar3
48+
case 3 => new Bar4
49+
case 4 => new Bar4
50+
}
51+
}
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package dotty.tools.benchmarks.lazyvals
2+
3+
import org.openjdk.jmh.annotations._
4+
import LazyVals.LazyHolder
5+
import org.openjdk.jmh.infra.Blackhole
6+
import java.util.concurrent.TimeUnit
7+
8+
@BenchmarkMode(Array(Mode.AverageTime))
9+
@Fork(2)
10+
@Threads(1)
11+
@Warmup(iterations = 5)
12+
@Measurement(iterations = 5)
13+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
14+
@State(Scope.Benchmark)
15+
class UninitializedAccess {
16+
17+
@Benchmark
18+
def measureInitialized(bh: Blackhole) = {
19+
var i = 0
20+
val holder = new LazyHolder
21+
bh.consume(holder)
22+
bh.consume(holder.value)
23+
i = i + 1
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package dotty.tools.benchmarks.lazyvals
2+
3+
import org.openjdk.jmh.annotations._
4+
import LazyVals.LazyHolder
5+
import org.openjdk.jmh.infra.Blackhole
6+
import java.util.concurrent.TimeUnit
7+
8+
@BenchmarkMode(Array(Mode.AverageTime))
9+
@Fork(2)
10+
@Threads(1)
11+
@Warmup(iterations = 5)
12+
@Measurement(iterations = 5)
13+
@OutputTimeUnit(TimeUnit.NANOSECONDS)
14+
@State(Scope.Benchmark)
15+
class UninitializedAccessMultiple {
16+
17+
@Benchmark
18+
def measureInitialized(bh: Blackhole) = {
19+
var i = 0
20+
while(i < 100) {
21+
val holder = new LazyHolder
22+
bh.consume(holder)
23+
bh.consume(holder.value)
24+
i = i + 1
25+
}
26+
}
27+
}

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

+1
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ private sealed trait YSettings:
330330
val YrecheckTest: Setting[Boolean] = BooleanSetting("-Yrecheck-test", "Run basic rechecking (internal test only)")
331331
val YccDebug: Setting[Boolean] = BooleanSetting("-Ycc-debug", "Used in conjunction with captureChecking language import, debug info for captured references")
332332
val YccNoAbbrev: Setting[Boolean] = BooleanSetting("-Ycc-no-abbrev", "Used in conjunction with captureChecking language import, suppress type abbreviations")
333+
val YlightweightLazyVals: Setting[Boolean] = BooleanSetting("-Ylightweight-lazy-vals", "Use experimental lightweight implementation of lazy vals")
333334

334335
/** Area-specific debug output */
335336
val YexplainLowlevel: Setting[Boolean] = BooleanSetting("-Yexplain-lowlevel", "When explaining type errors, show types at a lower level.")

compiler/src/dotty/tools/dotc/core/Definitions.scala

+6
Original file line numberDiff line numberDiff line change
@@ -1990,6 +1990,12 @@ class Definitions {
19901990
addSyntheticSymbolsComments
19911991
}
19921992

1993+
/** Definitions used in Lazy Vals implementation */
1994+
val LazyValsModuleName = "scala.runtime.LazyVals"
1995+
@tu lazy val LazyValsModule = requiredModule(LazyValsModuleName)
1996+
@tu lazy val LazyValsWaitingState = requiredClass(s"$LazyValsModuleName.Waiting")
1997+
@tu lazy val LazyValsControlState = requiredClass(s"$LazyValsModuleName.LazyValControlState")
1998+
19931999
def addSyntheticSymbolsComments(using Context): Unit =
19942000
def add(sym: Symbol, doc: String) = ctx.docCtx.foreach(_.addDocstring(sym, Some(Comment(NoSpan, doc))))
19952001

0 commit comments

Comments
 (0)