From 3174bbcf620863d993cf66fe4b3a5da0a7a4d988 Mon Sep 17 00:00:00 2001 From: melontini <104443436+melontini@users.noreply.github.com> Date: Fri, 10 May 2024 14:24:28 +0700 Subject: [PATCH] Cache the way we got the variable to avoid parsing the identifier and looking up values every time. --- .../commander/impl/expression/EvalUtils.java | 51 ++++++++++++------- .../extensions/ReflectiveMapStructure.java | 5 +- .../extensions/ReflectiveValueConverter.java | 2 +- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/main/java/me/melontini/commander/impl/expression/EvalUtils.java b/src/main/java/me/melontini/commander/impl/expression/EvalUtils.java index 6758b38..231ff50 100644 --- a/src/main/java/me/melontini/commander/impl/expression/EvalUtils.java +++ b/src/main/java/me/melontini/commander/impl/expression/EvalUtils.java @@ -12,9 +12,10 @@ import com.ezylang.evalex.parser.ParseException; import com.google.common.base.CaseFormat; import com.google.common.collect.ImmutableMap; import com.mojang.serialization.DataResult; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; import lombok.SneakyThrows; import me.melontini.commander.impl.event.data.types.ExtractionTypes; +import me.melontini.commander.impl.expression.extensions.ProxyMap; import me.melontini.commander.impl.expression.extensions.ReflectiveValueConverter; import me.melontini.commander.impl.expression.functions.*; import me.melontini.commander.impl.expression.functions.arrays.*; @@ -30,10 +31,10 @@ import net.minecraft.util.Identifier; import org.jetbrains.annotations.Nullable; import java.math.BigDecimal; -import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; public class EvalUtils { @@ -51,7 +52,7 @@ public class EvalUtils { .singleQuoteStringLiteralsAllowed(true); var fd = ExpressionConfiguration.defaultConfiguration().getFunctionDictionary(); - Map functions = new HashMap<>(((MapBasedFunctionDictionaryAccessor) fd) + Map functions = new Object2ReferenceOpenHashMap<>(((MapBasedFunctionDictionaryAccessor) fd) .commander$getFunctions().entrySet().stream() .collect(Collectors.toMap(e -> CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, e.getKey()), Map.Entry::getValue))); functions.put("random", new RangedRandomFunction()); @@ -109,7 +110,7 @@ public class EvalUtils { public static DataResult parseExpression(String expression) { try { Expression exp = new Expression(expression, CONFIGURATION); - ((ExpressionAccessor) exp).commander$constants(new HashMap<>(CONFIGURATION.getDefaultConstants())); + ((ExpressionAccessor) exp).commander$constants(new Object2ReferenceOpenHashMap<>(CONFIGURATION.getDefaultConstants())); exp.validate(); return DataResult.success(exp); } catch (Throwable throwable) { @@ -119,44 +120,60 @@ public class EvalUtils { public static class LootContextDataAccessor implements DataAccessorIfc { - private static final Map> overrides = ImmutableMap.of( + private static final Map> overrides = new Object2ReferenceOpenHashMap<>(Map.of( new Identifier("level"), LootContext::getWorld, new Identifier("luck"), LootContext::getLuck - ); + )); public static final ThreadLocal LOCAL = new ThreadLocal<>(); - private final Map parameters = new HashMap<>(); + private final Map parameters = new Object2ReferenceOpenHashMap<>(); + //In most cases the expression is reused, so caching this helps us avoid some big overhead. + private final Map> varCache = new Object2ReferenceOpenHashMap<>(); @Override public @Nullable EvaluationValue getData(String variable) { - var localParam = parameters.get(variable); - if (localParam != null) return localParam; + var supplier = varCache.get(variable); + if (supplier != null) return supplier.get(); //Parameters are cached by setData, so this is fine. var r = Identifier.validate(variable); if (r.error().isPresent()) { - throw new CmdEvalException("%s - %s".formatted(variable, r.error().orElseThrow().message())); + throw new CmdEvalException("%s - no such variable or %s".formatted(variable, r.error().orElseThrow().message())); } var id = r.result().orElseThrow(); var func = overrides.get(id); - if (func != null) return CONFIGURATION.getEvaluationValueConverter().convertObject(func.apply(LOCAL.get()), CONFIGURATION); + if (func != null) { + supplier = () -> ProxyMap.convert(func.apply(LOCAL.get())); + varCache.put(variable, supplier); + return supplier.get(); + } var param = ExtractionTypes.getParameter(id); - if (param == null) throw new CmdEvalException("%s is not a registered loot context parameter!".formatted(id)); - - var object = LOCAL.get().get(param); - if (object == null) return null; - return CONFIGURATION.getEvaluationValueConverter().convertObject(object, CONFIGURATION); + if (param == null) + throw new CmdEvalException("%s is not a registered loot context parameter, variable or override!".formatted(id)); + supplier = () -> { + var object = LOCAL.get().get(param); + if (object == null) return null; + return ProxyMap.convert(object); + }; + varCache.put(variable, supplier); + return supplier.get(); } @Override public void setData(String variable, EvaluationValue value) { parameters.put(variable, value); + + if (value == null) { + varCache.remove(variable); + } else { + varCache.put(variable, () -> parameters.get(variable)); //We're already here, so might as well cache. + } } } public static class SimpleFunctionDictionary implements FunctionDictionaryIfc { - private final Map functions = new Object2ObjectOpenHashMap<>(); + private final Map functions = new Object2ReferenceOpenHashMap<>(); public static FunctionDictionaryIfc ofFunctions(Map functions) { FunctionDictionaryIfc dictionary = new SimpleFunctionDictionary(); diff --git a/src/main/java/me/melontini/commander/impl/expression/extensions/ReflectiveMapStructure.java b/src/main/java/me/melontini/commander/impl/expression/extensions/ReflectiveMapStructure.java index 6a90465..3edacfd 100644 --- a/src/main/java/me/melontini/commander/impl/expression/extensions/ReflectiveMapStructure.java +++ b/src/main/java/me/melontini/commander/impl/expression/extensions/ReflectiveMapStructure.java @@ -105,10 +105,11 @@ public class ReflectiveMapStructure extends ProxyMap { } private static @Nullable Tuple, Function> findFieldOrMethod(Class cls, String name) { + var keeper = Commander.get().mappingKeeper(); String mapped; Class target = cls; do { - if ((mapped = Commander.get().mappingKeeper().getFieldOrMethod(target, name)) != null) return findAccessor(target, mapped); + if ((mapped = keeper.getFieldOrMethod(target, name)) != null) return findAccessor(target, mapped); var targetItfs = target.getInterfaces(); if (targetItfs.length == 0) continue; @@ -116,7 +117,7 @@ public class ReflectiveMapStructure extends ProxyMap { while (!interfaces.isEmpty()) { var itf = interfaces.poll(); - if ((mapped = Commander.get().mappingKeeper().getFieldOrMethod(itf, name)) != null) return findAccessor(itf, mapped); + if ((mapped = keeper.getFieldOrMethod(itf, name)) != null) return findAccessor(itf, mapped); if ((targetItfs = itf.getInterfaces()).length > 0) interfaces.addAll(List.of(targetItfs)); } } while ((target = target.getSuperclass()) != null); diff --git a/src/main/java/me/melontini/commander/impl/expression/extensions/ReflectiveValueConverter.java b/src/main/java/me/melontini/commander/impl/expression/extensions/ReflectiveValueConverter.java index 7fbea18..9d39d4d 100644 --- a/src/main/java/me/melontini/commander/impl/expression/extensions/ReflectiveValueConverter.java +++ b/src/main/java/me/melontini/commander/impl/expression/extensions/ReflectiveValueConverter.java @@ -11,7 +11,7 @@ import java.util.List; public class ReflectiveValueConverter implements EvaluationValueConverterIfc { - static List converters = Arrays.asList( + private static final List converters = Arrays.asList( new NumberConverter(), new StringConverter(), new BooleanConverter(), -- 2.51.2