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

Qute: ValueResolverGenerator optimization - reduce allocations #34420

Merged
merged 1 commit into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -20,16 +20,33 @@
*/
public final class CompletedStage<T> implements CompletionStage<T>, Supplier<T> {

static final CompletedStage<Void> VOID = new CompletedStage<>(null, null);
@SuppressWarnings("rawtypes")
static final CompletedStage NULL = new CompletedStage<>(null, null);

@SuppressWarnings("unchecked")
public static <T> CompletedStage<T> of(T result) {
if (result == null) {
// Use a shared constant for nulls
return (CompletedStage<T>) NULL;
}
return new CompletedStage<T>(result, null);
}

public static <T> CompletedStage<T> failure(Throwable t) {
Objects.requireNonNull(t);
return new CompletedStage<T>(null, t);
}

@SuppressWarnings("unchecked")
public static CompletedStage<Void> ofVoid() {
return NULL;
}

@SuppressWarnings("unchecked")
public static CompletedStage<Object> ofNull() {
return NULL;
}

private final T result;
private final Throwable exception;

Expand Down Expand Up @@ -84,7 +101,7 @@ public CompletionStage<Void> thenAccept(Consumer<? super T> action) {
} catch (Throwable e) {
return new CompletedStage<>(null, e);
}
return VOID;
return ofVoid();
}
return new CompletedStage<>(null, exception);
}
Expand All @@ -108,7 +125,7 @@ public CompletionStage<Void> thenRun(Runnable action) {
} catch (final Throwable e) {
return new CompletedStage<>(null, e);
}
return VOID;
return ofVoid();
}
return new CompletedStage<>(null, exception);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

