From 070b7b6d126af5d04cb6a4315d823a2ad6dbc6bb Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Wed, 20 Mar 2024 15:44:28 -0400 Subject: [PATCH] Add some simple patches to cut down allocation rate when ticking chunks --- .../perf/ticking_chunk_alloc/BatMixin.java | 28 ++++++++++ .../ChunkGeneratorMixin.java | 26 ++++++++++ .../ticking_chunk_alloc/ChunkHolderMixin.java | 40 ++++++++++++++ .../embeddedt/modernfix/util/EitherUtil.java | 52 +++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/ticking_chunk_alloc/BatMixin.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/ticking_chunk_alloc/ChunkGeneratorMixin.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/ticking_chunk_alloc/ChunkHolderMixin.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/util/EitherUtil.java diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/ticking_chunk_alloc/BatMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/ticking_chunk_alloc/BatMixin.java new file mode 100644 index 00000000..e3cc0ddc --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/ticking_chunk_alloc/BatMixin.java @@ -0,0 +1,28 @@ +package org.embeddedt.modernfix.common.mixin.perf.ticking_chunk_alloc; + +import net.minecraft.world.entity.ambient.Bat; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.time.LocalDate; + +@Mixin(value = Bat.class, priority = 1200) +public class BatMixin { + private static long mfix$lastQueriedTime = -1L; + private static LocalDate mfix$lastQueriedDate = null; + + /** + * @author embeddedt + * @reason avoid excessive allocations from continuously querying the date, only get a new date once every 30 seconds + */ + @Redirect(method = "isHalloween", at = @At(value = "INVOKE", target = "Ljava/time/LocalDate;now()Ljava/time/LocalDate;"), require = 0) + private static LocalDate useCachedLocalDate() { + LocalDate date = mfix$lastQueriedDate; + if(date == null || Math.abs(System.currentTimeMillis() - mfix$lastQueriedTime) > 30000) { + mfix$lastQueriedDate = date = LocalDate.now(); + mfix$lastQueriedTime = System.currentTimeMillis(); + } + return date; + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/ticking_chunk_alloc/ChunkGeneratorMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/ticking_chunk_alloc/ChunkGeneratorMixin.java new file mode 100644 index 00000000..193995f4 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/ticking_chunk_alloc/ChunkGeneratorMixin.java @@ -0,0 +1,26 @@ +package org.embeddedt.modernfix.common.mixin.perf.ticking_chunk_alloc; + +import net.minecraft.world.level.chunk.ChunkGenerator; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +@Mixin(ChunkGenerator.class) +public class ChunkGeneratorMixin { + /** + * @author embeddedt + * @reason Avoid allocation if the chunk contains no structures + */ + @Redirect(method = "getMobsAt", at = @At(value = "INVOKE", target = "Ljava/util/Map;entrySet()Ljava/util/Set;"), require = 0) + private Set avoidSetAllocation(Map instance) { + if(instance.isEmpty()) { + return Collections.emptySet(); + } else { + return instance.entrySet(); + } + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/ticking_chunk_alloc/ChunkHolderMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/ticking_chunk_alloc/ChunkHolderMixin.java new file mode 100644 index 00000000..2ea7c2de --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/ticking_chunk_alloc/ChunkHolderMixin.java @@ -0,0 +1,40 @@ +package org.embeddedt.modernfix.common.mixin.perf.ticking_chunk_alloc; + +import com.mojang.datafixers.util.Either; +import net.minecraft.server.level.ChunkHolder; +import net.minecraft.world.level.chunk.LevelChunk; +import org.embeddedt.modernfix.util.EitherUtil; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.concurrent.CompletableFuture; + +@Mixin(value = ChunkHolder.class, priority = 500) +public abstract class ChunkHolderMixin { + @Shadow public abstract CompletableFuture> getTickingChunkFuture(); + + @Shadow public abstract CompletableFuture> getFullChunkFuture(); + + /** + * @author embeddedt + * @reason avoid Optional allocation + */ + @Overwrite + public LevelChunk getTickingChunk() { + CompletableFuture> completableFuture = this.getTickingChunkFuture(); + Either either = completableFuture.getNow(null); + return either == null ? null : EitherUtil.leftOrNull(either); + } + + /** + * @author embeddedt + * @reason avoid Optional allocation + */ + @Overwrite + public LevelChunk getFullChunk() { + CompletableFuture> completableFuture = this.getFullChunkFuture(); + Either either = completableFuture.getNow(null); + return either == null ? null : EitherUtil.leftOrNull(either); + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/util/EitherUtil.java b/common/src/main/java/org/embeddedt/modernfix/util/EitherUtil.java new file mode 100644 index 00000000..52bc52c2 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/util/EitherUtil.java @@ -0,0 +1,52 @@ +package org.embeddedt.modernfix.util; + +import com.mojang.datafixers.util.Either; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.Field; + +public class EitherUtil { + private static final Class LEFT, RIGHT; + private static final MethodHandle LEFT_VAL, RIGHT_VAL; + + static { + try { + LEFT = Class.forName("com.mojang.datafixers.util.Either$Left"); + RIGHT = Class.forName("com.mojang.datafixers.util.Either$Right"); + Field lvalue = LEFT.getDeclaredField("value"); + lvalue.setAccessible(true); + Field rvalue = RIGHT.getDeclaredField("value"); + rvalue.setAccessible(true); + LEFT_VAL = MethodHandles.publicLookup().unreflectGetter(lvalue).asType(MethodType.methodType(Object.class, Either.class)); + RIGHT_VAL = MethodHandles.publicLookup().unreflectGetter(rvalue).asType(MethodType.methodType(Object.class, Either.class)); + } catch(ReflectiveOperationException e) { + throw new AssertionError("Failed to hook DFU Either", e); + } + } + + @SuppressWarnings("unchecked") + public static L leftOrNull(Either either) { + if(either.getClass() == LEFT) { + try { + return (L)LEFT_VAL.invokeExact(either); + } catch(Throwable e) { + throw new RuntimeException(e); + } + } + return null; + } + + @SuppressWarnings("unchecked") + public static R rightOrNull(Either either) { + if(either.getClass() == RIGHT) { + try { + return (R)RIGHT_VAL.invokeExact(either); + } catch(Throwable e) { + throw new RuntimeException(e); + } + } + return null; + } +}