From 0391e7befff48855cf1fbf690f4d5376f03bc53c Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Wed, 14 Aug 2024 14:44:16 -0700 Subject: [PATCH] Use custom random in block random ticking The CAS performed by the regular random appears expensive. These changes drop my server tick time locally @ tickSpeed = 10,000 from 53ms to 37ms. --- .../moonrise/common/util/SimpleRandom.java | 53 +++++++++++++++++++ .../random_ticking/ServerLevelMixin.java | 27 ++++++++-- 2 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 src/main/java/ca/spottedleaf/moonrise/common/util/SimpleRandom.java diff --git a/src/main/java/ca/spottedleaf/moonrise/common/util/SimpleRandom.java b/src/main/java/ca/spottedleaf/moonrise/common/util/SimpleRandom.java new file mode 100644 index 0000000..3961d9c --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/common/util/SimpleRandom.java @@ -0,0 +1,53 @@ +package ca.spottedleaf.moonrise.common.util; + +import net.minecraft.world.level.levelgen.LegacyRandomSource; + +/** + * Avoid costly CAS of superclass + */ +public final class SimpleRandom extends LegacyRandomSource { + + private static final long MULTIPLIER = 25214903917L; + private static final long ADDEND = 11L; + private static final int BITS = 48; + private static final long MASK = (1L << BITS) - 1; + + private long value; + + public SimpleRandom(final long seed) { + super(0L); + this.value = seed; + } + + @Override + public void setSeed(final long seed) { + this.value = (seed ^ MULTIPLIER) & MASK; + } + + private long advanceSeed() { + return this.value = ((this.value * MULTIPLIER) + ADDEND) & MASK; + } + + @Override + public int next(final int bits) { + return (int)(this.advanceSeed() >>> (BITS - bits)); + } + + @Override + public int nextInt() { + final long seed = this.advanceSeed(); + return (int)(seed >>> (BITS - Integer.SIZE)); + } + + + @Override + public int nextInt(final int bound) { + if (bound <= 0) { + throw new IllegalArgumentException(); + } + + // https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ + final long value = this.advanceSeed() >>> (BITS - Integer.SIZE); + return (int)((value * bound) >>> Integer.SIZE); + } +} diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/random_ticking/ServerLevelMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/random_ticking/ServerLevelMixin.java index 2c3bd83..4d7cd7e 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/random_ticking/ServerLevelMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/random_ticking/ServerLevelMixin.java @@ -2,6 +2,7 @@ package ca.spottedleaf.moonrise.mixin.random_ticking; import ca.spottedleaf.moonrise.common.list.IntList; import ca.spottedleaf.moonrise.common.util.MoonriseCommon; +import ca.spottedleaf.moonrise.common.util.SimpleRandom; import ca.spottedleaf.moonrise.common.util.WorldUtil; import ca.spottedleaf.moonrise.patches.block_counting.BlockCountingChunkSection; import com.llamalad7.mixinextras.sugar.Local; @@ -38,6 +39,24 @@ abstract class ServerLevelMixin extends Level implements WorldGenLevel { @Unique private static final LevelChunkSection[] EMPTY_SECTION_ARRAY = new LevelChunkSection[0]; + @Unique + private final SimpleRandom simpleRandom = new SimpleRandom(0L); + + /** + * @reason Use faster random + * @author Spottedleaf + */ + @Redirect( + method = "tickChunk", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/util/RandomSource;nextInt(I)I" + ) + ) + private int nextInt(final RandomSource instance, final int bound) { + return this.simpleRandom.nextInt(bound); + } + /** * @reason Optimise random ticking so that it will not retrieve BlockStates unnecessarily, as well as * optionally avoiding double ticking fluid blocks. @@ -55,7 +74,7 @@ abstract class ServerLevelMixin extends Level implements WorldGenLevel { @Local(ordinal = 0, argsOnly = true) final int tickSpeed) { final LevelChunkSection[] sections = chunk.getSections(); final int minSection = WorldUtil.getMinSection((ServerLevel)(Object)this); - final RandomSource random = this.random; + final SimpleRandom simpleRandom = this.simpleRandom; final boolean tickFluids = !MoonriseCommon.getConfig().bugFixes.fixMC224294; final ChunkPos cpos = chunk.getPos(); @@ -74,7 +93,7 @@ abstract class ServerLevelMixin extends Level implements WorldGenLevel { for (int i = 0; i < tickSpeed; ++i) { final int tickingBlocks = tickList.size(); - final int index = random.nextInt() & ((16 * 16 * 16) - 1); + final int index = simpleRandom.nextInt() & ((16 * 16 * 16) - 1); if (index >= tickingBlocks) { // most of the time we fall here @@ -87,11 +106,11 @@ abstract class ServerLevelMixin extends Level implements WorldGenLevel { // do not use a mutable pos, as some random tick implementations store the input without calling immutable()! final BlockPos pos = new BlockPos((location & 15) | offsetX, ((location >>> (4 + 4)) & 15) | offsetY, ((location >>> 4) & 15) | offsetZ); - state.randomTick((ServerLevel)(Object)this, pos, random); + state.randomTick((ServerLevel)(Object)this, pos, simpleRandom); if (tickFluids) { final FluidState fluidState = state.getFluidState(); if (fluidState.isRandomlyTicking()) { - fluidState.randomTick((ServerLevel)(Object)this, pos, random); + fluidState.randomTick((ServerLevel)(Object)this, pos, simpleRandom); } } }