Skip to content

Commit

Permalink
EnsoMultiValue.firstDispatch to speed benchmarks up (#11975)
Browse files Browse the repository at this point in the history
  • Loading branch information
JaroslavTulach authored Jan 10, 2025
1 parent b02e228 commit 7d9d69b
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,16 @@ private static void registerValue(
}
var singleMultiValue =
EnsoMultiValue.NewNode.getUncached()
.newValue(new Type[] {type}, 1, new Object[] {rawValue});
.newValue(new Type[] {type}, 1, 0, new Object[] {rawValue});
var n = t.getMetaSimpleName();
data.add(new Object[] {singleMultiValue, n, 0});
var secondMultiValue =
EnsoMultiValue.NewNode.getUncached()
.newValue(new Type[] {rawInt, type}, 2, new Object[] {5L, rawValue});
.newValue(new Type[] {rawInt, type}, 2, 0, new Object[] {5L, rawValue});
data.add(new Object[] {secondMultiValue, n, 1});
var firstMultiValue =
EnsoMultiValue.NewNode.getUncached()
.newValue(new Type[] {type, rawInt}, 2, new Object[] {rawValue, 6L});
.newValue(new Type[] {type, rawInt}, 2, 0, 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 @@ -95,7 +95,7 @@ public void multiValueWithHiddenType() {
};
var multi =
EnsoMultiValue.NewNode.getUncached()
.newValue(types, 1, new Object[] {42L, "Meaning"});
.newValue(types, 1, 0, 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 @@ -6,6 +6,7 @@
import com.oracle.truffle.api.CallTarget;
import org.enso.interpreter.runtime.data.EnsoMultiValue;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.test.utils.ContextUtils;
import org.enso.test.utils.TestRootNode;
import org.graalvm.polyglot.Context;
Expand Down Expand Up @@ -38,12 +39,11 @@ public void avoidDoubleWrappingOfEnsoMultiValue() {
ctx(),
() -> {
var builtins = ContextUtils.leakContext(ctx).getBuiltins();
var hi = Text.create("Hi");
var m1 =
EnsoMultiValue.NewNode.getUncached()
.newValue(
new Type[] {builtins.text(), builtins.number().getInteger()},
2,
new Object[] {"Hi", 42});
new Type[] {builtins.text(), builtins.number().getInteger()}, 2, 0, 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 @@ -62,7 +62,7 @@ public void multiValueToInteger() throws Exception {
ContextUtils.evalModule(ctx, Source.newBuilder("enso", code, "conv.enso").build(), "conv");
var both =
EnsoMultiValue.NewNode.getUncached()
.newValue(types, types.length, new Object[] {2L, Text.create("Two")});
.newValue(types, types.length, 0, new Object[] {2L, Text.create("Two")});
var eq =
ContextUtils.executeInContext(
ctx,
Expand Down Expand Up @@ -108,7 +108,7 @@ private void multiValueToText(int dispatchLength) throws Exception {
ContextUtils.evalModule(ctx, Source.newBuilder("enso", code, "conv.enso").build(), "conv");
var both =
EnsoMultiValue.NewNode.getUncached()
.newValue(types, dispatchLength, new Object[] {2L, Text.create("Two")});
.newValue(types, dispatchLength, 0, 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 @@ -68,8 +68,7 @@ private static void registerValue(
return;
}
var both =
EnsoMultiValue.NewNode.getUncached()
.newValue(new Type[] {typ1, typ2}, 2, new Object[] {r1, r2});
EnsoMultiValue.NewNode.getUncached().newValue(new Type[] {typ1, typ2}, 2, 0, r1, r2);
data.add(new Object[] {both});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public void testEqualityIntegerAndMultiValue() {
var fourExtraText =
EnsoMultiValue.NewNode.getUncached()
.newValue(
new Type[] {intType, textText}, 1, new Object[] {4L, Text.create("Hi")});
new Type[] {intType, textText}, 1, 0, new Object[] {4L, Text.create("Hi")});

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

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

assertFalse("4 != t", equalityCheck(4L, hi));
assertFalse("4 != 4t", equalityCheck(4L, textFour));
Expand Down Expand Up @@ -149,7 +150,7 @@ public void testEqualityIntegerAndMultiValueWithIntText() {
var fourExtraText =
EnsoMultiValue.NewNode.getUncached()
.newValue(
new Type[] {intType, textText}, 2, new Object[] {4L, Text.create("Hi")});
new Type[] {intType, textText}, 2, 0, new Object[] {4L, Text.create("Hi")});

assertFalse("4 != 4t", equalityCheck(4L, fourExtraText));
assertFalse("5 != 4t", equalityCheck(5L, fourExtraText));
Expand All @@ -171,15 +172,15 @@ public void twoMultiValues() {
var fourExtraText =
EnsoMultiValue.NewNode.getUncached()
.newValue(
new Type[] {intType, textText}, 1, new Object[] {4L, Text.create("Hi")});
new Type[] {intType, textText}, 1, 0, new Object[] {4L, Text.create("Hi")});
var fourExtraText2 =
EnsoMultiValue.NewNode.getUncached()
.newValue(
new Type[] {intType, textText}, 1, new Object[] {4L, Text.create("Hi")});
new Type[] {intType, textText}, 1, 0, new Object[] {4L, Text.create("Hi")});
var fiveExtraText =
EnsoMultiValue.NewNode.getUncached()
.newValue(
new Type[] {intType, textText}, 1, new Object[] {5L, Text.create("Hi")});
new Type[] {intType, textText}, 1, 0, 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.NewNode.getUncached().newValue(types, dispatchTypes, values);
var raw = EnsoMultiValue.NewNode.getUncached().newValue(types, dispatchTypes, 0, values);
var wrap = ctx.asValue(raw);
collect.add(wrap);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ Object executeCheckOrConversion(VirtualFrame frame, Object value, ExpressionNode
values = Arrays.copyOf(values, at);
valueTypes = Arrays.copyOf(valueTypes, at);
}
return newNode.newValue(valueTypes, valueTypes.length, values);
return newNode.newValue(valueTypes, valueTypes.length, 0, values);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,36 +147,46 @@ static AllTypesWith create() {
*
* @param first first set of types
* @param second second set of types
* @param moveToFirst what type index to move to first
* @return union of both types
*/
abstract Type[] executeAllTypes(EnsoMultiType first, EnsoMultiType second);
abstract Type[] executeAllTypes(EnsoMultiType first, EnsoMultiType second, int moveToFirst);

@Specialization(
limit = INLINE_CACHE_LIMIT,
guards = {
"self == cachedSelf",
"nextOrNull == cachedNextOrNull",
"moveToFirst == cachedMovedToFirst",
})
Type[] optimizeForTypes(
EnsoMultiType self,
EnsoMultiType nextOrNull,
int moveToFirst,
@Cached("self") EnsoMultiType cachedSelf,
@Cached("nextOrNull") EnsoMultiType cachedNextOrNull,
@Cached("slowlyComputeTypes(self, nextOrNull)") Type[] result) {
@Cached("moveToFirst") int cachedMovedToFirst,
@Cached("slowlyComputeTypes(self, nextOrNull, moveToFirst)") Type[] result) {
return result;
}

@Specialization(replaces = "optimizeForTypes")
Type[] slowlyComputeTypes(EnsoMultiType self, EnsoMultiType nextOrNull) {
Type[] slowlyComputeTypes(EnsoMultiType self, EnsoMultiType nextOrNull, int moveToFirst) {
Type[] concat;
if (nextOrNull == null || nextOrNull.types.length == 0) {
return self.types.clone();
concat = self.types.clone();
} else {
var next = nextOrNull;
var arr = new Type[self.types.length + next.types.length];
System.arraycopy(self.types, 0, arr, 0, self.types.length);
System.arraycopy(next.types, 0, arr, self.types.length, next.types.length);
return arr;
concat = new Type[self.types.length + next.types.length];
System.arraycopy(self.types, 0, concat, 0, self.types.length);
System.arraycopy(next.types, 0, concat, self.types.length, next.types.length);
}
if (moveToFirst != 0) {
var first = concat[0];
concat[0] = concat[moveToFirst];
concat[moveToFirst] = first;
}
return concat;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,14 @@
public final class EnsoMultiValue extends EnsoObject {
private final EnsoMultiType dispatch;
private final EnsoMultiType extra;
private final int firstDispatch;

@CompilationFinal(dimensions = 1)
private final Object[] values;

private EnsoMultiValue(EnsoMultiType dispatch, EnsoMultiType extra, Object[] values) {
assert !dispatch.hasIntersectionWith(extra)
: "Dispatch (" + dispatch + " and extra " + extra + " should be disjoin!";
private EnsoMultiValue(
EnsoMultiType dispatch, EnsoMultiType extra, Object[] values, int firstDispatch) {
this.firstDispatch = firstDispatch;
this.dispatch = dispatch;
this.extra = extra;
this.values = values;
Expand All @@ -75,22 +76,28 @@ public static NewNode getUncached() {
* @param types all the types this value can be {@link CastToNode cast to}
* @param dispatchTypes the (subset of) types that the value is cast to currently - bigger than
* {@code 0} and at most {@code type.length}
* @param firstDispatch location of first dispatch type in the values
* @param values value of each of the provided {@code types}
* @return non-{@code null} multi value instance
*/
@NeverDefault
public EnsoMultiValue newValue(
@NeverDefault Type[] types,
@NeverDefault int dispatchTypes,
@NeverDefault int firstDispatch,
@NeverDefault Object... values) {
assert firstDispatch >= 0;
assert dispatchTypes > 0;
assert dispatchTypes <= types.length;
assert types.length == values.length;
assert firstDispatch + dispatchTypes <= types.length;
assert !Stream.of(values).anyMatch(v -> v instanceof EnsoMultiValue)
: "Avoid double wrapping " + Arrays.toString(values);
var dt = executeTypes(types, 0, dispatchTypes);
var et = executeTypes(types, dispatchTypes, types.length);
return new EnsoMultiValue(dt, et, values);
assert !dt.hasIntersectionWith(et)
: "Dispatch (" + dt + " and extra " + et + " should be disjoin!";
return new EnsoMultiValue(dt, et, values, firstDispatch);
}

abstract EnsoMultiType executeTypes(Type[] types, int from, int to);
Expand Down Expand Up @@ -164,9 +171,9 @@ final Type getType() {
final Type[] allTypes(
boolean includeExtraTypes, @Cached EnsoMultiType.AllTypesWith allTypesWith) {
if (!includeExtraTypes) {
return allTypesWith.executeAllTypes(dispatch, null);
return allTypesWith.executeAllTypes(dispatch, null, 0);
} else {
return allTypesWith.executeAllTypes(dispatch, extra);
return allTypesWith.executeAllTypes(dispatch, extra, 0);
}
}

Expand All @@ -190,9 +197,9 @@ private enum InteropType {

private record Value(InteropType type, Object value) {}

static Value find(Object[] values, int max, InteropLibrary iop) {
static Value find(Object[] values, int firstDispatch, int max, InteropLibrary iop) {
for (var i = 0; i < max; i++) {
var v = values[i];
var v = values[firstDispatch + i];
if (iop.isNull(v)) {
return new Value(NULL, v);
}
Expand Down Expand Up @@ -226,7 +233,7 @@ static Value find(Object[] values, int max, InteropLibrary iop) {
}

private InteropType.Value findInteropTypeValue(InteropLibrary iop) {
return InteropType.find(values, dispatch.typesLength(), iop);
return InteropType.find(values, firstDispatch, dispatch.typesLength(), iop);
}

@ExportMessage
Expand Down Expand Up @@ -455,7 +462,7 @@ Object getMembers(
var names = new TreeSet<String>();
for (var i = 0; i < dispatch.typesLength(); i++) {
try {
var members = iop.getMembers(values[i]);
var members = iop.getMembers(values[firstDispatch + i]);
var len = iop.getArraySize(members);
for (var j = 0L; j < len; j++) {
var name = iop.readArrayElement(members, j);
Expand All @@ -471,7 +478,7 @@ Object getMembers(
boolean isMemberInvocable(
String name, @Shared("interop") @CachedLibrary(limit = "10") InteropLibrary iop) {
for (var i = 0; i < dispatch.typesLength(); i++) {
if (iop.isMemberInvocable(values[i], name)) {
if (iop.isMemberInvocable(values[firstDispatch + i], name)) {
return true;
}
}
Expand All @@ -488,8 +495,8 @@ Object invokeMember(
UnsupportedTypeException,
UnknownIdentifierException {
for (var i = 0; i < dispatch.typesLength(); i++) {
if (iop.isMemberInvocable(values[i], name)) {
return iop.invokeMember(values[i], name, args);
if (iop.isMemberInvocable(values[firstDispatch + i], name)) {
return iop.invokeMember(values[firstDispatch + i], name, args);
}
}
throw UnknownIdentifierException.create(name);
Expand All @@ -498,7 +505,7 @@ Object invokeMember(
@TruffleBoundary
@Override
public String toString() {
var both = EnsoMultiType.AllTypesWith.getUncached().executeAllTypes(dispatch, extra);
var both = EnsoMultiType.AllTypesWith.getUncached().executeAllTypes(dispatch, extra, 0);
return Stream.of(both).map(t -> t.getName()).collect(Collectors.joining(" & "));
}

Expand Down Expand Up @@ -544,27 +551,31 @@ public static CastToNode getUncached() {
public final Object findTypeOrNull(
Type type, EnsoMultiValue mv, boolean reorderOnly, boolean allTypes) {
var dispatch = mv.dispatch;
var i = findNode.executeFindIndex(type, dispatch);
if (i == -1 && allTypes) {
var extraIndex = findNode.executeFindIndex(type, mv.extra);
i = extraIndex == -1 ? -1 : dispatch.typesLength() + extraIndex;
var typeIndex = findNode.executeFindIndex(type, dispatch);
var valueIndex = -1;
if (typeIndex == -1) {
if (allTypes) {
var extraIndex = findNode.executeFindIndex(type, mv.extra);
if (extraIndex != -1) {
if (extraIndex < mv.firstDispatch) {
valueIndex = extraIndex;
} else {
var rem = extraIndex - mv.firstDispatch;
valueIndex = mv.firstDispatch + dispatch.typesLength() + rem;
assert typeIndex < mv.values.length;
}
typeIndex = dispatch.typesLength() + extraIndex;
}
}
} else {
valueIndex = mv.firstDispatch + typeIndex;
}
if (i != -1) {
if (typeIndex != -1) {
if (reorderOnly) {
var copyTypes = allTypesWith.executeAllTypes(dispatch, mv.extra);
if (i == 0 && dispatch.typesLength() == 1) {
return newNode.newValue(copyTypes, 1, mv.values);
} else {
copyTypes = copyTypes.clone();
var copyValues = mv.values.clone();
copyTypes[0] = copyTypes[i];
copyValues[0] = copyValues[i];
copyTypes[i] = dispatch.firstType();
copyValues[i] = mv.values[0];
return newNode.newValue(copyTypes, 1, copyValues);
}
var copyTypes = allTypesWith.executeAllTypes(dispatch, mv.extra, typeIndex);
return newNode.newValue(copyTypes, 1, valueIndex, mv.values);
} else {
return mv.values[i];
return mv.values[valueIndex];
}
} else {
return null;
Expand All @@ -583,7 +594,7 @@ public final Pair<Function, Type> resolveSymbol(
MethodResolverNode node, UnresolvedSymbol symbol) {
var ctx = EnsoContext.get(node);
Pair<Function, Type> foundAnyMethod = null;
for (var t : EnsoMultiType.AllTypesWith.getUncached().executeAllTypes(dispatch, null)) {
for (var t : EnsoMultiType.AllTypesWith.getUncached().executeAllTypes(dispatch, null, 0)) {
var fnAndType = node.execute(t, symbol);
if (fnAndType != null) {
if (dispatch.typesLength() == 1 || fnAndType.getRight() != ctx.getBuiltins().any()) {
Expand Down

0 comments on commit 7d9d69b

Please sign in to comment.