From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Mon, 6 Apr 2020 17:53:29 -0700 Subject: [PATCH] Optimize GoalSelector Goal.Flag Set operations Optimise the stream.anyMatch statement to move to a bitset where we can replace the call with a single bitwise operation. diff --git a/net/minecraft/world/entity/ai/goal/Goal.java b/net/minecraft/world/entity/ai/goal/Goal.java index a66c86066ca2eda10f0ef62e5197a765a994f250..f54bbe2e65b18f214266769c7a64144baafa9a58 100644 --- a/net/minecraft/world/entity/ai/goal/Goal.java +++ b/net/minecraft/world/entity/ai/goal/Goal.java @@ -7,7 +7,15 @@ import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; public abstract class Goal { - private final EnumSet flags = EnumSet.noneOf(Goal.Flag.class); + private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from GoalSelector + + // Paper start - remove streams from GoalSelector; make sure types are not empty + protected Goal() { + if (this.goalTypes.size() == 0) { + this.goalTypes.addUnchecked(Flag.UNKNOWN_BEHAVIOR); + } + } + // Paper end - remove streams from GoalSelector public abstract boolean canUse(); @@ -33,8 +41,13 @@ public abstract class Goal { } public void setFlags(EnumSet flagSet) { - this.flags.clear(); - this.flags.addAll(flagSet); + // Paper start - remove streams from GoalSelector + this.goalTypes.clear(); + this.goalTypes.addAllUnchecked(flagSet); + if (this.goalTypes.size() == 0) { + this.goalTypes.addUnchecked(Flag.UNKNOWN_BEHAVIOR); + } + // Paper end - remove streams from GoalSelector } @Override @@ -42,18 +55,20 @@ public abstract class Goal { return this.getClass().getSimpleName(); } - public EnumSet getFlags() { - return this.flags; + // Paper start - remove streams from GoalSelector + public ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet getFlags() { + return this.goalTypes; + // Paper end - remove streams from GoalSelector } // Paper start - Mob Goal API public boolean hasFlag(final Goal.Flag flag) { - return this.flags.contains(flag); + return this.goalTypes.hasElement(flag); } public void addFlag(final Goal.Flag flag) { - this.flags.add(flag); + this.goalTypes.addUnchecked(flag); } // Paper end - Mob Goal API diff --git a/net/minecraft/world/entity/ai/goal/GoalSelector.java b/net/minecraft/world/entity/ai/goal/GoalSelector.java index 674966c580220a4e0c83a628c763aaea8bfd0b1c..859b859d29b637200cf7c9a0bd52d9f712413e3d 100644 --- a/net/minecraft/world/entity/ai/goal/GoalSelector.java +++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java @@ -23,7 +23,8 @@ public class GoalSelector { }; private final Map lockedFlags = new EnumMap<>(Goal.Flag.class); private final Set availableGoals = new ObjectLinkedOpenHashSet<>(); - private final EnumSet disabledFlags = EnumSet.noneOf(Goal.Flag.class); + private static final Goal.Flag[] GOAL_FLAG_VALUES = Goal.Flag.values(); // Paper - remove streams from GoalSelector + private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from GoalSelector private int curRate; // Paper - EAR 2 public void addGoal(int priority, Goal goal) { @@ -60,18 +61,18 @@ public class GoalSelector { this.availableGoals.removeIf(wrappedGoal1 -> wrappedGoal1.getGoal() == goal); } - private static boolean goalContainsAnyFlags(WrappedGoal goal, EnumSet flag) { - for (Goal.Flag flag1 : goal.getFlags()) { - if (flag.contains(flag1)) { - return true; - } - } - - return false; + // Paper start - Perf: optimize goal types + private static boolean goalContainsAnyFlags(WrappedGoal goal, ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet flags) { + return goal.getFlags().hasCommonElements(flags); } private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map flag) { - for (Goal.Flag flag1 : goal.getFlags()) { + long flagIterator = goal.getFlags().getBackingSet(); + int wrappedGoalSize = goal.getFlags().size(); + for (int i = 0; i < wrappedGoalSize; ++i) { + final Goal.Flag flag1 = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; + flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator); + // Paper end - Perf: optimize goal types if (!flag.getOrDefault(flag1, NO_GOAL).canBeReplacedBy(goal)) { return false; } @@ -85,7 +86,7 @@ public class GoalSelector { profilerFiller.push("goalCleanup"); for (WrappedGoal wrappedGoal : this.availableGoals) { - if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.disabledFlags) || !wrappedGoal.canContinueToUse())) { + if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) { // Paper - Perf: optimize goal types by removing streams wrappedGoal.stop(); } } @@ -95,11 +96,14 @@ public class GoalSelector { profilerFiller.push("goalUpdate"); for (WrappedGoal wrappedGoalx : this.availableGoals) { - if (!wrappedGoalx.isRunning() - && !goalContainsAnyFlags(wrappedGoalx, this.disabledFlags) - && goalCanBeReplacedForAllFlags(wrappedGoalx, this.lockedFlags) - && wrappedGoalx.canUse()) { - for (Goal.Flag flag : wrappedGoalx.getFlags()) { + // Paper start + if (!wrappedGoalx.isRunning() && !goalContainsAnyFlags(wrappedGoalx, this.goalTypes) && goalCanBeReplacedForAllFlags(wrappedGoalx, this.lockedFlags) && wrappedGoalx.canUse()) { + long flagIterator = wrappedGoalx.getFlags().getBackingSet(); + int wrappedGoalSize = wrappedGoalx.getFlags().size(); + for (int i = 0; i < wrappedGoalSize; ++i) { + final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; + flagIterator ^= ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit(flagIterator); + // Paper end WrappedGoal wrappedGoal1 = this.lockedFlags.getOrDefault(flag, NO_GOAL); wrappedGoal1.stop(); this.lockedFlags.put(flag, wrappedGoalx); @@ -131,11 +135,11 @@ public class GoalSelector { } public void disableControlFlag(Goal.Flag flag) { - this.disabledFlags.add(flag); + this.goalTypes.addUnchecked(flag); // Paper - remove streams from GoalSelector } public void enableControlFlag(Goal.Flag flag) { - this.disabledFlags.remove(flag); + this.goalTypes.removeUnchecked(flag); // Paper - remove streams from GoalSelector } public void setControlFlag(Goal.Flag flag, boolean enabled) { diff --git a/net/minecraft/world/entity/ai/goal/WrappedGoal.java b/net/minecraft/world/entity/ai/goal/WrappedGoal.java index 4bdbd323b642ed3422948fe24780be8b503602dc..2c2ab6a1df9d3d23773e44ce4041cc1c21b55163 100644 --- a/net/minecraft/world/entity/ai/goal/WrappedGoal.java +++ b/net/minecraft/world/entity/ai/goal/WrappedGoal.java @@ -69,7 +69,7 @@ public class WrappedGoal extends Goal { } @Override - public EnumSet getFlags() { + public ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet getFlags() { // Paper - remove streams from GoalSelector return this.goal.getFlags(); }