From 05d074d5571bdf39bb4264c7f26b9cec417acf42 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Mon, 13 May 2019 20:37:18 -0700 Subject: [PATCH] Optimize DataWatcher The lock in DataWatcher is used to prevent concurrent modifications to the 'd' field (entries in MCP). However any modifications to this map only occur on initialization of an Entity in its constructor. This modification is write-locked. Every other access is through a readlock, which allows the threads to pass if there is no thread holding the writelock. Since the writelock is only obtained in the constructor of the Entity, the further readlocks are actually useless (which get obtained on set, get, etc calls). The entries field ('d' currently) has also been declared as Int2ObjectOpenHashMap to avoid autoboxing on put(), get(), etc calls. --- .../0079-Use-Optimized-Collections.patch | 6 +- .../0393-Elide-lock-in-DataWatcher.patch | 140 ++++++++++++++++++ 2 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 Spigot-Server-Patches/0393-Elide-lock-in-DataWatcher.patch diff --git a/Spigot-Server-Patches/0079-Use-Optimized-Collections.patch b/Spigot-Server-Patches/0079-Use-Optimized-Collections.patch index 296a7cc3f..95e1e7a3a 100644 --- a/Spigot-Server-Patches/0079-Use-Optimized-Collections.patch +++ b/Spigot-Server-Patches/0079-Use-Optimized-Collections.patch @@ -1,4 +1,4 @@ -From 7ec6ae32dc53d6401706f46533d05f47cb554b30 Mon Sep 17 00:00:00 2001 +From b82a8f1e62534de6c4f4bfceeda56a580ca0eaa6 Mon Sep 17 00:00:00 2001 From: Aikar Date: Wed, 30 Mar 2016 02:13:24 -0400 Subject: [PATCH] Use Optimized Collections @@ -13,7 +13,7 @@ These collections are super fast as seen http://java-performance.info/hashmap-overview-jdk-fastutil-goldman-sachs-hppc-koloboke-trove-january-2015/ diff --git a/src/main/java/net/minecraft/server/DataWatcher.java b/src/main/java/net/minecraft/server/DataWatcher.java -index 79a240cd1..6c259effb 100644 +index 79a240cd1d..f224043d8e 100644 --- a/src/main/java/net/minecraft/server/DataWatcher.java +++ b/src/main/java/net/minecraft/server/DataWatcher.java @@ -12,6 +12,7 @@ import java.util.Map; @@ -29,7 +29,7 @@ index 79a240cd1..6c259effb 100644 private static final Map, Integer> b = Maps.newHashMap(); private final Entity c; - private final Map> d = Maps.newHashMap(); -+ private final Map> d = new Int2ObjectOpenHashMap<>(); // Paper ++ private final Int2ObjectOpenHashMap> d = new Int2ObjectOpenHashMap<>(); // Paper private final ReadWriteLock e = new ReentrantReadWriteLock(); private boolean f = true; private boolean g; diff --git a/Spigot-Server-Patches/0393-Elide-lock-in-DataWatcher.patch b/Spigot-Server-Patches/0393-Elide-lock-in-DataWatcher.patch new file mode 100644 index 000000000..9e5a83228 --- /dev/null +++ b/Spigot-Server-Patches/0393-Elide-lock-in-DataWatcher.patch @@ -0,0 +1,140 @@ +From 8cb44a97224fca86ef23ca29a80cbadb9d4ea1d3 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sat, 11 May 2019 08:19:27 -0700 +Subject: [PATCH] Elide lock in DataWatcher + +The lock in DataWatcher is used to prevent concurrent modifications +to the 'd' field (entries in MCP). However any modifications to +this map only occur on initialization of an Entity in its +constructor. This modification is write-locked. + +Every other access is through a readlock, which allows +the threads to pass if there is no thread holding the +writelock. + +Since the writelock is only obtained in the constructor +of the Entity, the further readlocks are actually +useless (which get obtained on set, get, etc calls). + +diff --git a/src/main/java/net/minecraft/server/DataWatcher.java b/src/main/java/net/minecraft/server/DataWatcher.java +index f224043d8e..bbea8ef726 100644 +--- a/src/main/java/net/minecraft/server/DataWatcher.java ++++ b/src/main/java/net/minecraft/server/DataWatcher.java +@@ -23,7 +23,7 @@ public class DataWatcher { + private static final Map, Integer> b = Maps.newHashMap(); + private final Entity c; + private final Int2ObjectOpenHashMap> d = new Int2ObjectOpenHashMap<>(); // Paper +- private final ReadWriteLock e = new ReentrantReadWriteLock(); ++ //private final ReadWriteLock e = new ReentrantReadWriteLock(); // Paper - not required + private boolean f = true; + private boolean g; + +@@ -88,30 +88,14 @@ public class DataWatcher { + private void registerObject(DataWatcherObject datawatcherobject, T t0) { + DataWatcher.Item datawatcher_item = new DataWatcher.Item<>(datawatcherobject, t0); + +- this.e.writeLock().lock(); ++ //this.e.writeLock().lock(); // Paper - not required + this.d.put(datawatcherobject.a(), datawatcher_item); + this.f = false; +- this.e.writeLock().unlock(); ++ //this.e.writeLock().unlock(); // Paper - not required + } + + private DataWatcher.Item b(DataWatcherObject datawatcherobject) { +- this.e.readLock().lock(); +- +- DataWatcher.Item datawatcher_item; +- +- try { +- datawatcher_item = (DataWatcher.Item) this.d.get(datawatcherobject.a()); +- } catch (Throwable throwable) { +- CrashReport crashreport = CrashReport.a(throwable, "Getting synched entity data"); +- CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Synched entity data"); +- +- crashreportsystemdetails.a("Data ID", (Object) datawatcherobject); +- throw new ReportedException(crashreport); +- } finally { +- this.e.readLock().unlock(); +- } +- +- return datawatcher_item; ++ return (DataWatcher.Item)this.d.get(datawatcherobject.a()); // Paper - avoid lock and try catch, get() does not fail + } + + public T get(DataWatcherObject datawatcherobject) { +@@ -158,7 +142,7 @@ public class DataWatcher { + List> list = null; + + if (this.g) { +- this.e.readLock().lock(); ++ //this.e.readLock().lock(); // Paper - not required + Iterator iterator = this.d.values().iterator(); + + while (iterator.hasNext()) { +@@ -174,7 +158,7 @@ public class DataWatcher { + } + } + +- this.e.readLock().unlock(); ++ //this.e.readLock().unlock(); // Paper - not required + } + + this.g = false; +@@ -182,7 +166,7 @@ public class DataWatcher { + } + + public void a(PacketDataSerializer packetdataserializer) throws IOException { +- this.e.readLock().lock(); ++ //this.e.readLock().lock(); // Paper - not required + Iterator iterator = this.d.values().iterator(); + + while (iterator.hasNext()) { +@@ -191,7 +175,7 @@ public class DataWatcher { + a(packetdataserializer, datawatcher_item); + } + +- this.e.readLock().unlock(); ++ //this.e.readLock().unlock(); // Paper - not required + packetdataserializer.writeByte(255); + } + +@@ -199,7 +183,7 @@ public class DataWatcher { + public List> c() { + List> list = null; + +- this.e.readLock().lock(); ++ //this.e.readLock().lock(); // Paper - not required + + DataWatcher.Item datawatcher_item; + +@@ -210,7 +194,7 @@ public class DataWatcher { + } + } + +- this.e.readLock().unlock(); ++ //this.e.readLock().unlock(); // Paper - not required + return list; + } + +@@ -261,7 +245,7 @@ public class DataWatcher { + + public void e() { + this.g = false; +- this.e.readLock().lock(); ++ //this.e.readLock().lock(); // Paper - not required + Iterator iterator = this.d.values().iterator(); + + while (iterator.hasNext()) { +@@ -270,7 +254,7 @@ public class DataWatcher { + datawatcher_item.a(false); + } + +- this.e.readLock().unlock(); ++ //this.e.readLock().unlock(); // Paper - not required + } + + public static class Item { +-- +2.21.0 +