Skip to content
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

Add compiler benchmarks #9158

Merged
merged 22 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
61a7966
runtime-benchmarks run with Truffle DSL processor
Akirathan Feb 23, 2024
95962c3
Add skeleton of some compiler benchmarks
Akirathan Feb 23, 2024
7cc217c
ManyLocalVars is a separate benchmark
Akirathan Feb 26, 2024
38170a1
Make sure there are no unused variables in ManyLocalVarsBenchmark
Akirathan Feb 26, 2024
5f79c1a
Compiler stdout and stderr is checked in teardown
Akirathan Feb 26, 2024
5c8da15
Set sensible measure and warmup params for ManyLocalVarsBenchmark
Akirathan Feb 26, 2024
e4396a1
Add ManyErrorsBenchmark
Akirathan Feb 26, 2024
a188e19
Explicitly specify Fork to ManyErrorsBenchmark.
Akirathan Feb 27, 2024
a737cb7
Ensure bench-data dir is created
Akirathan Feb 27, 2024
33e8edc
CodeGenerator keeps track of defined and used variables
Akirathan Feb 27, 2024
1ab1fc8
Created ManyNestedBlocksBenchmark
Akirathan Feb 27, 2024
a62396c
Add ManySmallMethodsBenchmark
Akirathan Feb 27, 2024
7377702
Remove unused ModuleCompilerBenchmarks.
Akirathan Feb 27, 2024
c4edb0b
fmt
Akirathan Feb 27, 2024
d9c3fb6
Finetune parameters of ManyLocalVarsBenchmark
Akirathan Feb 27, 2024
23434b8
Fix InlineCompilerBenchmarks
Akirathan Feb 27, 2024
c12457f
Merge branch 'develop' into wip/akirathan/8419-compiler-benchmarks
Akirathan Feb 28, 2024
52a35ef
Add inline compiler benchmark for errors
Akirathan Feb 29, 2024
4bda14a
Add ImportStandardLibrariesBenchmark
Akirathan Feb 29, 2024
43d0fc2
Add docs
Akirathan Mar 1, 2024
e20fd3c
fmt
Akirathan Mar 1, 2024
b2ddf95
Add docs
Akirathan Mar 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -1833,6 +1833,7 @@ lazy val `runtime-benchmarks` =
"jakarta.xml.bind" % "jakarta.xml.bind-api" % jaxbVersion,
"com.sun.xml.bind" % "jaxb-impl" % jaxbVersion,
"org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion,
"org.graalvm.truffle" % "truffle-dsl-processor" % graalMavenPackagesVersion % "provided",
"org.slf4j" % "slf4j-api" % slf4jVersion,
"org.slf4j" % "slf4j-nop" % slf4jVersion
),
Expand All @@ -1848,6 +1849,14 @@ lazy val `runtime-benchmarks` =
frgaalSourceLevel,
"--enable-preview"
),
javacOptions ++= Seq(
"-s",
(Compile / sourceManaged).value.getAbsolutePath,
"-Xlint:unchecked"
),
Compile / compile := (Compile / compile)
.dependsOn(Def.task { (Compile / sourceManaged).value.mkdirs })
.value,
parallelExecution := false,
modulePath := {
val requiredModIds = GraalVM.modules ++ GraalVM.langsPkgs ++ Seq(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
package org.enso.compiler.benchmarks;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

public class CodeGenerator {
private static final int MAX_TEXT_LITERAL_SIZE = 20;
private static final int SMALL_LETTERS_CNT = 26;
private static final int SEED = 42;

private static final List<String> OPERATORS =
List.of("+", "-", "*", "/", "&&", "||", "==", "!=", "<", ">", "<=", ">=");

private static final List<String> METHODS =
List.of(
"map", "filter", "foldl", "foldr", "head", "tail", "init", "last", "length", "reverse");

/**
* A collection of identifiers that were defined. I.e., for these identifiers, there exist
* assignments in the generated code.
*/
private final List<String> definedIdentifiers = new ArrayList<>();

/** A collection of identifiers that were used in some expression. */
private final Set<String> usedIdentifiers = new HashSet<>();

private final Random random = new Random(SEED);
private int identifierCnt = 0;

/** Creates a new empty code generator. */
public CodeGenerator() {}

/**
* @param identifiers Collection of already defined identifiers, like arguments to a function.
*/
public CodeGenerator(Collection<String> identifiers) {
definedIdentifiers.addAll(identifiers);
}

/**
* Creates an expression that initializes a new variable.
*
* @param expressionSize Size of the expression that will be used to initialize the variable.
* @return The assignment expression that defines the new variable
*/
public String defineNewVariable(int expressionSize) {
assert expressionSize >= 0;
var ident = nextIdentifier();
definedIdentifiers.add(ident);
var initExpr = createExpressionFromDefinedIdentifiers(expressionSize);
return ident + " = " + initExpr;
}

/** Returns a new identifiers and records it as a defined variable. */
public String defineNewVariable() {
var ident = nextIdentifier();
definedIdentifiers.add(ident);
return ident;
}

/**
* Randomly chooses one of the defined identifiers.
*
* @return The chosen identifier.
*/
public String chooseDefinedIdentifier() {
assert !definedIdentifiers.isEmpty();
var randomIdx = random.nextInt(definedIdentifiers.size());
return definedIdentifiers.get(randomIdx);
}

/**
* Returns a collection of identifiers that are not used in any expression. These identifiers were
* defined, but not used, so the compiler should generate a warning about them.
*
* @return A collection of unused identifiers.
*/
public Set<String> getUnusedIdentifiers() {
var allDefined = new HashSet<>(definedIdentifiers);
allDefined.removeAll(usedIdentifiers);
return allDefined;
}

public void markVariableAsUsed(String variable) {
assert definedIdentifiers.contains(variable);
usedIdentifiers.add(variable);
}

public List<String> createIdentifiers(int count) {
List<String> idents = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
idents.add(nextIdentifier());
}
return idents;
}

private String nextIdentifier() {
identifierCnt++;
return "operator" + identifierCnt;
}

private String nextMethod() {
var idx = random.nextInt(METHODS.size());
return METHODS.get(idx);
}

private String nextTextLiteral() {
var size = random.nextInt(MAX_TEXT_LITERAL_SIZE);
var sb = new StringBuilder();
for (int i = 0; i < size; i++) {
var rndChar = (char) ('a' + random.nextInt(SMALL_LETTERS_CNT));
sb.append(rndChar);
}
return "\"" + sb + "\"";
}

private String nextIntLiteral() {
return Integer.toString(random.nextInt());
}

private String nextDecimalLiteral() {
var part1 = random.nextInt();
var decimalPart = Math.abs(random.nextInt());
return part1 + "." + decimalPart;
}

public String nextLiteral() {
var rndInt = random.nextInt(3);
return switch (rndInt) {
case 0 -> nextTextLiteral();
case 1 -> nextIntLiteral();
case 2 -> nextDecimalLiteral();
default -> throw new UnsupportedOperationException("unimplemented");
};
}

/**
* Creates an expression using only the defined identifiers.
*
* @param size Arity of the expression.
*/
public String createExpressionFromDefinedIdentifiers(int size) {
return createExpression(definedIdentifiers, size);
}

/**
* Creates an expression with the given size.
*
* @param identifiers A collection of all the identifiers that can be used in the expression.
* These may be undefined identifiers. In that case, the compiler should throw an error.
* @param size Arity of the expression.
* @return A string representing the expression.
*/
public String createExpression(List<String> identifiers, int size) {
if (identifiers.isEmpty()) {
return nextLiteral();
}

return switch (size) {
case 0 -> nextLiteral();
// Either a single identifier or a method call on the identifier
case 1 -> {
var sb = new StringBuilder();
var ident = chooseIdentifier(identifiers);
usedIdentifiers.add(ident);
sb.append(ident);
var shouldCallMethod = random.nextBoolean();
if (shouldCallMethod) {
sb.append(".").append(nextMethod());
}
yield sb.toString();
}
// Method call or binary operator
case 2 -> {
var sb = new StringBuilder();
var shouldCallMethod = random.nextBoolean();
var ident1 = chooseIdentifier(identifiers);
usedIdentifiers.add(ident1);
if (shouldCallMethod) {
var methodArg = createExpression(identifiers, 1);
sb.append(ident1)
.append(".")
.append(nextMethod())
.append(" (")
.append(methodArg)
.append(")");
} else {
var ident2 = chooseIdentifier(identifiers);
usedIdentifiers.add(ident2);
// Binary operator
sb.append(ident1).append(nextOperator()).append(ident2);
}
yield sb.toString();
}
// Split into two expressions with random size
default -> {
var sb = new StringBuilder();
var shouldCallMethod = random.nextBoolean();
if (shouldCallMethod) {
var ident = chooseIdentifier(identifiers);
usedIdentifiers.add(ident);
var methodArity = size - 1;
List<String> methodArgs = new ArrayList<>();
for (int i = 0; i < methodArity; i++) {
methodArgs.add(createExpression(identifiers, size - 1));
}
sb.append(ident).append(".").append(nextMethod()).append(" ");
for (var methodArg : methodArgs) {
sb.append(methodArg).append(" ");
}
} else {
var rndIdx = Math.max(2, random.nextInt(size));
var size1 = rndIdx;
var size2 = size - rndIdx;
var expr1 = createExpression(identifiers, size1);
var expr2 = createExpression(identifiers, size2);
var op = nextOperator();
sb.append("(").append(expr1).append(")").append(op).append("(").append(expr2).append(")");
}
yield sb.toString();
}
};
}

private String nextOperator() {
var idx = random.nextInt(OPERATORS.size());
return OPERATORS.get(idx);
}

private String chooseIdentifier(List<String> identifiers) {
assert !identifiers.isEmpty();
var randomIdx = random.nextInt(identifiers.size());
return identifiers.get(randomIdx);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package org.enso.compiler.benchmarks;

import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.logging.Level;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.polyglot.LanguageInfo;
import org.enso.polyglot.MethodNames;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;

public class Utils {
public static Context.Builder createDefaultContextBuilder() {
return Context.newBuilder()
.allowExperimentalOptions(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.option(RuntimeOptions.DISABLE_IR_CACHES, "true")
.option(RuntimeOptions.STRICT_ERRORS, "true")
.logHandler(System.err)
.allowIO(IOAccess.ALL)
.allowAllAccess(true);
}

public static EnsoContext leakEnsoContext(Context ctx) {
return ctx.getBindings(LanguageInfo.ID)
.invokeMember(MethodNames.TopScope.LEAK_CONTEXT)
.as(EnsoContext.class);
}

public static Object unwrapReceiver(Context ctx, Value value) {
var unwrapper = new Unwrapper();
var unwrapperValue = ctx.asValue(unwrapper);
unwrapperValue.execute(value);
return unwrapper.args[0];
}

public static File createSrcFile(String code, String name) {
var benchDataDir = Path.of(".", "target", "bench-data");
benchDataDir.toFile().mkdirs();
var srcFile = benchDataDir.resolve(name).toFile();
try {
Files.writeString(srcFile.toPath(), code);
} catch (IOException e) {
throw new AssertionError(e);
}
return srcFile;
}

@ExportLibrary(InteropLibrary.class)
static final class Unwrapper implements TruffleObject {
Object[] args;

@ExportMessage
Object execute(Object[] args) {
this.args = args;
return this;
}

@ExportMessage
boolean isExecutable() {
return true;
}
}
}
Loading
Loading