Skip to content

Commit 2dd5966

Browse files
committed
Add AnnotationsMappingBenchmark
1 parent 85b1c0e commit 2dd5966

File tree

3 files changed

+149
-0
lines changed

3 files changed

+149
-0
lines changed

bench-micro/scripts/compare.sh

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env bash
2+
3+
name=$1
4+
commit_1=$2
5+
commit_2=$3
6+
runs=2
7+
8+
if [ -z "$name" ] || [ -z "$commit_1" ] || [ -z "$commit_2" ]; then
9+
echo "Usage: $0 <benchmark-name> <commit-1> <commit-2>"
10+
exit 1
11+
fi
12+
13+
run_benchmark() {
14+
jvm=$1
15+
jvm_coursier_id=$2
16+
run=$3
17+
hash=$4
18+
json_file=../../results/$hash-$name-$jvm-$run.json
19+
txt_file=../../results/$hash-$name-$jvm-$run.txt
20+
echo "Running $name benchmark with JVM $jvm at commit $hash, run $run out of $runs"
21+
git checkout $hash
22+
git clean -fdx
23+
eval "$(coursier java --jvm "$jvm_coursier_id" --env)"
24+
rm -rf "$json_file" "$stdout_file" .bloop .sbt .bsp .metals target
25+
sbt "clean; scala3-bench-micro / Jmh / run -rf JSON -rff $json_file -o $txt_file $name"
26+
}
27+
28+
read -p "WARNING: This script will brutally reset your Dotty repo (git clean -fdx). Type 'y' to continue." -n 1 -r
29+
echo # new line
30+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi
31+
32+
read -p "This script should be run form the Dotty root repository. Type 'y' to continue." -n 1 -r
33+
echo # new line
34+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi
35+
36+
read -p "This script will create an empty directory <dotty>/../results to store results. Type 'y' to continue." -n 1 -r
37+
echo # new line
38+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi
39+
40+
mkdir ../results
41+
42+
for run in $(seq 1 $runs); do
43+
for hash in $commit_1 $commit_2; do
44+
run_benchmark openjdk "adoptium:1.17.0.11" $run $hash
45+
run_benchmark graal "graalvm-java17:22.3.3" $run $hash
46+
done
47+
done
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package dotty.tools.benchmarks
2+
3+
import org.openjdk.jmh.annotations.{Benchmark, BenchmarkMode, Fork, Level, Measurement, Mode as JMHMode, Param, Scope, Setup, State, Warmup}
4+
import java.util.concurrent.TimeUnit.SECONDS
5+
6+
import dotty.tools.dotc.{Driver, Run, Compiler}
7+
import dotty.tools.dotc.ast.{tpd, TreeTypeMap}, tpd.{Apply, Block, Tree, TreeAccumulator, TypeApply}
8+
import dotty.tools.dotc.core.Annotations.{Annotation, ConcreteAnnotation, EmptyAnnotation}
9+
import dotty.tools.dotc.core.Contexts.{ContextBase, Context, ctx, withMode}
10+
import dotty.tools.dotc.core.Mode
11+
import dotty.tools.dotc.core.Phases.Phase
12+
import dotty.tools.dotc.core.Symbols.{defn, mapSymbols, Symbol}
13+
import dotty.tools.dotc.core.Types.{AnnotatedType, NoType, SkolemType, TermRef, Type, TypeMap}
14+
import dotty.tools.dotc.parsing.Parser
15+
import dotty.tools.dotc.typer.TyperPhase
16+
17+
/** Benchmark to compare different ways to map concrete annotations.
18+
*
19+
* The main performance bottleneck there is the mapping of the annotation's
20+
* inner tree of; a `TreeTypeMap` is much slower than a `TypeMap`.
21+
*
22+
* Run with: scala3-bench-micro / Jmh / run AnnotationsMappingBenchmark
23+
*/
24+
@Fork(value = 4)
25+
@Warmup(iterations = 4, time = 1, timeUnit = SECONDS)
26+
@Measurement(iterations = 4, time = 1, timeUnit = SECONDS)
27+
@BenchmarkMode(Array(JMHMode.Throughput))
28+
@State(Scope.Thread)
29+
class AnnotationsMappingBenchmark:
30+
var tp: Type = null
31+
var specialIntTp: Type = null
32+
var context: Context = null
33+
var typeFunction: Context ?=> Type => Type = null
34+
var typeMap: TypeMap = null
35+
36+
@Param(Array("v1", "v2", "v3", "v4"))
37+
var valName: String = null
38+
39+
@Param(Array("id", "mapInts"))
40+
var typeFunctionName: String = null
41+
42+
@Setup(Level.Iteration)
43+
def setup(): Unit =
44+
val testPhase =
45+
new Phase:
46+
final override def phaseName = "testPhase"
47+
final override def run(using ctx: Context): Unit =
48+
val pkg = ctx.compilationUnit.tpdTree.symbol
49+
tp = pkg.requiredClass("Test").requiredValueRef(valName).underlying
50+
specialIntTp = pkg.requiredClass("Test").requiredType("SpecialInt").typeRef
51+
context = ctx
52+
53+
val compiler =
54+
new Compiler:
55+
private final val baseCompiler = new Compiler()
56+
final override def phases = List(List(Parser()), List(TyperPhase()), List(testPhase))
57+
58+
val driver =
59+
new Driver:
60+
final override def newCompiler(using Context): Compiler = compiler
61+
62+
driver.process(Array("-classpath", System.getProperty("BENCH_CLASS_PATH"), "tests/someAnnotatedTypes.scala"))
63+
64+
typeFunction =
65+
typeFunctionName match
66+
case "id" => tp => tp
67+
case "mapInts" => tp => (if tp frozen_=:= defn.IntType then specialIntTp else tp)
68+
case _ => throw new IllegalArgumentException(s"Unknown type function: $typeFunctionName")
69+
70+
typeMap =
71+
new TypeMap(using context):
72+
final override def apply(tp: Type): Type = typeFunction(mapOver(tp))
73+
74+
@Benchmark def applyTypeMap() = typeMap.apply(tp)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
class Test:
2+
class FlagAnnot extends annotation.StaticAnnotation
3+
class StringAnnot(val s: String) extends annotation.StaticAnnotation
4+
class LambdaAnnot(val f: Int => Boolean) extends annotation.StaticAnnotation
5+
6+
type SpecialInt <: Int
7+
8+
val v1: Int @FlagAnnot = 42
9+
10+
val v2: Int @StringAnnot("hello") = 42
11+
12+
val v3: Int @LambdaAnnot(it => it == 42) = 42
13+
14+
val v4: Int @LambdaAnnot(it => {
15+
def g(x: Int, y: Int) = x - y + 5
16+
g(it, 7) * 2 == 80
17+
}) = 42
18+
19+
/*val v5: Int @LambdaAnnot(it => {
20+
class Foo(x: Int):
21+
def xPlus10 = x + 10
22+
def xPlus20 = x + 20
23+
def xPlus(y: Int) = x + y
24+
val foo = Foo(it)
25+
foo.xPlus10 - foo.xPlus20 + foo.xPlus(30) == 62
26+
}) = 42*/
27+
28+
def main(args: Array[String]): Unit = ???

0 commit comments

Comments
 (0)