Skip to content

Commit

Permalink
Merge pull request #34420 from mkouba/qute-generators-alloc-optim
Browse files Browse the repository at this point in the history
Qute: ValueResolverGenerator optimization - reduce allocations
  • Loading branch information
mkouba authored Jun 30, 2023
2 parents 3c08443 + 4fd19eb commit 2cf035a
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 21 deletions.
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

0 comments on commit 2cf035a

Please sign in to comment.