Skip to content

Commit

Permalink
Initial benchmarks for intersection types + a bit of speedup (#11924)
Browse files Browse the repository at this point in the history
  • Loading branch information
JaroslavTulach authored Jan 7, 2025
1 parent a755461 commit 2ead3f5
Show file tree
Hide file tree
Showing 16 changed files with 675 additions and 160 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package org.enso.interpreter.bench.benchmarks.semantic;

import java.util.concurrent.TimeUnit;
import org.enso.common.MethodNames;
import org.enso.compiler.benchmarks.Utils;
import org.graalvm.polyglot.Value;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.Blackhole;

/**
* These benchmarks compare performance of {@link EnsoMultiValue}. They create a vector in a certain
* configuration representing numbers and then they perform {@code sum} operation on it.
*/
@BenchmarkMode(Mode.AverageTime)
@Fork(1)
@Warmup(iterations = 3, time = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class MultiValueBenchmarks {
private Value arrayOfNumbers;
private Value sum;
private Value self;
private final long length = 100000;

@Setup
public void initializeBenchmark(BenchmarkParams params) throws Exception {
var ctx = Utils.createDefaultContextBuilder().build();
var code =
"""
from Standard.Base import Vector, Float, Number, Integer
type Complex
private Number re:Float im:Float
Complex.from (that:Number) = Complex.Number that 0
sum arr =
go acc i = if i >= arr.length then acc else
v = arr.at i
sum = acc + v
@Tail_Call go sum i+1
go 0 0
sum_re arr =
go acc i = if i >= arr.length then acc else
v = arr.at i . re
sum = acc + v
@Tail_Call go sum i+1
go 0 0
make_vector typ n =
Vector.new n i->
r = 3 + 5*i
case typ of
0 -> r:Complex
1 -> r:Integer
2 -> r:Float
3 ->
c = r:Complex&Float
c:Float
4 ->
c = r:Float&Complex
c:Float
5 -> r:Complex&Float
6 -> r:Float&Complex
""";
var benchmarkName = SrcUtil.findName(params);
var src = SrcUtil.source(benchmarkName, code);
int type = Integer.parseInt(benchmarkName.substring(benchmarkName.length() - 1));

var module = ctx.eval(src);
this.self = module.invokeMember(MethodNames.Module.GET_ASSOCIATED_TYPE);
var makeVector = module.invokeMember("get_method", self, "make_vector");
this.arrayOfNumbers = makeVector.execute(self, type, length);
this.sum =
module.invokeMember(MethodNames.Module.EVAL_EXPRESSION, type == 0 ? "sum_re" : "sum");
}

/**
* The <b>base benchmark</b> for this suite. Measures how much it takes to access an Atom in a
* Vector, read {@code re:Float} field out of it and sum all of them together.
*/
@Benchmark
public void sumOverComplexBaseBenchmark0(Blackhole matter) {
performBenchmark(matter);
}

/**
* Working with {@code Integer} should be the fastest. The plus operation on integer are faster
* than those on {@code Float} and moreover the {@code Vector} has a special representation when
* full of {@code long} values.
*/
@Benchmark
public void sumOverInteger1(Blackhole matter) {
performBenchmark(matter);
}

/**
* Working with {@code Float} should be also fast. The {@code Vector} has a special representation
* when full of {@code double} values that increases cache locality.
*/
@Benchmark
public void sumOverFloat2(Blackhole matter) {
performBenchmark(matter);
}

//
// Following benchmarks shall catch up with the base benchmark
//

@Benchmark
public void sumOverComplexFloatRecastedToFloat3(Blackhole matter) {
performBenchmark(matter);
}

@Benchmark
public void sumOverFloatComplexRecastedToFloat4(Blackhole matter) {
performBenchmark(matter);
}

@Benchmark
public void sumOverComplexAndFloat5(Blackhole matter) {
performBenchmark(matter);
}

@Benchmark
public void sumOverFloatAndComplex6(Blackhole matter) {
performBenchmark(matter);
}

private void performBenchmark(Blackhole matter) throws AssertionError {
var resultValue = sum.execute(arrayOfNumbers);
if (!resultValue.fitsInLong()) {
throw new AssertionError("Shall be a long: " + resultValue);
}
long result = resultValue.asLong();
long expectedResult = length * 3L + (5L * (length * (length - 1L) / 2L));
boolean isResultCorrect = result == expectedResult;
if (!isResultCorrect) {
throw new AssertionError("Expecting " + expectedResult + " but was " + result);
}
matter.consume(result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,17 +91,24 @@ private static void registerValue(
if (rawValue instanceof EnsoMultiValue) {
return;
}
var rawInt = (Type) ContextUtils.unwrapValue(ctx(), g.typeInteger());
var rawType = ContextUtils.unwrapValue(ctx(), t);
if (rawType instanceof Type type) {
var singleMultiValue = EnsoMultiValue.create(new Type[] {type}, 1, new Object[] {rawValue});
if (rawType == rawInt) {
return;
}
var singleMultiValue =
EnsoMultiValue.NewNode.getUncached()
.newValue(new Type[] {type}, 1, new Object[] {rawValue});
var n = t.getMetaSimpleName();
data.add(new Object[] {singleMultiValue, n, 0});
var rawInt = (Type) ContextUtils.unwrapValue(ctx(), g.typeInteger());
var secondMultiValue =
EnsoMultiValue.create(new Type[] {rawInt, type}, 2, new Object[] {5L, rawValue});
EnsoMultiValue.NewNode.getUncached()
.newValue(new Type[] {rawInt, type}, 2, new Object[] {5L, rawValue});
data.add(new Object[] {secondMultiValue, n, 1});
var firstMultiValue =
EnsoMultiValue.create(new Type[] {type, rawInt}, 2, new Object[] {rawValue, 6L});
EnsoMultiValue.NewNode.getUncached()
.newValue(new Type[] {type, rawInt}, 2, new Object[] {rawValue, 6L});
data.add(new Object[] {firstMultiValue, n, 0});
} else {
if (!t.isHostObject()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ public void multiValueWithHiddenType() {
new Type[] {
ensoCtx.getBuiltins().number().getInteger(), ensoCtx.getBuiltins().text()
};
var multi = EnsoMultiValue.create(types, 1, new Object[] {42L, "Meaning"});
var multi =
EnsoMultiValue.NewNode.getUncached()
.newValue(types, 1, new Object[] {42L, "Meaning"});
var arr = (Object[]) testTypesCall.call(multi, true);
var allTypes = (Type[]) arr[1];
assertEquals("Two types", 2, allTypes.length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ public void avoidDoubleWrappingOfEnsoMultiValue() {
() -> {
var builtins = ContextUtils.leakContext(ctx).getBuiltins();
var m1 =
EnsoMultiValue.create(
new Type[] {builtins.text(), builtins.number().getInteger()},
2,
new Object[] {"Hi", 42});
EnsoMultiValue.NewNode.getUncached()
.newValue(
new Type[] {builtins.text(), builtins.number().getInteger()},
2,
new Object[] {"Hi", 42});
assertEquals("Text & Integer", m1.toDisplayString(true));

var res = convert.call(m1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ public void multiValueToInteger() throws Exception {
""";
var conv =
ContextUtils.evalModule(ctx, Source.newBuilder("enso", code, "conv.enso").build(), "conv");
var both = EnsoMultiValue.create(types, types.length, new Object[] {2L, Text.create("Two")});
var both =
EnsoMultiValue.NewNode.getUncached()
.newValue(types, types.length, new Object[] {2L, Text.create("Two")});
var eq =
ContextUtils.executeInContext(
ctx,
Expand Down Expand Up @@ -104,7 +106,9 @@ private void multiValueToText(int dispatchLength) throws Exception {
""";
var conv =
ContextUtils.evalModule(ctx, Source.newBuilder("enso", code, "conv.enso").build(), "conv");
var both = EnsoMultiValue.create(types, dispatchLength, new Object[] {2L, Text.create("Two")});
var both =
EnsoMultiValue.NewNode.getUncached()
.newValue(types, dispatchLength, new Object[] {2L, Text.create("Two")});
var eq =
ContextUtils.executeInContext(
ctx,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ private static void registerValue(
if (r2 instanceof EnsoMultiValue) {
return;
}
var both = EnsoMultiValue.create(new Type[] {typ1, typ2}, 2, new Object[] {r1, r2});
if (typ1 == typ2) {
return;
}
var both =
EnsoMultiValue.NewNode.getUncached()
.newValue(new Type[] {typ1, typ2}, 2, new Object[] {r1, r2});
data.add(new Object[] {both});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ public void testEqualityIntegerAndMultiValue() {
var intType = builtins.number().getInteger();
var textText = builtins.text();
var fourExtraText =
EnsoMultiValue.create(
new Type[] {intType, textText}, 1, new Object[] {4L, Text.create("Hi")});
EnsoMultiValue.NewNode.getUncached()
.newValue(
new Type[] {intType, textText}, 1, new Object[] {4L, Text.create("Hi")});

assertTrue("4 == 4t", equalityCheck(4L, fourExtraText));
assertFalse("5 != 4t", equalityCheck(5L, fourExtraText));
Expand Down Expand Up @@ -85,7 +86,7 @@ public void testEqualityTextAndExtraIntegerMultiValue() {
// x = _ : (Text & Integer) : Text
// e.g. multi value with Text and Integer, casted to Text only
//
var multiV = EnsoMultiValue.create(bothTypes, 1, text, integer);
var multiV = EnsoMultiValue.NewNode.getUncached().newValue(bothTypes, 1, text, integer);

assertTrue("'Hi' == multiV", equalityCheck(text, multiV));
assertFalse("'Ahoj' != multiV", equalityCheck(ahoj, multiV));
Expand All @@ -112,11 +113,14 @@ public void testEqualityIntegerAndMultiValueWithBoth() {
var textText = builtins.text();
var hi = Text.create("Hi");
var textFour =
EnsoMultiValue.create(new Type[] {textText, intType}, 2, new Object[] {hi, 4L});
EnsoMultiValue.NewNode.getUncached()
.newValue(new Type[] {textText, intType}, 2, new Object[] {hi, 4L});
var textFive =
EnsoMultiValue.create(new Type[] {textText, intType}, 2, new Object[] {hi, 5L});
EnsoMultiValue.NewNode.getUncached()
.newValue(new Type[] {textText, intType}, 2, new Object[] {hi, 5L});
var fourText =
EnsoMultiValue.create(new Type[] {intType, textText}, 2, new Object[] {4L, hi});
EnsoMultiValue.NewNode.getUncached()
.newValue(new Type[] {intType, textText}, 2, new Object[] {4L, hi});

assertFalse("4 != t", equalityCheck(4L, hi));
assertFalse("4 != 4t", equalityCheck(4L, textFour));
Expand All @@ -143,8 +147,9 @@ public void testEqualityIntegerAndMultiValueWithIntText() {
var intType = builtins.number().getInteger();
var textText = builtins.text();
var fourExtraText =
EnsoMultiValue.create(
new Type[] {intType, textText}, 2, new Object[] {4L, Text.create("Hi")});
EnsoMultiValue.NewNode.getUncached()
.newValue(
new Type[] {intType, textText}, 2, new Object[] {4L, Text.create("Hi")});

assertFalse("4 != 4t", equalityCheck(4L, fourExtraText));
assertFalse("5 != 4t", equalityCheck(5L, fourExtraText));
Expand All @@ -164,14 +169,17 @@ public void twoMultiValues() {
var intType = builtins.number().getInteger();
var textText = builtins.text();
var fourExtraText =
EnsoMultiValue.create(
new Type[] {intType, textText}, 1, new Object[] {4L, Text.create("Hi")});
EnsoMultiValue.NewNode.getUncached()
.newValue(
new Type[] {intType, textText}, 1, new Object[] {4L, Text.create("Hi")});
var fourExtraText2 =
EnsoMultiValue.create(
new Type[] {intType, textText}, 1, new Object[] {4L, Text.create("Hi")});
EnsoMultiValue.NewNode.getUncached()
.newValue(
new Type[] {intType, textText}, 1, new Object[] {4L, Text.create("Hi")});
var fiveExtraText =
EnsoMultiValue.create(
new Type[] {intType, textText}, 1, new Object[] {5L, Text.create("Hi")});
EnsoMultiValue.NewNode.getUncached()
.newValue(
new Type[] {intType, textText}, 1, new Object[] {5L, Text.create("Hi")});

assertFalse("!= for sure #1", equalityCheck(fiveExtraText, fourExtraText));
assertFalse("!= for sure #2", equalityCheck(fourExtraText, fiveExtraText));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,7 @@ public List<Value> numbersMultiText() {

private void addMultiToCollect(
List<Value> collect, Type[] types, int dispatchTypes, Object... values) {
var raw = EnsoMultiValue.create(types, dispatchTypes, values);
var raw = EnsoMultiValue.NewNode.getUncached().newValue(types, dispatchTypes, values);
var wrap = ctx.asValue(raw);
collect.add(wrap);
}
Expand Down
Loading

0 comments on commit 2ead3f5

Please sign in to comment.