From c009ef05052e85f562ebed23081647ade23fbe04 Mon Sep 17 00:00:00 2001 From: themode Date: Wed, 4 May 2022 18:24:15 +0200 Subject: [PATCH] Avoid slow synchronized path during reference building --- .../server/snapshot/SnapshotUpdaterImpl.java | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/minestom/server/snapshot/SnapshotUpdaterImpl.java b/src/main/java/net/minestom/server/snapshot/SnapshotUpdaterImpl.java index ed3758841..63b4a412c 100644 --- a/src/main/java/net/minestom/server/snapshot/SnapshotUpdaterImpl.java +++ b/src/main/java/net/minestom/server/snapshot/SnapshotUpdaterImpl.java @@ -8,7 +8,8 @@ import java.util.*; import java.util.concurrent.atomic.AtomicReference; final class SnapshotUpdaterImpl implements SnapshotUpdater { - private final Map> referenceMap = new IdentityHashMap<>(); + private final IdentityHashMap> referenceMap = new IdentityHashMap<>(); + private IdentityHashMap> readOnlyReferenceMap; private List queue = new ArrayList<>(); static @NotNull T update(@NotNull Snapshotable snapshotable) { @@ -19,12 +20,21 @@ final class SnapshotUpdaterImpl implements SnapshotUpdater { } @Override - public synchronized @NotNull AtomicReference reference(@NotNull Snapshotable snapshotable) { - AtomicReference ref = new AtomicReference<>(); - var prev = referenceMap.putIfAbsent(snapshotable, ref); - if (prev != null) return (AtomicReference) prev; - this.queue.add(new Entry(snapshotable, ref)); - return (AtomicReference) ref; + public @NotNull AtomicReference reference(@NotNull Snapshotable snapshotable) { + AtomicReference ref; + // Very often the same snapshotable is referenced multiple times. + var readOnly = this.readOnlyReferenceMap; + if (readOnly != null && (ref = readOnly.get(snapshotable)) != null) { + return (AtomicReference) ref; + } + // If this is a new snapshotable, we need to create a new reference. + synchronized (this) { + ref = new AtomicReference<>(); + var prev = referenceMap.putIfAbsent(snapshotable, ref); + if (prev != null) return (AtomicReference) prev; + this.queue.add(new Entry(snapshotable, ref)); + return (AtomicReference) ref; + } } record Entry(Snapshotable snapshotable, AtomicReference ref) { @@ -34,6 +44,7 @@ final class SnapshotUpdaterImpl implements SnapshotUpdater { List temp; while (!(temp = new ArrayList<>(queue)).isEmpty()) { queue = new ArrayList<>(); + readOnlyReferenceMap = (IdentityHashMap>) referenceMap.clone(); temp.parallelStream().forEach(entry -> { Snapshotable snap = entry.snapshotable; entry.ref.set(Objects.requireNonNull(snap.updateSnapshot(this), "Snapshot must not be null after an update!"));