Skip to content

Commit

Permalink
Simplify AnnotationsMappingBenchmark
Browse files Browse the repository at this point in the history
  • Loading branch information
mbovel committed May 6, 2024
1 parent 61139c4 commit d39973c
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 168 deletions.
38 changes: 38 additions & 0 deletions bench-micro/scripts/compare.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env bash

name=$1
commit_1=$2
commit_2=$3
runs=2

if [ -z "$name" ] || [ -z "$commit_1" ] || [ -z "$commit_2" ]; then
echo "Usage: $0 <benchmark-name> <commit-1> <commit-2>"
exit 1
fi

run_benchmark() {
jvm=$1
jvm_coursier_id=$2
run=$3
hash=$4
json_file=results/$name-$jvm-$run.json
txt_file=results/$name-$jvm-$run.txt
echo "Running $name benchmark with JVM $jvm at commit $hash, run $run out of $runs"
git checkout $hash
git clean -fdx
eval "$(coursier java --jvm "$jvm_coursier_id" --env)"
rm -rf "$json_file" "$stdout_file" .bloop .sbt .bsp .metals target
sbt "clean; scala3-bench-micro / Jmh / run -rf JSON -rff $json_file -o $txt_file $name"
}

read -p "WARNING: this script will brutally reset your Dotty repo (git clean -fdx). Type 'y' to continue." -n 1 -r
echo # new line
if [[ $REPLY =~ ^[Yy]$ ]]
then
for run in $(seq 1 $runs); do
for hash in $commit_1 $commit_2; do
run_benchmark openjdk "adoptium:1.17.0.11" $run $hash
run_benchmark graal "graalvm-java17:22.3.3" $run $hash
done
done
fi
21 changes: 0 additions & 21 deletions bench-micro/scripts/run.sh

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,11 @@ class AnnotationsMappingBenchmark:
@Param(Array("v1", "v2", "v3", "v4"))
var valName: String = null

@Param(Array("current", "oldCheck", "newCheckEquals", "newCheckEq", "noCheck", "noCheckCopySymbols"))
var typeMapName: String = null

@Param(Array("id", "mapInts"))
var typeFunctionName: String = null

@Setup(Level.Iteration)
def setup(): Unit =
/** A custom phase that is used to retrieve the `Type`s and `Context` to be
* used in the benchmark.
*/
val testPhase =
new Phase:
final override def phaseName = "testPhase"
Expand All @@ -58,20 +52,15 @@ class AnnotationsMappingBenchmark:
specialIntTp = pkg.requiredClass("Test").requiredType("SpecialInt").typeRef
context = ctx

/** A custom compiler that only runs the `Parser`, `TyperPhase` and
* `testPhase`.
*/
val compiler =
new Compiler:
private final val baseCompiler = new Compiler()
final override def phases = List(List(Parser()), List(TyperPhase()), List(testPhase))

/** A custom driver that uses `compiler`. */
val driver =
new Driver:
final override def newCompiler(using Context): Compiler = compiler

// Runs the driver with the test file.
driver.process(Array("-classpath", System.getProperty("BENCH_CLASS_PATH"), "tests/someAnnotatedTypes.scala"))

typeFunction =
Expand All @@ -80,142 +69,8 @@ class AnnotationsMappingBenchmark:
case "mapInts" => tp => (if tp frozen_=:= defn.IntType then specialIntTp else tp)
case _ => throw new IllegalArgumentException(s"Unknown type function: $typeFunctionName")

/** Creates a new `TypeMap` that uses `mapConcreteAnnotationWith` to map
* concrete annotations. It is used to compare several ways to map these
* annotations.
*/
def makeTypeMap(mapConcreteAnnotationWith: (ConcreteAnnotation, TypeMap) => Context ?=> Annotation) =
typeMap =
new TypeMap(using context):
final override def apply(tp: Type): Type = typeFunction(mapOver(tp))
final override def mapOver(tp: Type) =
tp match
case tp @ AnnotatedType(underlying, annot) =>
val underlying1 = this(underlying)
val annot1 =
annot match
case annot: ConcreteAnnotation => mapConcreteAnnotationWith(annot, this)
case _ => annot.mapWith(this)
if annot1 eq EmptyAnnotation then underlying1
else derivedAnnotatedType(tp, underlying1, annot1)
case _ => super.mapOver(tp)

/** Retrieves all argument from a tree. This old implementation does not
* include type arguments.
*/
def oldAllArguments(tree: Tree)(using Context): List[Tree] =
tpd.unsplice(tree) match
case Apply(fn, args) => oldAllArguments(fn) ::: args
case TypeApply(fn, _) => oldAllArguments(fn)
case Block(_, expr) => oldAllArguments(expr)
case _ => Nil

