From 974d84555d47b46d87aca6f8365d6608f17a6d27 Mon Sep 17 00:00:00 2001 From: William Blake Galbreath Date: Fri, 6 Mar 2020 16:33:58 -0600 Subject: [PATCH] Optimize entity pathfinder selector --- .../co/aikar/timings/MinecraftTimings.java | 6 + .../tuinity/util/OptimizedSmallEnumSet.java | 65 +++++++++ .../net/minecraft/server/PathfinderGoal.java | 6 +- .../server/PathfinderGoalSelector.java | 132 +++++++++++------- .../server/PathfinderGoalWrapped.java | 6 +- 5 files changed, 161 insertions(+), 54 deletions(-) create mode 100644 src/main/java/com/tuinity/tuinity/util/OptimizedSmallEnumSet.java diff --git a/src/main/java/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java index 434833d50e..7fc9a625a3 100644 --- a/src/main/java/co/aikar/timings/MinecraftTimings.java +++ b/src/main/java/co/aikar/timings/MinecraftTimings.java @@ -43,6 +43,12 @@ public final class MinecraftTimings { public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update"); public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate"); + // Purpur start + public static final Timing goalCleanup = Timings.ofSafe("PathfinderGoal - Cleanup"); + public static final Timing goalUpdate = Timings.ofSafe("PathfinderGoal - Update"); + public static final Timing goalTick = Timings.ofSafe("PathfinderGoal - Tick"); + // Purpur end + private static final Map, String> taskNameCache = new MapMaker().weakKeys().makeMap(); private MinecraftTimings() {} diff --git a/src/main/java/com/tuinity/tuinity/util/OptimizedSmallEnumSet.java b/src/main/java/com/tuinity/tuinity/util/OptimizedSmallEnumSet.java new file mode 100644 index 0000000000..68db858885 --- /dev/null +++ b/src/main/java/com/tuinity/tuinity/util/OptimizedSmallEnumSet.java @@ -0,0 +1,65 @@ +package com.tuinity.tuinity.util; + +import java.util.Collection; + +// containing utils to work on small numbers of enums +public final class OptimizedSmallEnumSet { + + private final Class enumClass; + private long backingSet; + + public OptimizedSmallEnumSet(final Class clazz) { + if (clazz == null) { + throw new IllegalArgumentException("Null class"); + } + if (!clazz.isEnum()) { + throw new IllegalArgumentException("Class must be enum, not " + clazz.getCanonicalName()); + } + this.enumClass = clazz; + } + + public boolean addUnchecked(final E element) { + final int ordinal = element.ordinal(); + final long key = 1L << ordinal; + + final long prev = this.backingSet; + this.backingSet = prev | key; + + return (prev & key) == 0; + } + + public boolean removeUnchecked(final E element) { + final int ordinal = element.ordinal(); + final long key = 1L << ordinal; + + final long prev = this.backingSet; + this.backingSet = prev & ~key; + + return (prev & key) != 0; + } + + public void clear() { + this.backingSet = 0L; + } + + public int size() { + return Long.bitCount(this.backingSet); + } + + public void addAllUnchecked(final Collection enums) { + for (final E element : enums) { + if (element == null) { + throw new NullPointerException("Null element"); + } + this.backingSet |= (1L << element.ordinal()); + } + } + + public long getBackingSet() { + return this.backingSet; + } + + public boolean hasCommonElements(final OptimizedSmallEnumSet other) { + return (other.backingSet & this.backingSet) != 0; + } +} diff --git a/src/main/java/net/minecraft/server/PathfinderGoal.java b/src/main/java/net/minecraft/server/PathfinderGoal.java index e059930ba7..6c307a9095 100644 --- a/src/main/java/net/minecraft/server/PathfinderGoal.java +++ b/src/main/java/net/minecraft/server/PathfinderGoal.java @@ -4,7 +4,7 @@ import java.util.EnumSet; public abstract class PathfinderGoal { - private final EnumSet a = EnumSet.noneOf(PathfinderGoal.Type.class); + private final com.tuinity.tuinity.util.OptimizedSmallEnumSet a = new com.tuinity.tuinity.util.OptimizedSmallEnumSet<>(PathfinderGoal.Type.class); // Purpur public PathfinderGoal() {} @@ -30,14 +30,14 @@ public abstract class PathfinderGoal { public void setTypes(EnumSet types) { this.a(types); } // Purpur - OBFHELPER public void a(EnumSet enumset) { this.a.clear(); - this.a.addAll(enumset); + this.a.addAllUnchecked(enumset); // Purpur } public String toString() { return this.getClass().getSimpleName(); } - public EnumSet i() { + public com.tuinity.tuinity.util.OptimizedSmallEnumSet i() { // Purpur return this.a; } diff --git a/src/main/java/net/minecraft/server/PathfinderGoalSelector.java b/src/main/java/net/minecraft/server/PathfinderGoalSelector.java index 935136771e..b2bea3e241 100644 --- a/src/main/java/net/minecraft/server/PathfinderGoalSelector.java +++ b/src/main/java/net/minecraft/server/PathfinderGoalSelector.java @@ -12,6 +12,7 @@ import org.apache.logging.log4j.Logger; public class PathfinderGoalSelector { private static final Logger LOGGER = LogManager.getLogger(); + private static PathfinderGoalWrapped EMPTY_GOAL() { return b; }; // Purpur - OBFHELPER private static final PathfinderGoalWrapped b = new PathfinderGoalWrapped(Integer.MAX_VALUE, new PathfinderGoal() { @Override public boolean a() { @@ -26,7 +27,7 @@ public class PathfinderGoalSelector { private final Map c = new EnumMap(PathfinderGoal.Type.class); private final Set d = Sets.newLinkedHashSet();private Set getTasks() { return d; }// Paper - OBFHELPER private final GameProfilerFiller e; - private final EnumSet f = EnumSet.noneOf(PathfinderGoal.Type.class); + private final com.tuinity.tuinity.util.OptimizedSmallEnumSet disabledTypes = new com.tuinity.tuinity.util.OptimizedSmallEnumSet<>(PathfinderGoal.Type.class); // Purpur private int g = 3;private int getTickRate() { return g; } // Paper - OBFHELPER private int curRate;private int getCurRate() { return curRate; } private void incRate() { this.curRate++; } // Paper TODO @@ -58,65 +59,98 @@ public class PathfinderGoalSelector { // Paper end public void a(PathfinderGoal pathfindergoal) { - this.d.stream().filter((pathfindergoalwrapped) -> { - return pathfindergoalwrapped.j() == pathfindergoal; - }).filter(PathfinderGoalWrapped::g).forEach(PathfinderGoalWrapped::d); - this.d.removeIf((pathfindergoalwrapped) -> { - return pathfindergoalwrapped.j() == pathfindergoal; - }); + // Purpur start + for (java.util.Iterator iterator = getTasks().iterator(); iterator.hasNext();) { + PathfinderGoalWrapped wrappedGoal = iterator.next(); + if (wrappedGoal.getGoal() != pathfindergoal) { + continue; + } + if (wrappedGoal.isRunning()) { + wrappedGoal.d(); // reset goal + } + iterator.remove(); + } + // Purpur end } + private static final PathfinderGoal.Type[] PATHFINDER_GOAL_TYPES = PathfinderGoal.Type.values(); // Purpur + public void doTick() { this.e.enter("goalCleanup"); - this.c().filter((pathfindergoalwrapped) -> { - boolean flag; + // Purpur start + co.aikar.timings.MinecraftTimings.goalCleanup.startTiming(); + for (java.util.Iterator iter = getTasks().iterator(); iter.hasNext();) { + PathfinderGoalWrapped wrappedGoal = iter.next(); + if (!wrappedGoal.isRunning()) { + continue; // goal is not running + } + if (!this.disabledTypes.hasCommonElements(wrappedGoal.getTypes()) && wrappedGoal.b()) { + continue; // goal type is not disabled and should continue running + } + wrappedGoal.d(); // reset goal + } + this.c.forEach((type, goal) -> { + if (!goal.isRunning()) { + this.c.remove(type); // remove locked goal types if goal no longer running + } + }); + co.aikar.timings.MinecraftTimings.goalCleanup.stopTiming(); + // Purpur end + this.e.exit(); + this.e.enter("goalUpdate"); + // Purpur start + co.aikar.timings.MinecraftTimings.goalUpdate.startTiming(); + goal_update_loop: + for (java.util.Iterator iterator = getTasks().iterator(); iterator.hasNext();) { + PathfinderGoalWrapped wrappedGoal = iterator.next(); + if (wrappedGoal.isRunning()) { + continue; // goal is already running + } + + com.tuinity.tuinity.util.OptimizedSmallEnumSet wrappedGoalSet = wrappedGoal.getTypes(); - if (pathfindergoalwrapped.g()) { - Stream stream = pathfindergoalwrapped.i().stream(); - EnumSet enumset = this.f; + if (disabledTypes.hasCommonElements(wrappedGoalSet)) { + continue; // goal type is disabled + } - this.f.getClass(); - if (!stream.anyMatch(enumset::contains) && pathfindergoalwrapped.b()) { - flag = false; - return flag; + long k = wrappedGoalSet.getBackingSet(); + int wrappedGoalSize = wrappedGoalSet.size(); + for (int i = 0; i < wrappedGoalSize; ++i) { + PathfinderGoal.Type type = PATHFINDER_GOAL_TYPES[Long.numberOfTrailingZeros(k)]; + k ^= -k & k; // Tuinity's ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit + if (!c.getOrDefault(type, EMPTY_GOAL()).canBeReplacedBy(wrappedGoal)) { + continue goal_update_loop; // goal type(s) is locked by another running goal with same type(s) } } - flag = true; - return flag; - }).forEach(PathfinderGoal::d); - this.c.forEach((pathfindergoal_type, pathfindergoalwrapped) -> { - if (!pathfindergoalwrapped.g()) { - this.c.remove(pathfindergoal_type); + if (!wrappedGoal.a()) { + continue; // goal failed to run } - }); - this.e.exit(); - this.e.enter("goalUpdate"); - this.d.stream().filter((pathfindergoalwrapped) -> { - return !pathfindergoalwrapped.g(); - }).filter((pathfindergoalwrapped) -> { - Stream stream = pathfindergoalwrapped.i().stream(); - EnumSet enumset = this.f; - - this.f.getClass(); - return stream.noneMatch(enumset::contains); - }).filter((pathfindergoalwrapped) -> { - return pathfindergoalwrapped.i().stream().allMatch((pathfindergoal_type) -> { - return ((PathfinderGoalWrapped) this.c.getOrDefault(pathfindergoal_type, PathfinderGoalSelector.b)).a(pathfindergoalwrapped); - }); - }).filter(PathfinderGoalWrapped::a).forEach((pathfindergoalwrapped) -> { - pathfindergoalwrapped.i().forEach((pathfindergoal_type) -> { - PathfinderGoalWrapped pathfindergoalwrapped1 = (PathfinderGoalWrapped) this.c.getOrDefault(pathfindergoal_type, PathfinderGoalSelector.b); - - pathfindergoalwrapped1.d(); - this.c.put(pathfindergoal_type, pathfindergoalwrapped); - }); - pathfindergoalwrapped.c(); - }); + k = wrappedGoalSet.getBackingSet(); + wrappedGoalSize = wrappedGoalSet.size(); + for (int i = 0; i < wrappedGoalSize; ++i) { + PathfinderGoal.Type type = PATHFINDER_GOAL_TYPES[Long.numberOfTrailingZeros(k)]; + k ^= -k & k; // Tuinity's ca.spottedleaf.concurrentutil.util.IntegerUtil.getTrailingBit + c.getOrDefault(type, EMPTY_GOAL()).d(); // reset goal + c.put(type, wrappedGoal); + } + + wrappedGoal.c(); // start goal + } + co.aikar.timings.MinecraftTimings.goalUpdate.stopTiming(); + // Purpur end this.e.exit(); this.e.enter("goalTick"); - this.c().forEach(PathfinderGoalWrapped::e); + // Purpur start + co.aikar.timings.MinecraftTimings.goalTick.startTiming(); + for (PathfinderGoalWrapped wrappedGoal : getTasks()) { + if (wrappedGoal.isRunning()) { + wrappedGoal.e(); // goal tick + } + } + co.aikar.timings.MinecraftTimings.goalTick.stopTiming(); + // Purpur end this.e.exit(); } @@ -125,11 +159,11 @@ public class PathfinderGoalSelector { } public void a(PathfinderGoal.Type pathfindergoal_type) { - this.f.add(pathfindergoal_type); + this.disabledTypes.addUnchecked(pathfindergoal_type); // Purpur } public void b(PathfinderGoal.Type pathfindergoal_type) { - this.f.remove(pathfindergoal_type); + this.disabledTypes.removeUnchecked(pathfindergoal_type); // Purpur } public void a(PathfinderGoal.Type pathfindergoal_type, boolean flag) { diff --git a/src/main/java/net/minecraft/server/PathfinderGoalWrapped.java b/src/main/java/net/minecraft/server/PathfinderGoalWrapped.java index 29657fed75..172e74b965 100644 --- a/src/main/java/net/minecraft/server/PathfinderGoalWrapped.java +++ b/src/main/java/net/minecraft/server/PathfinderGoalWrapped.java @@ -14,6 +14,7 @@ public class PathfinderGoalWrapped extends PathfinderGoal { this.a = pathfindergoal; } + public boolean canBeReplacedBy(PathfinderGoalWrapped pathfindergoalwrapped) { return a(pathfindergoalwrapped); } // Purpur - OBFHELPER public boolean a(PathfinderGoalWrapped pathfindergoalwrapped) { return this.E_() && pathfindergoalwrapped.h() < this.h(); } @@ -59,8 +60,8 @@ public class PathfinderGoalWrapped extends PathfinderGoal { this.a.a(enumset); } - @Override - public EnumSet i() { + public com.tuinity.tuinity.util.OptimizedSmallEnumSet getTypes() { return i(); } // Purpur - OBFHELPER + @Override public com.tuinity.tuinity.util.OptimizedSmallEnumSet i() { // Purpur return this.a.i(); } @@ -73,6 +74,7 @@ public class PathfinderGoalWrapped extends PathfinderGoal { return this.b; } + public PathfinderGoal getGoal() { return j(); } // Purpur - OBFHELPER public PathfinderGoal j() { return this.a; } -- 2.24.0