From 92d2907d14e2dd9b230940cda69f383c9b9f6080 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sun, 12 Apr 2020 18:31:14 -0400 Subject: [PATCH] Multiple memory and performance optimizations (removing streams) --- ...72-Reduce-Either-Optional-allocation.patch | 59 +++++++++++++ ...0473-Remove-streams-from-PairedQueue.patch | 82 +++++++++++++++++++ ...474-Remove-streams-from-MinecraftKey.patch | 50 +++++++++++ ...e-memory-footprint-of-NBTTagCompound.patch | 54 ++++++++++++ 4 files changed, 245 insertions(+) create mode 100644 Spigot-Server-Patches/0472-Reduce-Either-Optional-allocation.patch create mode 100644 Spigot-Server-Patches/0473-Remove-streams-from-PairedQueue.patch create mode 100644 Spigot-Server-Patches/0474-Remove-streams-from-MinecraftKey.patch create mode 100644 Spigot-Server-Patches/0475-Reduce-memory-footprint-of-NBTTagCompound.patch diff --git a/Spigot-Server-Patches/0472-Reduce-Either-Optional-allocation.patch b/Spigot-Server-Patches/0472-Reduce-Either-Optional-allocation.patch new file mode 100644 index 0000000000..d99a4e2f89 --- /dev/null +++ b/Spigot-Server-Patches/0472-Reduce-Either-Optional-allocation.patch @@ -0,0 +1,59 @@ +From e5ba87668be36b649b80633170a33e7ad3fae019 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Mon, 6 Apr 2020 18:35:09 -0700 +Subject: [PATCH] Reduce Either Optional allocation + +In order to get chunk values, we shouldn't need to create +an optional each time. + +diff --git a/src/main/java/com/mojang/datafixers/util/Either.java b/src/main/java/com/mojang/datafixers/util/Either.java +index a90adac7b..efae74b71 100644 +--- a/src/main/java/com/mojang/datafixers/util/Either.java ++++ b/src/main/java/com/mojang/datafixers/util/Either.java +@@ -22,10 +22,10 @@ public abstract class Either implements App, L> { + } + + private static final class Left extends Either { +- private final L value; ++ private final L value; private final Optional valueOptional; // Paper - reduce the optional allocation... + + public Left(final L value) { +- this.value = value; ++ this.value = value; this.valueOptional = Optional.of(value); // Paper - reduce the optional allocation... + } + + @Override +@@ -51,7 +51,7 @@ public abstract class Either implements App, L> { + + @Override + public Optional left() { +- return Optional.of(value); ++ return this.valueOptional; // Paper - reduce the optional allocation... + } + + @Override +@@ -83,10 +83,10 @@ public abstract class Either implements App, L> { + } + + private static final class Right extends Either { +- private final R value; ++ private final R value; private final Optional valueOptional; // Paper - reduce the optional allocation... + + public Right(final R value) { +- this.value = value; ++ this.value = value; this.valueOptional = Optional.of(value); // Paper - reduce the optional allocation... + } + + @Override +@@ -117,7 +117,7 @@ public abstract class Either implements App, L> { + + @Override + public Optional right() { +- return Optional.of(value); ++ return this.valueOptional; // Paper - reduce the optional allocation... + } + + @Override +-- +2.25.1 + diff --git a/Spigot-Server-Patches/0473-Remove-streams-from-PairedQueue.patch b/Spigot-Server-Patches/0473-Remove-streams-from-PairedQueue.patch new file mode 100644 index 0000000000..888cbd212f --- /dev/null +++ b/Spigot-Server-Patches/0473-Remove-streams-from-PairedQueue.patch @@ -0,0 +1,82 @@ +From 7d32a5500fd79455112616c6a1646fb71b1dac43 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Mon, 6 Apr 2020 18:10:43 -0700 +Subject: [PATCH] Remove streams from PairedQueue + +We shouldn't be doing stream calls just to see if the queue is +empty. This creates loads of garbage thanks to how often it's called. + +diff --git a/src/main/java/net/minecraft/server/PairedQueue.java b/src/main/java/net/minecraft/server/PairedQueue.java +index 85bb22e4b..2369afb4f 100644 +--- a/src/main/java/net/minecraft/server/PairedQueue.java ++++ b/src/main/java/net/minecraft/server/PairedQueue.java +@@ -20,32 +20,30 @@ public interface PairedQueue { + + public static final class a implements PairedQueue { + +- private final List> a; ++ private final List> a; private final List> getQueues() { return this.a; } // Paper - OBFHELPER + + public a(int i) { +- this.a = (List) IntStream.range(0, i).mapToObj((j) -> { +- return Queues.newConcurrentLinkedQueue(); +- }).collect(Collectors.toList()); ++ // Paper start - remove streams ++ this.a = new java.util.ArrayList<>(i); // queues ++ for (int j = 0; j < i; ++j) { ++ this.getQueues().add(Queues.newConcurrentLinkedQueue()); ++ } ++ // Paper end - remove streams + } + + @Nullable + @Override + public Runnable a() { +- Iterator iterator = this.a.iterator(); +- +- Runnable runnable; +- +- do { +- if (!iterator.hasNext()) { +- return null; ++ // Paper start - remove iterator creation ++ for (int i = 0, len = this.getQueues().size(); i < len; ++i) { ++ Queue queue = this.getQueues().get(i); ++ Runnable ret = queue.poll(); ++ if (ret != null) { ++ return ret; + } +- +- Queue queue = (Queue) iterator.next(); +- +- runnable = (Runnable) queue.poll(); +- } while (runnable == null); +- +- return runnable; ++ } ++ return null; ++ // Paper end - remove iterator creation + } + + public boolean a(PairedQueue.b pairedqueue_b) { +@@ -57,7 +55,16 @@ public interface PairedQueue { + + @Override + public boolean b() { +- return this.a.stream().allMatch(Collection::isEmpty); ++ // Paper start - remove streams ++ // why are we doing streams every time we might want to execute a task? ++ for (int i = 0, len = this.getQueues().size(); i < len; ++i) { ++ Queue queue = this.getQueues().get(i); ++ if (!queue.isEmpty()) { ++ return false; ++ } ++ } ++ return true; ++ // Paper end - remove streams + } + } + +-- +2.25.1 + diff --git a/Spigot-Server-Patches/0474-Remove-streams-from-MinecraftKey.patch b/Spigot-Server-Patches/0474-Remove-streams-from-MinecraftKey.patch new file mode 100644 index 0000000000..234e44e315 --- /dev/null +++ b/Spigot-Server-Patches/0474-Remove-streams-from-MinecraftKey.patch @@ -0,0 +1,50 @@ +From 87074d6a3aa62c6ef3c61aff82bb869568066b5c Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Mon, 6 Apr 2020 18:06:24 -0700 +Subject: [PATCH] Remove streams from MinecraftKey + +They produce a lot of garbage. + +diff --git a/src/main/java/net/minecraft/server/MinecraftKey.java b/src/main/java/net/minecraft/server/MinecraftKey.java +index 2b271d3e5..b1beebf0e 100644 +--- a/src/main/java/net/minecraft/server/MinecraftKey.java ++++ b/src/main/java/net/minecraft/server/MinecraftKey.java +@@ -125,15 +125,29 @@ public class MinecraftKey implements Comparable { + } + + private static boolean c(String s) { +- return s.chars().allMatch((i) -> { +- return i == 95 || i == 45 || i >= 97 && i <= 122 || i >= 48 && i <= 57 || i == 47 || i == 46; +- }); ++ // Paper start - remove streams ++ for (int index = 0, len = s.length(); index < len; ++index) { ++ int i = (int)s.charAt(index); ++ boolean condition = i == 95 || i == 45 || i >= 97 && i <= 122 || i >= 48 && i <= 57 || i == 47 || i == 46; // this is copied from the replaced code. ++ if (!condition) { ++ return false; ++ } ++ } ++ return true; ++ // Paper end - remove streams + } + + private static boolean d(String s) { +- return s.chars().allMatch((i) -> { +- return i == 95 || i == 45 || i >= 97 && i <= 122 || i >= 48 && i <= 57 || i == 46; +- }); ++ // Paper start - remove streams ++ for (int index = 0, len = s.length(); index < len; ++index) { ++ int i = (int)s.charAt(index); ++ boolean condition = i == 95 || i == 45 || i >= 97 && i <= 122 || i >= 48 && i <= 57 || i == 46; // this is copied from the replaced code. ++ if (!condition) { ++ return false; ++ } ++ } ++ return true; ++ // Paper end - remove streams + } + + public static class a implements JsonDeserializer, JsonSerializer { +-- +2.25.1 + diff --git a/Spigot-Server-Patches/0475-Reduce-memory-footprint-of-NBTTagCompound.patch b/Spigot-Server-Patches/0475-Reduce-memory-footprint-of-NBTTagCompound.patch new file mode 100644 index 0000000000..c9a4d84385 --- /dev/null +++ b/Spigot-Server-Patches/0475-Reduce-memory-footprint-of-NBTTagCompound.patch @@ -0,0 +1,54 @@ +From 1702763635d87ea03b05354b5e541cda6e348da0 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Mon, 6 Apr 2020 17:39:25 -0700 +Subject: [PATCH] Reduce memory footprint of NBTTagCompound + +Fastutil maps are going to have a lower memory footprint - which +is important because we clone chunk data after reading it for safety. +So, reduce the impact of the clone on GC. + +diff --git a/src/main/java/net/minecraft/server/NBTTagCompound.java b/src/main/java/net/minecraft/server/NBTTagCompound.java +index 98deaba12..02a2ed1ba 100644 +--- a/src/main/java/net/minecraft/server/NBTTagCompound.java ++++ b/src/main/java/net/minecraft/server/NBTTagCompound.java +@@ -31,7 +31,7 @@ public class NBTTagCompound implements NBTBase { + if (i > 512) { + throw new RuntimeException("Tried to read NBT tag with too high complexity, depth > 512"); + } else { +- HashMap hashmap = Maps.newHashMap(); ++ it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap hashmap = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(8, 0.8f); // Paper - reduce memory footprint of NBTTagCompound + + byte b0; + +@@ -67,7 +67,7 @@ public class NBTTagCompound implements NBTBase { + } + + public NBTTagCompound() { +- this(Maps.newHashMap()); ++ this(new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(8, 0.8f)); // Paper - reduce memory footprint of NBTTagCompound + } + + @Override +@@ -402,9 +402,17 @@ public class NBTTagCompound implements NBTBase { + + @Override + public NBTTagCompound clone() { +- Map map = Maps.newHashMap(Maps.transformValues(this.map, NBTBase::clone)); ++ // Paper start - reduce memory footprint of NBTTagCompound ++ it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap ret = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(this.map.size(), 0.8f); + +- return new NBTTagCompound(map); ++ Iterator> iterator = (this.map instanceof it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap) ? ((it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap)this.map).object2ObjectEntrySet().fastIterator() : this.map.entrySet().iterator(); ++ while (iterator.hasNext()) { ++ Map.Entry entry = iterator.next(); ++ ret.put(entry.getKey(), entry.getValue().clone()); ++ } ++ ++ return new NBTTagCompound(ret); ++ // Paper end - reduce memory footprint of NBTTagCompound + } + + public boolean equals(Object object) { +-- +2.25.1 +