/** This is the old (<= d1489734b7) implementation of `Annotation.mapWith`.
* It 1. does not include type arguments and 2. uses `frozen_=:=` to
* compare types and 3. does not copy all symbols.
*/
def oldMapWith(annot: ConcreteAnnotation, tm: TypeMap)(using Context): Annotation =
val tree = annot.tree
val args = oldAllArguments(tree)
if args.isEmpty then annot
else
val findDiff = new TreeAccumulator[Type]:
def apply(x: Type, tree: Tree)(using Context): Type =
if tm.isRange(x) then x
else
val tp1 = tm(tree.tpe)
foldOver(if tp1 frozen_=:= tree.tpe then x else tp1, tree)
val diff = findDiff(NoType, args)
if tm.isRange(diff) then EmptyAnnotation
else if diff.exists then annot.derivedAnnotation(tm.mapOver(tree))
else annot

/** Retrieves all argument from a tree, including type arguments. */
def newAllArguments(tree: Tree)(using Context): List[Tree] =
tpd.unsplice(tree) match
case Apply(fn, args) => newAllArguments(fn) ::: args
case TypeApply(fn, args) => newAllArguments(fn) ::: args
case Block(_, expr) => newAllArguments(expr)
case _ => Nil

/** This is the new implementation of `Annotation.mapWith`. It 1. includes
* type arguments and 2. uses `==` to compare types and 3. copies all
* symbols by using a custom `TreeTypeMap` that overrides `withMappedSyms`.
*/
def newMapWithEquals(annot: ConcreteAnnotation, tm: TypeMap)(using Context): Annotation =
val tree = annot.tree
val args = newAllArguments(tree)
if args.isEmpty then annot
else
val findDiff = new TreeAccumulator[Type]:
def apply(x: Type, tree: Tree)(using Context): Type =
if tm.isRange(x) then x
else
val tp1 = tm(tree.tpe)
foldOver(if tp1 == tree.tpe then x else tp1, tree)
val diff = findDiff(NoType, args)
if tm.isRange(diff) then EmptyAnnotation
else if diff.exists then
val ttm =
new TreeTypeMap(tm):
final override def withMappedSyms(syms: List[Symbol]): TreeTypeMap =
withMappedSyms(syms, mapSymbols(syms, this, mapAlways = true))
annot.derivedAnnotation(ttm.transform(tree))
else annot

/** Exactly the same as `newMapWithEquals`, but uses `eq` instead of `==` to
* compare types.
*/
def newMapWithEq(annot: ConcreteAnnotation, tm: TypeMap)(using Context): Annotation =
val tree = annot.tree
val args = newAllArguments(tree)
if args.isEmpty then annot
else
val findDiff = new TreeAccumulator[Type]:
def apply(x: Type, tree: Tree)(using Context): Type =
if tm.isRange(x) then x
else
val tp1 = tm(tree.tpe)
foldOver(if tp1 eq tree.tpe then x else tp1, tree)
val diff = findDiff(NoType, args)
if tm.isRange(diff) then EmptyAnnotation
else if diff.exists then
val ttm =
new TreeTypeMap(tm):
final override def withMappedSyms(syms: List[Symbol]): TreeTypeMap =
withMappedSyms(syms, mapSymbols(syms, this, mapAlways = true))
annot.derivedAnnotation(ttm.transform(tree))
else annot

def noCheckMapWith(annot: ConcreteAnnotation, tm: TypeMap)(using Context): Annotation =
annot.derivedAnnotation(tm.mapOver(annot.tree))

def noCheckCopySymbolsMapWith(annot: ConcreteAnnotation, tm: TypeMap)(using Context): Annotation =
val ttm =
new TreeTypeMap(tm):
final override def withMappedSyms(syms: List[Symbol]): TreeTypeMap =
withMappedSyms(syms, mapSymbols(syms, this, mapAlways = true))
annot.derivedAnnotation(ttm.transform(annot.tree))

typeMap =
typeMapName match
case "current" =>
new TypeMap(using context):
final override def apply(tp: Type): Type = typeFunction(mapOver(tp))
case "oldCheck" =>
makeTypeMap(oldMapWith)
case "newCheckEquals" =>
// This should be the same as `current`, modulo a few indirections.
makeTypeMap(newMapWithEq)
case "newCheckEq" =>
makeTypeMap(newMapWithEq)
case "noCheck" =>
makeTypeMap(noCheckMapWith)
case "noCheckCopySymbols" =>
makeTypeMap(noCheckCopySymbolsMapWith)
case _ =>
throw new IllegalArgumentException(s"Unknown type map: $typeMapName")

@Benchmark
def applyTypeMap() =
val res = typeMap.apply(tp)
// println(res.show(using context))
@Benchmark def applyTypeMap() = typeMap.apply(tp)

0 comments on commit d39973c

Please sign in to comment.