From a6f451415e5a124c6c2044d8a182911ed8efc5d5 Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Wed, 1 May 2024 17:59:40 +0200 Subject: [PATCH] Simplify AnnotationsMappingBenchmark --- bench-micro/scripts/compare.sh | 47 ++++++ bench-micro/scripts/run.sh | 21 --- .../AnnotationsMappingBenchmark.scala | 157 +----------------- 3 files changed, 52 insertions(+), 173 deletions(-) create mode 100755 bench-micro/scripts/compare.sh delete mode 100755 bench-micro/scripts/run.sh diff --git a/bench-micro/scripts/compare.sh b/bench-micro/scripts/compare.sh new file mode 100755 index 000000000000..00458b44623f --- /dev/null +++ b/bench-micro/scripts/compare.sh @@ -0,0 +1,47 @@ +#!/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 " + exit 1 +fi + +run_benchmark() { + jvm=$1 + jvm_coursier_id=$2 + run=$3 + hash=$4 + json_file=../../results/$hash-$name-$jvm-$run.json + txt_file=../../results/$hash-$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 + +read -p "This script should be run form the Dotty root repository. Type 'y' to continue." -n 1 -r +echo # new line + +read -p "This script will create an empty directory /../results to store results. Type 'y' to continue." -n 1 -r +echo # new line + +mkdir ../results + +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 diff --git a/bench-micro/scripts/run.sh b/bench-micro/scripts/run.sh deleted file mode 100755 index 06b2445d2391..000000000000 --- a/bench-micro/scripts/run.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash - -name=$1 -runs=2 - -run_benchmark() { - jvm=$1 - jvm_coursier_id=$2 - run=$3 - json_file=results/$name-$jvm-$run.json - txt_file=results/$name-$jvm-$run.txt - - 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" -} - -for run in $(seq 1 $runs); do - run_benchmark openjdk "adoptium:1.17.0.11" $run - run_benchmark graal "graalvm-java17:22.3.3" $run -done diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/AnnotationsMappingBenchmark.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/AnnotationsMappingBenchmark.scala index dd2967790454..6be35e156841 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/AnnotationsMappingBenchmark.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/AnnotationsMappingBenchmark.scala @@ -21,11 +21,9 @@ import dotty.tools.dotc.typer.TyperPhase * * Run with: scala3-bench-micro / Jmh / run AnnotationsMappingBenchmark */ -@Fork(value = 5) -// Set to 0 to record all iterations. We remove the first iterations manually -// when processing the results. -@Warmup(iterations = 0, time = 1, timeUnit = SECONDS) -@Measurement(iterations = 10, time = 1, timeUnit = SECONDS) +@Fork(value = 4) +@Warmup(iterations = 4, time = 1, timeUnit = SECONDS) +@Measurement(iterations = 4, time = 1, timeUnit = SECONDS) @BenchmarkMode(Array(JMHMode.Throughput)) @State(Scope.Thread) class AnnotationsMappingBenchmark: @@ -38,17 +36,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" @@ -58,20 +50,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 = @@ -80,142 +67,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)