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

EnsoMultiValue.firstDispatch to speed benchmarks up #11975

Merged
merged 31 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
086d024
Ensure isAllTypes is compilation constant
JaroslavTulach Dec 19, 2024
a33965d
Using internal MultiType to represent Type[] but with guaranteed ==
JaroslavTulach Dec 19, 2024
171c799
Merge remote-tracking branch 'origin/develop' into wip/jtulach/MultiT…
JaroslavTulach Dec 19, 2024
775595c
Prefer partialEvaluationConstant assert
JaroslavTulach Dec 19, 2024
e2760ff
Benchmarks for intersection types
JaroslavTulach Dec 20, 2024
630ec62
Usage of Map & co. must be behind @TruffleBoundary
JaroslavTulach Dec 20, 2024
8defcfe
Unify findInteropTypeValue
JaroslavTulach Dec 20, 2024
d44d30e
Inline cache to find index of a type
JaroslavTulach Dec 20, 2024
7b6d364
Use EnsoMultiValue.NewNode to allocate new instances of EnsoMultiValue
JaroslavTulach Dec 20, 2024
bda398a
Basic specializations for NewNode
JaroslavTulach Dec 20, 2024
56b24aa
Splitting the FindIndexNode and caching requests for newNode
JaroslavTulach Dec 20, 2024
2dcc2c4
Just ask only for types the value 'has been cast to'
JaroslavTulach Dec 20, 2024
ee080a3
Provide cachedTypes as the first argument to activate the caches
JaroslavTulach Dec 20, 2024
53c2222
AllOfTypesCheckNode needs cached EnsoMultiValue.NewNode to allocate E…
JaroslavTulach Dec 20, 2024
9567257
Moving EnsoMultiType into outer scope
JaroslavTulach Dec 20, 2024
6bfdbf9
Sum re field of a Complex object in a Vector is the base benchmark
JaroslavTulach Dec 21, 2024
4dacf53
Cache dispatch on EnsoMultiValue.getDispatchId
JaroslavTulach Dec 21, 2024
ebe0553
Turing allTypesWith method into EnsoMultiType.AllTypesWith node
JaroslavTulach Dec 28, 2024
2301f9b
Only assert valid payload
JaroslavTulach Dec 30, 2024
8d5452c
Speeding up non-reordering reorderOnly case twice
JaroslavTulach Dec 30, 2024
ed8799c
Merging with develop and resolving conflicts
JaroslavTulach Dec 30, 2024
615b600
Don't use keyword as variable name
JaroslavTulach Dec 30, 2024
a68db22
Assert there is no intersection between dispatch and extra types
JaroslavTulach Dec 30, 2024
98ebc39
Merge remote-tracking branch 'origin/develop' into wip/jtulach/MultiT…
JaroslavTulach Jan 4, 2025
a9f57f0
Avoiding duplications when Number & Integer & Float and co.
JaroslavTulach Jan 4, 2025
035b2be
Merge branch 'develop' into wip/jtulach/MultiType11846
mergify[bot] Jan 4, 2025
a3414b2
Associate each EnsoMultiValue.values with firstDispatch index
JaroslavTulach Jan 5, 2025
3f0a3e3
Merge branch 'develop' into wip/jtulach/MultiType11846
mergify[bot] Jan 6, 2025
276b41e
Merge remote-tracking branch 'origin/wip/jtulach/MultiType11846' into…
JaroslavTulach Jan 7, 2025
1ffc0aa
Merging with latest develop that already contains #11924
JaroslavTulach Jan 7, 2025
7e2be2e
Splitting index into type and value ones
JaroslavTulach Jan 8, 2025
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
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();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea behind this PR's speed up is to avoid mv.values.clone() when we only need to select new single pivot value. Instead we give each EnsoMultiValue a firstDispatch index into the array that identifies the location of the currently selected type. Then ths reorderOnly operation can share the same mv.values array with the new EnsoMultiValue.

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
Loading