2021-06-14 03:06:38 +02:00
|
|
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
|
From: Aikar <aikar@aikar.co>
|
|
|
|
Date: Sun, 3 May 2020 22:35:09 -0400
|
|
|
|
Subject: [PATCH] Optimize Voxel Shape Merging
|
|
|
|
|
|
|
|
This method shows up as super hot in profiler, and also a high "self" time.
|
|
|
|
|
|
|
|
Upon analyzing, it appears most usages of this method fall down to the final
|
|
|
|
else statement of the nasty ternary.
|
|
|
|
|
|
|
|
Upon even further analyzation, it appears then the majority of those have a
|
|
|
|
consistent list 1.... One with Infinity head and Tails.
|
|
|
|
|
|
|
|
First optimization is to detect these infinite states and immediately return that
|
|
|
|
VoxelShapeMergerList so we can avoid testing the rest for most cases.
|
|
|
|
|
|
|
|
Break the method into 2 to help the JVM promote inlining of this fast path.
|
|
|
|
|
|
|
|
Then it was also noticed that VoxelShapeMergerList constructor is also a hotspot
|
|
|
|
with a high self time...
|
|
|
|
|
|
|
|
Well, knowing that in most cases our list 1 is actualy the same value, it allows
|
|
|
|
us to know that with an infinite list1, the result on the merger is essentially
|
|
|
|
list2 as the final values.
|
|
|
|
|
|
|
|
This let us analyze the 2 potential states (Infinite with 2 sources or 4 sources)
|
|
|
|
and compute a deterministic result for the MergerList values.
|
|
|
|
|
|
|
|
Additionally, this lets us avoid even allocating new objects for this too, further
|
|
|
|
reducing memory usage.
|
|
|
|
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/phys/shapes/IndirectMerger.java b/src/main/java/net/minecraft/world/phys/shapes/IndirectMerger.java
|
2024-04-12 21:14:06 +02:00
|
|
|
index e164c524aef4fa81fe96ac43454eecff1c38b9c1..9cfbbc61fcfc678f0988d6d45c7994d128051744 100644
|
2021-06-14 03:06:38 +02:00
|
|
|
--- a/src/main/java/net/minecraft/world/phys/shapes/IndirectMerger.java
|
|
|
|
+++ b/src/main/java/net/minecraft/world/phys/shapes/IndirectMerger.java
|
|
|
|
@@ -10,12 +10,33 @@ public class IndirectMerger implements IndexMerger {
|
|
|
|
private final int[] firstIndices;
|
|
|
|
private final int[] secondIndices;
|
|
|
|
private final int resultLength;
|
|
|
|
+ // Paper start
|
|
|
|
+ private static final int[] INFINITE_B_1 = new int[]{1, 1};
|
|
|
|
+ private static final int[] INFINITE_B_0 = new int[]{0, 0};
|
|
|
|
+ private static final int[] INFINITE_C = new int[]{0, 1};
|
|
|
|
+ // Paper end
|
|
|
|
|
|
|
|
public IndirectMerger(DoubleList first, DoubleList second, boolean includeFirstOnly, boolean includeSecondOnly) {
|
|
|
|
double d = Double.NaN;
|
|
|
|
int i = first.size();
|
|
|
|
int j = second.size();
|
|
|
|
int k = i + j;
|
|
|
|
+ // Paper start - optimize common path of infinity doublelist
|
|
|
|
+ int size = first.size();
|
|
|
|
+ double tail = first.getDouble(size - 1);
|
|
|
|
+ double head = first.getDouble(0);
|
|
|
|
+ if (head == Double.NEGATIVE_INFINITY && tail == Double.POSITIVE_INFINITY && !includeFirstOnly && !includeSecondOnly && (size == 2 || size == 4)) {
|
|
|
|
+ this.result = second.toDoubleArray();
|
|
|
|
+ this.resultLength = second.size();
|
|
|
|
+ if (size == 2) {
|
|
|
|
+ this.firstIndices = INFINITE_B_0;
|
|
|
|
+ } else {
|
|
|
|
+ this.firstIndices = INFINITE_B_1;
|
|
|
|
+ }
|
|
|
|
+ this.secondIndices = INFINITE_C;
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ // Paper end
|
|
|
|
this.result = new double[k];
|
|
|
|
this.firstIndices = new int[k];
|
|
|
|
this.secondIndices = new int[k];
|
|
|
|
diff --git a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
|
2024-04-16 21:44:59 +02:00
|
|
|
index 0fdd2cdd8d215ca1523eda8ad7316cdd5f41a6b5..86df4ef44d0a5107ee929dfd40d8ccb0779e8bfc 100644
|
2021-06-14 03:06:38 +02:00
|
|
|
--- a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
|
|
|
|
+++ b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
|
2024-04-12 21:14:06 +02:00
|
|
|
@@ -286,9 +286,21 @@ public final class Shapes {
|
2021-06-14 03:06:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@VisibleForTesting
|
|
|
|
- protected static IndexMerger createIndexMerger(int size, DoubleList first, DoubleList second, boolean includeFirst, boolean includeSecond) {
|
|
|
|
+ private static IndexMerger createIndexMerger(int size, DoubleList first, DoubleList second, boolean includeFirst, boolean includeSecond) { // Paper - private
|
|
|
|
+ // Paper start - fast track the most common scenario
|
|
|
|
+ // doublelist is usually a DoubleArrayList with Infinite head/tails that falls to the final else clause
|
|
|
|
+ // This is actually the most common path, so jump to it straight away
|
|
|
|
+ if (first.getDouble(0) == Double.NEGATIVE_INFINITY && first.getDouble(first.size() - 1) == Double.POSITIVE_INFINITY) {
|
|
|
|
+ return new IndirectMerger(first, second, includeFirst, includeSecond);
|
|
|
|
+ }
|
|
|
|
+ // Split out rest to hopefully inline the above
|
|
|
|
+ return lessCommonMerge(size, first, second, includeFirst, includeSecond);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static IndexMerger lessCommonMerge(int size, DoubleList first, DoubleList second, boolean includeFirst, boolean includeSecond) {
|
|
|
|
int i = first.size() - 1;
|
|
|
|
int j = second.size() - 1;
|
|
|
|
+ // Paper note - Rewrite below as optimized order if instead of nasty ternary
|
|
|
|
if (first instanceof CubePointRange && second instanceof CubePointRange) {
|
|
|
|
long l = lcm(i, j);
|
2024-04-16 21:44:59 +02:00
|
|
|
if ((long)size * l <= 256L) {
|
2024-04-12 21:14:06 +02:00
|
|
|
@@ -296,15 +308,22 @@ public final class Shapes {
|
2021-06-14 03:06:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-12 21:14:06 +02:00
|
|
|
- if (first.getDouble(i) < second.getDouble(0) - 1.0E-7) {
|
2021-06-14 03:06:38 +02:00
|
|
|
+ // Paper start - Identical happens more often than Disjoint
|
|
|
|
+ if (i == j && Objects.equals(first, second)) {
|
|
|
|
+ if (first instanceof IdenticalMerger) {
|
|
|
|
+ return (IndexMerger) first;
|
|
|
|
+ } else if (second instanceof IdenticalMerger) {
|
|
|
|
+ return (IndexMerger) second;
|
|
|
|
+ }
|
|
|
|
+ return new IdenticalMerger(first);
|
2024-04-12 21:14:06 +02:00
|
|
|
+ } else if (first.getDouble(i) < second.getDouble(0) - 1.0E-7) {
|
2021-06-14 03:06:38 +02:00
|
|
|
return new NonOverlappingMerger(first, second, false);
|
2024-04-12 21:14:06 +02:00
|
|
|
} else if (second.getDouble(j) < first.getDouble(0) - 1.0E-7) {
|
2021-06-14 03:06:38 +02:00
|
|
|
return new NonOverlappingMerger(second, first, true);
|
|
|
|
} else {
|
2024-04-12 21:14:06 +02:00
|
|
|
- return (IndexMerger)(i == j && Objects.equals(first, second)
|
|
|
|
- ? new IdenticalMerger(first)
|
|
|
|
- : new IndirectMerger(first, second, includeFirst, includeSecond));
|
2021-06-14 03:06:38 +02:00
|
|
|
+ return new IndirectMerger(first, second, includeFirst, includeSecond);
|
|
|
|
}
|
|
|
|
+ // Paper end
|
|
|
|
}
|
|
|
|
|
|
|
|
public interface DoubleLineConsumer {
|