From 50c70de7e53198a03ab2127184ed376464c804cc Mon Sep 17 00:00:00 2001 From: altrisi Date: Sat, 29 Jan 2022 23:44:50 +0100 Subject: [PATCH] Improve Annotated Scarpet runtime performance (#1273) - Change valueConverters list to array - Remove references to `Class.cast` and use direct casts instead --- .../script/annotation/AnnotationParser.java | 24 ++++++++----------- .../annotation/SimpleTypeConverter.java | 5 ++-- .../carpet/script/annotation/ValueCaster.java | 3 ++- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/main/java/carpet/script/annotation/AnnotationParser.java b/src/main/java/carpet/script/annotation/AnnotationParser.java index ad8e3a94..711a4bb2 100644 --- a/src/main/java/carpet/script/annotation/AnnotationParser.java +++ b/src/main/java/carpet/script/annotation/AnnotationParser.java @@ -7,6 +7,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Parameter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.ListIterator; import java.util.Objects; @@ -25,7 +26,6 @@ import carpet.script.Fluff.UsageProvider; import carpet.script.exception.InternalExpressionException; import carpet.script.LazyValue; import carpet.script.value.Value; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; /** *

This class parses methods annotated with the {@link ScarpetFunction} annotation in a given {@link Class}, generating @@ -148,7 +148,7 @@ public final class AnnotationParser private final String name; private final boolean isMethodVarArgs; private final int methodParamCount; - private final List> valueConverters; + private final ValueConverter[] valueConverters; private final Class varArgsType; private final boolean primitiveVarArgs; private final ValueConverter varArgsConverter; @@ -167,12 +167,12 @@ public final class AnnotationParser this.methodParamCount = method.getParameterCount(); Parameter[] methodParameters = method.getParameters(); - this.valueConverters = new ObjectArrayList<>(); + this.valueConverters = new ValueConverter[isMethodVarArgs ? methodParamCount - 1 : methodParamCount]; for (int i = 0; i < this.methodParamCount; i++) { Parameter param = methodParameters[i]; if (!isMethodVarArgs || i != this.methodParamCount - 1) // Varargs converter is separate - this.valueConverters.add(ValueConverter.fromAnnotatedType(param.getAnnotatedType())); + this.valueConverters[i] = ValueConverter.fromAnnotatedType(param.getAnnotatedType()); } Class originalVarArgsType = isMethodVarArgs ? methodParameters[methodParamCount - 1].getType().getComponentType() : null; this.varArgsType = ClassUtils.primitiveToWrapper(originalVarArgsType); // Primitive array cannot be cast to Obj[] @@ -182,8 +182,8 @@ public final class AnnotationParser OutputConverter converter = OutputConverter.get((Class) method.getReturnType()); this.outputConverter = converter; - this.isEffectivelyVarArgs = isMethodVarArgs ? true : valueConverters.stream().anyMatch(ValueConverter::consumesVariableArgs); - this.minParams = valueConverters.stream().mapToInt(ValueConverter::valueConsumption).sum(); // Note: In !varargs, this is params + this.isEffectivelyVarArgs = isMethodVarArgs || Arrays.stream(valueConverters).anyMatch(ValueConverter::consumesVariableArgs); + this.minParams = Arrays.stream(valueConverters).mapToInt(ValueConverter::valueConsumption).sum(); // Note: In !varargs, this is params int maxParams = this.minParams; // Unlimited == Integer.MAX_VALUE if (this.isEffectivelyVarArgs) { @@ -234,10 +234,6 @@ public final class AnnotationParser if (lv.size() > maxParams) throw new InternalExpressionException("Function '" + name + " expected up to " + maxParams + " arguments, got " + lv.size() + ". " + getUsage()); - } else - { - if (lv.size() != minParams) // min == max if not varargs. We do this cause we don't use function's arg checking to be able to return lazy - throw new InternalExpressionException("Function '" + name + "' expected " + minParams + " arguments, got " + lv.size() +". " + getUsage()); } Object[] params = getMethodParams(lv, context, t); try @@ -261,7 +257,7 @@ public final class AnnotationParser int regularArgs = isMethodVarArgs ? methodParamCount - 1 : methodParamCount; for (int i = 0; i < regularArgs; i++) { - params[i] = valueConverters.get(i).checkAndConvert(lvIterator, context, theLazyT); + params[i] = valueConverters[i].checkAndConvert(lvIterator, context, theLazyT); if (params[i] == null) throw new InternalExpressionException("Incorrect argument passsed to '" + name + "' function.\n" + getUsage()); } @@ -271,7 +267,7 @@ public final class AnnotationParser Object[] varArgs; if (varArgsConverter.consumesVariableArgs()) { - List varArgsList = new ArrayList<>(); + List varArgsList = new ArrayList<>(); // fastutil's is extremely slow in toArray, and we use that while (lvIterator.hasNext()) { Object obj = varArgsConverter.checkAndConvert(lvIterator, context, theLazyT); @@ -279,7 +275,7 @@ public final class AnnotationParser throw new InternalExpressionException("Incorrect argument passsed to '" + name + "' function.\n" + getUsage()); varArgsList.add(obj); } - varArgs = varArgsList.toArray(); + varArgs = varArgsList.toArray((Object[])Array.newInstance(varArgsType, 0)); } else { varArgs = (Object[]) Array.newInstance(varArgsType, remaining / varArgsConverter.valueConsumption()); @@ -302,7 +298,7 @@ public final class AnnotationParser StringBuilder builder = new StringBuilder("Usage: '"); builder.append(name); builder.append('('); - builder.append(valueConverters.stream().map(ValueConverter::getTypeName).filter(Objects::nonNull).collect(Collectors.joining(", "))); + builder.append(Arrays.stream(valueConverters).map(ValueConverter::getTypeName).filter(Objects::nonNull).collect(Collectors.joining(", "))); if (varArgsConverter != null) { builder.append(", "); diff --git a/src/main/java/carpet/script/annotation/SimpleTypeConverter.java b/src/main/java/carpet/script/annotation/SimpleTypeConverter.java index 9d915c53..498e3b36 100644 --- a/src/main/java/carpet/script/annotation/SimpleTypeConverter.java +++ b/src/main/java/carpet/script/annotation/SimpleTypeConverter.java @@ -85,9 +85,10 @@ public final class SimpleTypeConverter implements ValueConve } @Override - public R convert(Value value) + @SuppressWarnings("unchecked") // more than checked. not using class.cast because then "method is too big" for inlining, because javac is useless + public R convert(Value value) // and adds millions of casts. This one is even removed { - return valueClass.isInstance(value) ? converter.apply(valueClass.cast(value)) : null; + return valueClass.isInstance(value) ? converter.apply((T)value) : null; } /** diff --git a/src/main/java/carpet/script/annotation/ValueCaster.java b/src/main/java/carpet/script/annotation/ValueCaster.java index 539995d8..1ca8fee7 100644 --- a/src/main/java/carpet/script/annotation/ValueCaster.java +++ b/src/main/java/carpet/script/annotation/ValueCaster.java @@ -77,11 +77,12 @@ public final class ValueCaster implements ValueConverter // R always exten } @Override + @SuppressWarnings("unchecked") // more than checked, see SimpleTypeConverter#converter for reasoning public R convert(Value value) { if (!outputType.isInstance(value)) return null; - return outputType.cast(value); + return (R)value; } /**