public final class EvaluatedParams {

static final EvaluatedParams EMPTY = new EvaluatedParams(CompletedStage.VOID, new Supplier<?>[0]);
static final EvaluatedParams EMPTY = new EvaluatedParams(CompletedStage.ofVoid(), new Supplier<?>[0]);

/**
*
Expand Down Expand Up @@ -52,7 +52,7 @@ public static EvaluatedParams evaluate(EvalContext context) {
}
CompletionStage<?> cs;
if (asyncResults == null) {
cs = failure != null ? failure : CompletedStage.VOID;
cs = failure != null ? failure : CompletedStage.ofVoid();
} else if (asyncResults.size() == 1) {
cs = asyncResults.get(0);
} else {
Expand Down Expand Up @@ -101,7 +101,7 @@ public static EvaluatedParams evaluateMessageParams(EvalContext context) {
}
CompletionStage<?> cs;
if (asyncResults == null) {
cs = failure != null ? failure : CompletedStage.VOID;
cs = failure != null ? failure : CompletedStage.ofVoid();
} else if (asyncResults.size() == 1) {
cs = asyncResults.get(0);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public final class Results {

public static final CompletedStage<Object> FALSE = CompletedStage.of(false);
public static final CompletedStage<Object> TRUE = CompletedStage.of(true);
public static final CompletedStage<Object> NULL = CompletedStage.of(null);
public static final CompletedStage<Object> NULL = CompletedStage.NULL;

private Results() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ private Descriptors() {
public static final MethodDescriptor EVALUATE = MethodDescriptor.ofMethod(EvalContext.class, "evaluate",
CompletionStage.class, Expression.class);
static final MethodDescriptor LIST_GET = MethodDescriptor.ofMethod(List.class, "get", Object.class, int.class);
static final MethodDescriptor COMPLETED_STAGE = MethodDescriptor.ofMethod(CompletedStage.class,
static final MethodDescriptor COMPLETED_STAGE_OF = MethodDescriptor.ofMethod(CompletedStage.class,
"of", CompletedStage.class, Object.class);
public static final MethodDescriptor COMPLETABLE_FUTURE_ALL_OF = MethodDescriptor.ofMethod(CompletableFuture.class,
"allOf",
Expand All @@ -58,6 +58,8 @@ private Descriptors() {
CompletionStage.class, BiConsumer.class);
public static final MethodDescriptor BOOLEAN_LOGICAL_OR = MethodDescriptor.ofMethod(Boolean.class, "logicalOr",
boolean.class, boolean.class, boolean.class);
public static final MethodDescriptor BOOLEAN_VALUE = MethodDescriptor.ofMethod(Boolean.class, "booleanValue",
boolean.class);
public static final MethodDescriptor EVALUATED_PARAMS_EVALUATE = MethodDescriptor.ofMethod(EvaluatedParams.class,
"evaluate",
EvaluatedParams.class,
Expand Down Expand Up @@ -96,5 +98,7 @@ private Descriptors() {

public static final FieldDescriptor EVALUATED_PARAMS_STAGE = FieldDescriptor.of(EvaluatedParams.class, "stage",
CompletionStage.class);
public static final FieldDescriptor RESULTS_TRUE = FieldDescriptor.of(Results.class, "TRUE", CompletedStage.class);
public static final FieldDescriptor RESULTS_FALSE = FieldDescriptor.of(Results.class, "FALSE", CompletedStage.class);

}
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ private void implementResolve(ClassCreator valueResolver, ClassInfo declaringCla
if (returnsCompletionStage) {
ret = result;
} else {
ret = resolve.invokeStaticMethod(Descriptors.COMPLETED_STAGE, result);
ret = resolve.invokeStaticMethod(Descriptors.COMPLETED_STAGE_OF, result);
}
} else {
ret = resolve
Expand Down Expand Up @@ -520,7 +520,7 @@ public void addMethod(MethodInfo method, String matchName, List<String> matchNam
matchScope.load(param.name));
}
}
matchScope.returnValue(matchScope.invokeStaticMethod(Descriptors.COMPLETED_STAGE,
matchScope.returnValue(matchScope.invokeStaticMethod(Descriptors.COMPLETED_STAGE_OF,
matchScope.invokeStaticMethod(MethodDescriptor.of(method), args)));
} else {
ResultHandle ret = matchScope.newInstance(MethodDescriptor.ofConstructor(CompletableFuture.class));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.quarkus.qute.generator;

import static java.util.function.Predicate.not;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;

import java.lang.reflect.Modifier;
Expand Down Expand Up @@ -33,6 +34,7 @@
import org.jboss.jandex.IndexView;
import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.PrimitiveType;
import org.jboss.jandex.PrimitiveType.Primitive;
import org.jboss.jandex.Type;
import org.jboss.logging.Logger;

Expand All @@ -46,11 +48,13 @@
import io.quarkus.gizmo.FieldDescriptor;
import io.quarkus.gizmo.FunctionCreator;
import io.quarkus.gizmo.Gizmo;
import io.quarkus.gizmo.IfThenElse;
import io.quarkus.gizmo.MethodCreator;
import io.quarkus.gizmo.MethodDescriptor;
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.gizmo.Switch;
import io.quarkus.gizmo.TryBlock;
import io.quarkus.qute.CompletedStage;
import io.quarkus.qute.EvalContext;
import io.quarkus.qute.EvaluatedParams;
import io.quarkus.qute.NamespaceResolver;
Expand All @@ -76,7 +80,6 @@ public static Builder builder() {
public static final String NESTED_SEPARATOR = "$_";

private static final Logger LOGGER = Logger.getLogger(ValueResolverGenerator.class);

public static final String GET_PREFIX = "get";
public static final String IS_PREFIX = "is";
public static final String HAS_PREFIX = "has";
Expand Down Expand Up @@ -373,8 +376,8 @@ private boolean implementResolve(ClassCreator valueResolver, String clazzName, C
Consumer<BytecodeCreator> invokeMethod = new Consumer<BytecodeCreator>() {
@Override
public void accept(BytecodeCreator bc) {
ResultHandle ret;
boolean hasCompletionStage = !skipMemberType(method.returnType())
Type returnType = method.returnType();
boolean hasCompletionStage = !skipMemberType(returnType)
&& hasCompletionStageInTypeClosure(index.getClassByName(method.returnType().name()), index);
ResultHandle invokeRet;
if (Modifier.isInterface(clazz.flags())) {
Expand All @@ -383,11 +386,20 @@ public void accept(BytecodeCreator bc) {
invokeRet = bc.invokeVirtualMethod(MethodDescriptor.of(method), base);
}
if (hasCompletionStage) {
ret = invokeRet;
bc.returnValue(invokeRet);
} else {
ret = bc.invokeStaticMethod(Descriptors.COMPLETED_STAGE, invokeRet);
// Try to use some shared CompletedStage constants
if (returnType.kind() == org.jboss.jandex.Type.Kind.PRIMITIVE
&& returnType.asPrimitiveType().primitive() == Primitive.BOOLEAN) {
completeBoolean(bc, invokeRet);
} else if (method.returnType().name().equals(DotNames.BOOLEAN)) {
completeBoolean(bc, bc.invokeVirtualMethod(Descriptors.BOOLEAN_VALUE, invokeRet));
} else if (isEnum(returnType)) {
completeEnum(index.getClassByName(returnType.name()), valueResolver, invokeRet, bc);
} else {
bc.returnValue(bc.invokeStaticMethod(Descriptors.COMPLETED_STAGE_OF, invokeRet));
}
}
bc.returnValue(ret);
}
};
nameSwitch.caseOf(matchingNames, invokeMethod);
Expand All @@ -410,7 +422,7 @@ public void accept(BytecodeCreator bc) {
MethodDescriptor.ofMethod(clazz.name().toString(), getterName,
DescriptorUtils.typeToString(field.type())),
base);
bc.returnValue(bc.invokeStaticMethod(Descriptors.COMPLETED_STAGE, value));
bc.returnValue(bc.invokeStaticMethod(Descriptors.COMPLETED_STAGE_OF, value));
}
};
nameSwitch.caseOf(matching, invokeMethod);
Expand All @@ -422,7 +434,7 @@ public void accept(BytecodeCreator bc) {
ResultHandle value = bc.readInstanceField(
FieldDescriptor.of(clazzName, field.name(), field.type().name().toString()),
base);
ResultHandle ret = bc.invokeStaticMethod(Descriptors.COMPLETED_STAGE, value);
ResultHandle ret = bc.invokeStaticMethod(Descriptors.COMPLETED_STAGE_OF, value);
bc.returnValue(ret);
}
};
Expand Down Expand Up @@ -471,6 +483,71 @@ public void accept(BytecodeCreator bc) {
return true;
}

private void completeBoolean(BytecodeCreator bc, ResultHandle result) {
BranchResult isTrue = bc.ifTrue(result);
BytecodeCreator trueBranch = isTrue.trueBranch();
trueBranch.returnValue(trueBranch.readStaticField(Descriptors.RESULTS_TRUE));
BytecodeCreator falseBranch = isTrue.falseBranch();
falseBranch.returnValue(falseBranch.readStaticField(Descriptors.RESULTS_FALSE));
}

private boolean isEnum(Type returnType) {
if (returnType.kind() != org.jboss.jandex.Type.Kind.CLASS) {
return false;
}
ClassInfo maybeEnum = index.getClassByName(returnType.name());
return maybeEnum != null && maybeEnum.isEnum();
}

private boolean completeEnum(ClassInfo enumClass, ClassCreator valueResolver, ResultHandle result, BytecodeCreator bc) {
IfThenElse ifThenElse = null;
for (FieldInfo enumConstant : enumClass.enumConstants()) {
String name = enumClass.name().toString().replace(".", "_") + "$$"
+ enumConstant.name();
FieldDescriptor enumConstantField = FieldDescriptor.of(enumClass.name().toString(),
enumConstant.name(), enumClass.name().toString());

// Additional methods and fields are generated for enums that are part of the index
// We don't care about visibility and atomicity here
// private CompletedStage org_acme_MyEnum$$CONSTANT;
FieldDescriptor csField = valueResolver
.getFieldCreator(name, CompletedStage.class).setModifiers(ACC_PRIVATE)
.getFieldDescriptor();
// private CompletedStage org_acme_MyEnum$$CONSTANT() {
// if (org_acme_MyEnum$$CONSTANT == null) {
// org_acme_MyEnum$$CONSTANT = CompletedStage.of(MyEnum.CONSTANT);
// }
// return org_acme_MyEnum$$CONSTANT;
// }
MethodCreator enumConstantMethod = valueResolver.getMethodCreator(name,
CompletedStage.class).setModifiers(ACC_PRIVATE);
BytecodeCreator isNull = enumConstantMethod.ifNull(enumConstantMethod
.readInstanceField(csField, enumConstantMethod.getThis()))
.trueBranch();
ResultHandle val = isNull.readStaticField(enumConstantField);
isNull.writeInstanceField(csField, enumConstantMethod.getThis(),
isNull.invokeStaticMethod(Descriptors.COMPLETED_STAGE_OF, val));
enumConstantMethod.returnValue(enumConstantMethod
.readInstanceField(csField, enumConstantMethod.getThis()));

// Unfortunately, we can't use the BytecodeCreator#enumSwitch() here because the enum class is not loaded
// if(val.equals(MyEnum.CONSTANT))
// return org_acme_MyEnum$$CONSTANT();
BytecodeCreator match;
if (ifThenElse == null) {
ifThenElse = bc.ifThenElse(
Gizmo.equals(bc, result, bc.readStaticField(enumConstantField)));
match = ifThenElse.then();
} else {
match = ifThenElse.elseIf(
b -> Gizmo.equals(b, result, b.readStaticField(enumConstantField)));
}
match.returnValue(match.invokeVirtualMethod(
enumConstantMethod.getMethodDescriptor(), match.getThis()));
}
return true;
}

private boolean implementNamespaceResolve(ClassCreator valueResolver, String clazzName, ClassInfo clazz,
Predicate<AnnotationTarget> filter) {
MethodCreator resolve = valueResolver.getMethodCreator("resolve", CompletionStage.class, EvalContext.class)
Expand Down Expand Up @@ -512,7 +589,7 @@ private boolean implementNamespaceResolve(ClassCreator valueResolver, String cla
.trueBranch();
ResultHandle value = fieldMatch
.readStaticField(FieldDescriptor.of(clazzName, field.name(), field.type().name().toString()));
fieldMatch.returnValue(fieldMatch.invokeStaticMethod(Descriptors.COMPLETED_STAGE, value));
fieldMatch.returnValue(fieldMatch.invokeStaticMethod(Descriptors.COMPLETED_STAGE_OF, value));
}
}

Expand Down Expand Up @@ -541,7 +618,7 @@ private boolean implementNamespaceResolve(ClassCreator valueResolver, String cla
if (hasCompletionStage) {
ret = invokeRet;
} else {
ret = matchScope.invokeStaticMethod(Descriptors.COMPLETED_STAGE, invokeRet);
ret = matchScope.invokeStaticMethod(Descriptors.COMPLETED_STAGE_OF, invokeRet);
}
matchScope.returnValue(ret);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,18 @@ public boolean hasName() {
return name != null;
}

public boolean isActive() {
public Boolean isActive() {
return true;
}

public boolean hasItems() {
return false;
}

public MyEnum myEnum() {
return MyEnum.BAR;
}

public List<String> getList(int limit, String dummy) {
AtomicInteger idx = new AtomicInteger(0);
return Stream.generate(() -> "" + idx.getAndIncrement())
Expand Down