aboutsummaryrefslogtreecommitdiffhomepage
path: root/paper-server/patches/features/0015-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch
diff options
context:
space:
mode:
Diffstat (limited to 'paper-server/patches/features/0015-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch')
-rw-r--r--paper-server/patches/features/0015-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch137
1 files changed, 137 insertions, 0 deletions
diff --git a/paper-server/patches/features/0015-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch b/paper-server/patches/features/0015-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch
new file mode 100644
index 0000000000..84bcbe2f2e
--- /dev/null
+++ b/paper-server/patches/features/0015-Optimize-Pathfinder-Remove-Streams-Optimized-collect.patch
@@ -0,0 +1,137 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+Date: Tue, 4 Aug 2020 22:24:15 +0200
+Subject: [PATCH] Optimize Pathfinder - Remove Streams / Optimized collections
+
+I utilized the IDE to convert streams to non streams code, so shouldn't
+be any risk of behavior change. Only did minor optimization of the
+generated code set to remove unnecessary things.
+
+I expect us to just drop this patch on next major update and re-apply
+it with the IDE again and re-apply the collections optimization.
+
+Optimize collection by creating a list instead of a set of the key and value.
+
+This lets us get faster foreach iteration, as well as avoids map lookups on
+the values when needed.
+
+diff --git a/net/minecraft/world/level/pathfinder/PathFinder.java b/net/minecraft/world/level/pathfinder/PathFinder.java
+index a6ef296f7af1f784e1f0772947a6cb3519a3bc2a..81de6c1bbef1cafd3036e736dd305fbedc8368c6 100644
+--- a/net/minecraft/world/level/pathfinder/PathFinder.java
++++ b/net/minecraft/world/level/pathfinder/PathFinder.java
+@@ -43,8 +43,12 @@ public class PathFinder {
+ if (start == null) {
+ return null;
+ } else {
+- Map<Target, BlockPos> map = targetPositions.stream()
+- .collect(Collectors.toMap(pos -> this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), Function.identity()));
++ // Paper start - Perf: remove streams and optimize collection
++ List<Map.Entry<Target, BlockPos>> map = Lists.newArrayList();
++ for (BlockPos pos : targetPositions) {
++ map.add(new java.util.AbstractMap.SimpleEntry<>(this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos));
++ }
++ // Paper end - Perf: remove streams and optimize collection
+ Path path = this.findPath(start, map, maxRange, accuracy, searchDepthMultiplier);
+ this.nodeEvaluator.done();
+ return path;
+@@ -52,19 +56,19 @@ public class PathFinder {
+ }
+
+ @Nullable
+- private Path findPath(Node node, Map<Target, BlockPos> targetPositions, float maxRange, int accuracy, float searchDepthMultiplier) {
++ private Path findPath(Node node, List<Map.Entry<Target, BlockPos>> positions, float maxRange, int accuracy, float searchDepthMultiplier) { // Paper - optimize collection
+ ProfilerFiller profilerFiller = Profiler.get();
+ profilerFiller.push("find_path");
+ profilerFiller.markForCharting(MetricCategory.PATH_FINDING);
+- Set<Target> set = targetPositions.keySet();
++ // Set<Target> set = targetPositions.keySet(); // Paper
+ node.g = 0.0F;
+- node.h = this.getBestH(node, set);
++ node.h = this.getBestH(node, positions); // Paper - optimize collection
+ node.f = node.h;
+ this.openSet.clear();
+ this.openSet.insert(node);
+- Set<Node> set1 = ImmutableSet.of();
++ // Set<Node> set1 = ImmutableSet.of(); // Paper - unused - diff on change
+ int i = 0;
+- Set<Target> set2 = Sets.newHashSetWithExpectedSize(set.size());
++ List<Map.Entry<Target, BlockPos>> entryList = Lists.newArrayListWithExpectedSize(positions.size()); // Paper - optimize collection
+ int i1 = (int)(this.maxVisitedNodes * searchDepthMultiplier);
+
+ while (!this.openSet.isEmpty()) {
+@@ -75,14 +79,18 @@ public class PathFinder {
+ Node node1 = this.openSet.pop();
+ node1.closed = true;
+
+- for (Target target : set) {
++ // Paper start - optimize collection
++ for (int positionIndex = 0, size = positions.size(); positionIndex < size; positionIndex++) {
++ final Map.Entry<Target, BlockPos> entry = positions.get(positionIndex);
++ Target target = entry.getKey();
+ if (node1.distanceManhattan(target) <= accuracy) {
+ target.setReached();
+- set2.add(target);
++ entryList.add(entry);
++ // Paper end - Perf: remove streams and optimize collection
+ }
+ }
+
+- if (!set2.isEmpty()) {
++ if (!entryList.isEmpty()) { // Paper - Perf: remove streams and optimize collection; rename
+ break;
+ }
+
+@@ -97,7 +105,7 @@ public class PathFinder {
+ if (node2.walkedDistance < maxRange && (!node2.inOpenSet() || f1 < node2.g)) {
+ node2.cameFrom = node1;
+ node2.g = f1;
+- node2.h = this.getBestH(node2, set) * 1.5F;
++ node2.h = this.getBestH(node2, positions) * 1.5F; // Paper - Perf: remove streams and optimize collection
+ if (node2.inOpenSet()) {
+ this.openSet.changeCost(node2, node2.g + node2.h);
+ } else {
+@@ -109,25 +117,34 @@ public class PathFinder {
+ }
+ }
+
+- Optional<Path> optional = !set2.isEmpty()
+- ? set2.stream()
+- .map(pathfinder -> this.reconstructPath(pathfinder.getBestNode(), targetPositions.get(pathfinder), true))
+- .min(Comparator.comparingInt(Path::getNodeCount))
+- : set.stream()
+- .map(pathfinder -> this.reconstructPath(pathfinder.getBestNode(), targetPositions.get(pathfinder), false))
+- .min(Comparator.comparingDouble(Path::getDistToTarget).thenComparingInt(Path::getNodeCount));
++ // Paper start - Perf: remove streams and optimize collection
++ Path best = null;
++ boolean entryListIsEmpty = entryList.isEmpty();
++ Comparator<Path> comparator = entryListIsEmpty
++ ? Comparator.comparingInt(Path::getNodeCount)
++ : Comparator.comparingDouble(Path::getDistToTarget).thenComparingInt(Path::getNodeCount);
++ for (Map.Entry<Target, BlockPos> entry : entryListIsEmpty ? positions : entryList) {
++ Path path = this.reconstructPath(entry.getKey().getBestNode(), entry.getValue(), !entryListIsEmpty);
++ if (best == null || comparator.compare(path, best) < 0) {
++ best = path;
++ }
++ }
+ profilerFiller.pop();
+- return optional.isEmpty() ? null : optional.get();
++ return best;
++ // Paper end - Perf: remove streams and optimize collection
+ }
+
+ protected float distance(Node first, Node second) {
+ return first.distanceTo(second);
+ }
+
+- private float getBestH(Node node, Set<Target> targets) {
++ private float getBestH(Node node, List<Map.Entry<Target, BlockPos>> targets) { // Paper - Perf: remove streams and optimize collection; Set<Target> -> List<Map.Entry<Target, BlockPos>>
+ float f = Float.MAX_VALUE;
+
+- for (Target target : targets) {
++ // Paper start - Perf: remove streams and optimize collection
++ for (int i = 0, targetsSize = targets.size(); i < targetsSize; i++) {
++ final Target target = targets.get(i).getKey();
++ // Paper end - Perf: remove streams and optimize collection
+ float f1 = node.distanceTo(target);
+ target.updateBest(f1, node);
+ f = Math.min(f1, f);