From 4802988bbb7f702b04c27a9f4fc16b120538063d Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 30 May 2025 21:18:22 -0400 Subject: [PATCH 1/6] Add sysprop to allow using async profiler for launch profiling --- .../java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java b/common/src/main/java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java index 428a6586..03af3f27 100644 --- a/common/src/main/java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java +++ b/common/src/main/java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java @@ -37,7 +37,7 @@ public class SparkLaunchProfiler { private static ExecutorService executor = Executors.newSingleThreadScheduledExecutor((new ThreadFactoryBuilder()).setNameFormat("spark-modernfix-async-worker").build()); private static final SparkPlatform platform = new SparkPlatform(new ModernFixSparkPlugin()); - private static final boolean USE_JAVA_SAMPLER_FOR_LAUNCH = true; //Boolean.getBoolean("modernfix.profileLaunchWithJavaSampler"); + private static final boolean USE_JAVA_SAMPLER_FOR_LAUNCH = !Boolean.getBoolean("modernfix.profileWithAsyncSampler"); public static void start(String key) { if (!ongoingSamplers.containsKey(key)) { From 6e591f5ace9cd67672b1ffd0e6ba0c10d92ca860 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 30 May 2025 21:23:51 -0400 Subject: [PATCH 2/6] Add more sysprops --- .../org/embeddedt/modernfix/spark/SparkLaunchProfiler.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java b/common/src/main/java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java index 03af3f27..2bff5d0c 100644 --- a/common/src/main/java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java +++ b/common/src/main/java/org/embeddedt/modernfix/spark/SparkLaunchProfiler.java @@ -38,16 +38,18 @@ public class SparkLaunchProfiler { private static final SparkPlatform platform = new SparkPlatform(new ModernFixSparkPlugin()); private static final boolean USE_JAVA_SAMPLER_FOR_LAUNCH = !Boolean.getBoolean("modernfix.profileWithAsyncSampler"); + private static final int SAMPLING_INTERVAL = Integer.getInteger("modernfix.profileSamplingIntervalMicroseconds", 4000); + private static final String THREAD_GROUPER = System.getProperty("modernfix.profileSamplingThreadGrouper", "by-pool"); public static void start(String key) { if (!ongoingSamplers.containsKey(key)) { Sampler sampler; - SamplerSettings settings = new SamplerSettings(4000, ThreadDumper.ALL, ThreadGrouper.BY_NAME, -1, false); + SamplerSettings settings = new SamplerSettings(SAMPLING_INTERVAL, ThreadDumper.ALL, ThreadGrouper.parseConfigSetting(THREAD_GROUPER), -1, false); try { if(USE_JAVA_SAMPLER_FOR_LAUNCH) { throw new UnsupportedOperationException(); } - sampler = new AsyncSampler(platform, settings, new SampleCollector.Execution(4000)); + sampler = new AsyncSampler(platform, settings, new SampleCollector.Execution(SAMPLING_INTERVAL)); } catch (UnsupportedOperationException e) { sampler = new JavaSampler(platform, settings, true, true); } From 94271b866b50df7f212ec04ce2084b15df693be6 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 30 May 2025 21:35:52 -0400 Subject: [PATCH 3/6] Add world join profiling --- .../embeddedt/modernfix/ModernFixClient.java | 4 ++++ .../MinecraftMixin.java | 18 ++++++++++++++++++ .../WorldLoaderMixin.java | 18 ++++++++++++++++++ .../core/config/ModernFixEarlyConfig.java | 1 + 4 files changed, 41 insertions(+) create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/spark_profile_world_join/MinecraftMixin.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/spark_profile_world_join/WorldLoaderMixin.java diff --git a/common/src/main/java/org/embeddedt/modernfix/ModernFixClient.java b/common/src/main/java/org/embeddedt/modernfix/ModernFixClient.java index 320e2ab9..7dbee016 100644 --- a/common/src/main/java/org/embeddedt/modernfix/ModernFixClient.java +++ b/common/src/main/java/org/embeddedt/modernfix/ModernFixClient.java @@ -16,6 +16,7 @@ import org.embeddedt.modernfix.platform.ModernFixPlatformHooks; import org.embeddedt.modernfix.searchtree.JEIBackedSearchTree; import org.embeddedt.modernfix.searchtree.REIBackedSearchTree; import org.embeddedt.modernfix.searchtree.SearchTreeProviderRegistry; +import org.embeddedt.modernfix.spark.SparkLaunchProfiler; import org.embeddedt.modernfix.util.ClassInfoManager; import org.embeddedt.modernfix.world.IntegratedWatchdog; @@ -100,6 +101,9 @@ public class ModernFixClient { ModernFix.LOGGER.warn("Time from main menu to in-game was " + timeSpentLoading + " seconds"); ModernFix.LOGGER.warn("Total time to load game and open world was " + (timeSpentLoading + gameStartTimeSeconds) + " seconds"); } + if (ModernFixPlatformHooks.INSTANCE.modPresent("spark") && ModernFixMixinPlugin.instance.isOptionEnabled("feature.spark_profile_world_join.WorldJoin")) { + SparkLaunchProfiler.stop("world_join"); + } resetWorldLoadStateMachine(); } } diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/spark_profile_world_join/MinecraftMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/spark_profile_world_join/MinecraftMixin.java new file mode 100644 index 00000000..f817ec71 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/spark_profile_world_join/MinecraftMixin.java @@ -0,0 +1,18 @@ +package org.embeddedt.modernfix.common.mixin.feature.spark_profile_world_join; + +import net.minecraft.client.Minecraft; +import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.embeddedt.modernfix.spark.SparkLaunchProfiler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Minecraft.class) +@ClientOnlyMixin +public class MinecraftMixin { + @Inject(method = "prepareForMultiplayer", at = @At("HEAD")) + private void startProfiling(CallbackInfo ci) { + SparkLaunchProfiler.start("world_join"); + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/spark_profile_world_join/WorldLoaderMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/spark_profile_world_join/WorldLoaderMixin.java new file mode 100644 index 00000000..713886b7 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/feature/spark_profile_world_join/WorldLoaderMixin.java @@ -0,0 +1,18 @@ +package org.embeddedt.modernfix.common.mixin.feature.spark_profile_world_join; + +import net.minecraft.server.WorldLoader; +import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.embeddedt.modernfix.spark.SparkLaunchProfiler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(WorldLoader.class) +@ClientOnlyMixin +public class WorldLoaderMixin { + @Inject(method = "load", at = @At("HEAD")) + private static void startProfiling(CallbackInfoReturnable cir) { + SparkLaunchProfiler.start("world_join"); + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index b488e4c0..3f40fbfd 100644 --- a/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -182,6 +182,7 @@ public class ModernFixEarlyConfig { .put("mixin.feature.snapshot_easter_egg", true) .put("mixin.feature.warn_missing_perf_mods", true) .put("mixin.feature.spark_profile_launch", false) + .put("mixin.feature.spark_profile_world_join", false) .put("mixin.feature.log_stdout_in_log_files", true) .put("mixin.devenv", isDevEnv) .put("mixin.perf.remove_spawn_chunks", isDevEnv) From 71b444288bcff33fa74bef0a182f30d1cc500f33 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 30 May 2025 22:20:51 -0400 Subject: [PATCH 4/6] Remove nbt_memory_usage This will be replaced by more targeted optimizations towards the most common allocators of duplicate keys. The current implementation forces every key to be interned which may be redundant in many cases (e.g. when constants are given to the compound tag) --- .../nbt_memory_usage/CompoundTag1Mixin.java | 20 ---------- .../nbt_memory_usage/CompoundTagMixin.java | 40 ------------------- 2 files changed, 60 deletions(-) delete mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/nbt_memory_usage/CompoundTag1Mixin.java delete mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/nbt_memory_usage/CompoundTagMixin.java diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/nbt_memory_usage/CompoundTag1Mixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/nbt_memory_usage/CompoundTag1Mixin.java deleted file mode 100644 index 8034f856..00000000 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/nbt_memory_usage/CompoundTag1Mixin.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.embeddedt.modernfix.common.mixin.perf.nbt_memory_usage; - -import net.minecraft.nbt.Tag; -import org.embeddedt.modernfix.util.CanonizingStringMap; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyVariable; - -import java.util.Map; - -@Mixin(targets = "net/minecraft/nbt/CompoundTag$1") -public class CompoundTag1Mixin { - @ModifyVariable(method = "load(Ljava/io/DataInput;ILnet/minecraft/nbt/NbtAccounter;)Lnet/minecraft/nbt/CompoundTag;", at = @At(value = "INVOKE_ASSIGN", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false)) - private Map modifyMap(Map map) { - CanonizingStringMap newMap = new CanonizingStringMap<>(); - if(map != null) - newMap.putAll(map); - return newMap; - } -} \ No newline at end of file diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/nbt_memory_usage/CompoundTagMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/nbt_memory_usage/CompoundTagMixin.java deleted file mode 100644 index 274af319..00000000 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/nbt_memory_usage/CompoundTagMixin.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.embeddedt.modernfix.common.mixin.perf.nbt_memory_usage; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.Tag; -import org.embeddedt.modernfix.util.CanonizingStringMap; -import org.spongepowered.asm.mixin.*; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyArg; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.Map; - -@Mixin(CompoundTag.class) -public class CompoundTagMixin { - @Shadow @Final - private Map tags; - - /** - * Ensure that the default backing map is a CanonizingStringMap. - */ - @ModifyArg(method = "()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/nbt/CompoundTag;(Ljava/util/Map;)V"), index = 0) - private static Map useCanonizingStringMap(Map incoming) { - CanonizingStringMap newMap = new CanonizingStringMap<>(); - if(incoming != null) - newMap.putAll(incoming); - return newMap; - } - - /** - * @author embeddedt - * @reason use more efficient method when copying canonizing string map - */ - @Inject(method = "copy()Lnet/minecraft/nbt/CompoundTag;", at = @At("HEAD"), cancellable = true) - public void copyEfficient(CallbackInfoReturnable cir) { - if(this.tags instanceof CanonizingStringMap) { - cir.setReturnValue(new CompoundTag(CanonizingStringMap.deepCopy((CanonizingStringMap)this.tags, Tag::copy))); - } - } -} From e7542afd1c90b166793c6be83ffea01b31533225 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sun, 1 Jun 2025 15:24:29 -0400 Subject: [PATCH 5/6] Disable mixin.perf.faster_ingredients with VMP installed Related: #565 --- .../embeddedt/modernfix/core/config/ModernFixEarlyConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index 3f40fbfd..228914ca 100644 --- a/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/common/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -241,6 +241,7 @@ public class ModernFixEarlyConfig { disableIfModPresent("mixin.bugfix.buffer_builder_leak", "isometric-renders", "witherstormmod"); disableIfModPresent("mixin.feature.remove_chat_signing", "nochatreports"); disableIfModPresent("mixin.perf.faster_texture_loading", "stitch", "optifine", "changed"); + disableIfModPresent("mixin.perf.faster_ingredients", "vmp"); if(isFabric) { disableIfModPresent("mixin.bugfix.packet_leak", "memoryleakfix"); } From a170f072ff50229dc1b40404e6e83061ce854f2e Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Mon, 2 Jun 2025 19:08:23 -0400 Subject: [PATCH 6/6] Invalidate memoization for non-category creative tabs when categories are rebuilt --- .../CreativeModeTabMixin.java | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/memoize_creative_tab_build/CreativeModeTabMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/memoize_creative_tab_build/CreativeModeTabMixin.java index c74bb4a2..989e8066 100644 --- a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/memoize_creative_tab_build/CreativeModeTabMixin.java +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/memoize_creative_tab_build/CreativeModeTabMixin.java @@ -2,15 +2,22 @@ package org.embeddedt.modernfix.common.mixin.perf.memoize_creative_tab_build; import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.item.CreativeModeTab; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @Mixin(CreativeModeTab.class) -public class CreativeModeTabMixin { +public abstract class CreativeModeTabMixin { + @Shadow public abstract CreativeModeTab.Type getType(); + @Unique private CreativeModeTab.ItemDisplayParameters mfix$oldParameters; + @Unique + private static boolean MFIX$REBUILT_NON_CATEGORY = false; + /** * @author embeddedt * @reason Vanilla already does similar memoization in the CreativeModeTabs class, but mods often have to bypass @@ -20,9 +27,26 @@ public class CreativeModeTabMixin { */ @WrapMethod(method = "buildContents") private synchronized void buildContentsIfChanged(CreativeModeTab.ItemDisplayParameters parameters, Operation original) { - if (mfix$oldParameters == null || mfix$oldParameters.needsUpdate(parameters.enabledFeatures(), parameters.hasPermissions(), parameters.holders())) { - original.call(parameters); + synchronized (CreativeModeTab.class) { + if (mfix$oldParameters == null || mfix$oldParameters.needsUpdate(parameters.enabledFeatures(), parameters.hasPermissions(), parameters.holders())) { + original.call(parameters); + if (this.getType() == CreativeModeTab.Type.CATEGORY) { + if (MFIX$REBUILT_NON_CATEGORY) { + // We must mark every other tab that's not a category as needing rebuild. Not doing this causes mods + // that build a search tab early on without having built the dependencies to permanently leave it + // in a broken state. + for (CreativeModeTab tab : BuiltInRegistries.CREATIVE_MODE_TAB) { + if (tab.getType() != CreativeModeTab.Type.CATEGORY) { + ((CreativeModeTabMixin)(Object)tab).mfix$oldParameters = null; + } + } + MFIX$REBUILT_NON_CATEGORY = false; + } + } else { + MFIX$REBUILT_NON_CATEGORY = true; + } + } + mfix$oldParameters = parameters; } - mfix$oldParameters = parameters; } }