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/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java index 16f9a98b8a939e5ca7e2dc04f87134a7ed66736b..dd423302b1baa64ef86ded87a29659342e28c142 100644 --- a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java +++ b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java @@ -4,7 +4,16 @@ import java.util.EnumSet; import net.minecraft.util.Mth; public abstract class Goal { - private final EnumSet flags = EnumSet.noneOf(Goal.Flag.class); + private final EnumSet flags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be. + private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector + + // Paper start - remove streams from pathfindergoalselector; make sure types are not empty + public Goal() { + if (this.goalTypes.size() == 0) { + this.goalTypes.add(Flag.UNKNOWN_BEHAVIOR); + } + } + // Paper end - remove streams from pathfindergoalselector public abstract boolean canUse(); @@ -30,8 +39,13 @@ public abstract class Goal { } public void setFlags(EnumSet controls) { - this.flags.clear(); - this.flags.addAll(controls); + // Paper start - remove streams from pathfindergoalselector + this.goalTypes.clear(); + this.goalTypes.addAll(controls); + if (this.goalTypes.size() == 0) { + this.goalTypes.add(Flag.UNKNOWN_BEHAVIOR); + } + // Paper end - remove streams from pathfindergoalselector } @Override @@ -39,8 +53,10 @@ public abstract class Goal { return this.getClass().getSimpleName(); } - public EnumSet getFlags() { - return this.flags; + // Paper start - remove streams from pathfindergoalselector + public com.destroystokyo.paper.util.set.OptimizedSmallEnumSet getFlags() { + return this.goalTypes; + // Paper end - remove streams from pathfindergoalselector } protected int adjustedTickDelay(int ticks) { diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java index 040d62effc651d14d3557f8ff582cb078b74ae1e..38af5c7280366fd6ec077f3d914ea5f3ee77451a 100644 --- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java +++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java @@ -31,10 +31,12 @@ public class GoalSelector { private final Map lockedFlags = new EnumMap<>(Goal.Flag.class); private final Set availableGoals = Sets.newLinkedHashSet(); private final Supplier profiler; - private final EnumSet disabledFlags = EnumSet.noneOf(Goal.Flag.class); + private final EnumSet disabledFlags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be. + private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector private int tickCount; private int newGoalRate = 3; private int curRate; + private static final Goal.Flag[] GOAL_FLAG_VALUES = Goal.Flag.values(); // Paper - remove streams from pathfindergoalselector public GoalSelector(Supplier profiler) { this.profiler = profiler; @@ -64,22 +66,32 @@ public class GoalSelector { } // Paper end public void removeGoal(Goal goal) { - this.availableGoals.stream().filter(wrappedGoal -> wrappedGoal.getGoal() == goal).filter(WrappedGoal::isRunning).forEach(WrappedGoal::stop); - this.availableGoals.removeIf(wrappedGoal -> wrappedGoal.getGoal() == goal); - } - - private static boolean goalContainsAnyFlags(WrappedGoal goal, EnumSet controls) { - for (Goal.Flag flag : goal.getFlags()) { - if (controls.contains(flag)) { - return true; + // Paper start - remove streams from pathfindergoalselector + for (java.util.Iterator iterator = this.availableGoals.iterator(); iterator.hasNext();) { + WrappedGoal goalWrapped = iterator.next(); + if (goalWrapped.getGoal() != goal) { + continue; } + if (goalWrapped.isRunning()) { + goalWrapped.stop(); + } + iterator.remove(); } + // Paper end - remove streams from pathfindergoalselector + } - return false; + private static boolean goalContainsAnyFlags(WrappedGoal goal, com.destroystokyo.paper.util.set.OptimizedSmallEnumSet controls) { + return goal.getFlags().hasCommonElements(controls); // Paper } private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map goalsByControl) { - for (Goal.Flag flag : goal.getFlags()) { + // Paper start + long flagIterator = goal.getFlags().getBackingSet(); + int wrappedGoalSize = goal.getFlags().size(); + for (int i = 0; i < wrappedGoalSize; ++i) { + final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; + flagIterator ^= io.papermc.paper.util.IntegerUtil.getTrailingBit(flagIterator); + // Paper end if (!goalsByControl.getOrDefault(flag, NO_GOAL).canBeReplacedBy(goal)) { return false; } @@ -93,7 +105,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(); } } @@ -111,11 +123,14 @@ public class GoalSelector { profilerFiller.push("goalUpdate"); for (WrappedGoal wrappedGoal2 : this.availableGoals) { - if (!wrappedGoal2.isRunning() - && !goalContainsAnyFlags(wrappedGoal2, this.disabledFlags) - && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags) - && wrappedGoal2.canUse()) { - for (Goal.Flag flag : wrappedGoal2.getFlags()) { + // Paper start + if (!wrappedGoal2.isRunning() && !goalContainsAnyFlags(wrappedGoal2, this.goalTypes) && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags) && wrappedGoal2.canUse()) { + long flagIterator = wrappedGoal2.getFlags().getBackingSet(); + int wrappedGoalSize = wrappedGoal2.getFlags().size(); + for (int i = 0; i < wrappedGoalSize; ++i) { + final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)]; + flagIterator ^= io.papermc.paper.util.IntegerUtil.getTrailingBit(flagIterator); + // Paper end WrappedGoal wrappedGoal3 = this.lockedFlags.getOrDefault(flag, NO_GOAL); wrappedGoal3.stop(); this.lockedFlags.put(flag, wrappedGoal2); @@ -155,11 +170,11 @@ public class GoalSelector { } public void disableControlFlag(Goal.Flag control) { - this.disabledFlags.add(control); + this.goalTypes.add(control); // Paper - remove streams from pathfindergoalselector } public void enableControlFlag(Goal.Flag control) { - this.disabledFlags.remove(control); + this.goalTypes.remove(control); // Paper - remove streams from pathfindergoalselector } public void setControlFlag(Goal.Flag control, boolean enabled) { diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java index b02d3deb550830245c8945ef17d3073ea930fdda..65ccdbaa5230c02d44a5959bca0f6fc30237a6fd 100644 --- a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java +++ b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java @@ -69,8 +69,10 @@ public class WrappedGoal extends Goal { } @Override - public EnumSet getFlags() { + // Paper start - remove streams from pathfindergoalselector + public com.destroystokyo.paper.util.set.OptimizedSmallEnumSet getFlags() { return this.goal.getFlags(); + // Paper end - remove streams from pathfindergoalselector } public boolean isRunning() {