Refactor blockstate caching

This commit is contained in:
embeddedt 2023-01-07 12:37:44 -05:00
parent 3dedb49a45
commit 0fb6c71734
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
6 changed files with 72 additions and 56 deletions

View File

@ -121,11 +121,6 @@ repositories {
includeGroup "curse.maven"
}
}
maven {
// location of the maven that hosts JEI files
name = "Progwml6 maven"
url = "https://dvs1.progwml6.com/files/maven/"
}
}
dependencies {
@ -150,6 +145,8 @@ dependencies {
runtimeOnly fg.deobf("mezz.jei:jei-${minecraft_version}:${jei_version}")
runtimeOnly fg.deobf("curse.maven:spark-361579:3767277")
compileOnly fg.deobf("curse.maven:refinedstorage-243076:3807951")
}
// Example for how to get properties into the manifest for reading at runtime.

View File

@ -21,14 +21,17 @@ import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent;
import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent;
import net.minecraftforge.fml.event.server.FMLServerStartedEvent;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.fml.network.FMLNetworkConstants;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.embeddedt.modernfix.core.config.ModernFixConfig;
import java.lang.management.ManagementFactory;
import java.util.stream.Collectors;
// The value here should match an entry in the META-INF/mods.toml file
@ -54,4 +57,12 @@ public class ModernFix {
ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.DISPLAYTEST, () -> Pair.of(() -> FMLNetworkConstants.IGNORESERVERONLY, (a, b) -> true));
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ModernFixConfig.COMMON_CONFIG);
}
@SubscribeEvent
public void onServerStarted(FMLServerStartedEvent event) {
if(FMLLoader.getDist() == Dist.DEDICATED_SERVER) {
float gameStartTime = ManagementFactory.getRuntimeMXBean().getUptime() / 1000f;
ModernFix.LOGGER.warn("Dedicated server took " + gameStartTime + " seconds to load");
}
}
}

View File

@ -1,23 +1,30 @@
package org.embeddedt.modernfix.blockstate;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableSet;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.Util;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.world.EmptyBlockReader;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.duck.IBlockState;
import org.embeddedt.modernfix.util.AsyncStopwatch;
import org.embeddedt.modernfix.util.BakeReason;
import org.embeddedt.modernfix.util.OrderedParallelModDispatcher;
import java.util.ArrayList;
import java.util.Map;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class BlockStateCacheHandler {
private static final Set<String> PRECACHED_COLLISION_SHAPES = ImmutableSet.<String>builder()
.add("refinedstorage")
.add("cabletiers")
.add("extrastorage")
.build();
public static void handleStateCache(BlockState state) {
if(BakeReason.currentBakeReason == BakeReason.FREEZE
|| BakeReason.currentBakeReason == BakeReason.REMOTE_SNAPSHOT_INJECT
@ -34,42 +41,20 @@ public class BlockStateCacheHandler {
handleStateCache(state);
}
public static void rebuildParallel(boolean force) {
Map<String, ArrayList<BlockState>> statesByModId = StreamSupport.stream(Block.BLOCK_STATE_REGISTRY.spliterator(), false)
.collect(Collectors.groupingBy(state -> state.getBlock().getRegistryName().getNamespace(), Collectors.toCollection(ArrayList::new)));
/* Run some special sauce for Refined Storage since it has very slow collision shapes */
Stopwatch realtimeStopwatch = Stopwatch.createStarted();
AsyncStopwatch cpuStopwatch = new AsyncStopwatch();
/* For safety, do built-in blocks first */
cpuStopwatch.startMeasuringAsync();
ArrayList<BlockState> initialStates = statesByModId.remove("minecraft");
for(BlockState state : initialStates) {
handleStateCacheParallel(state, force);
}
cpuStopwatch.stopMeasuringAsync();
OrderedParallelModDispatcher.dispatchBlocking(Util.backgroundExecutor(), modId -> {
ArrayList<BlockState> states = statesByModId.get(modId);
if(states == null)
return;
cpuStopwatch.startMeasuringAsync();
states.removeIf(state -> {
try {
handleStateCacheParallel(state, force);
return true;
} catch(RuntimeException e) {
ModernFix.LOGGER.error("Error computing state cache for " + state + ": ", e);
return false;
}
});
cpuStopwatch.stopMeasuringAsync();
});
cpuStopwatch.startMeasuringAsync();
for(ArrayList<BlockState> remainingStates : statesByModId.values()) {
for(BlockState state : remainingStates) {
handleStateCacheParallel(state, force);
}
}
cpuStopwatch.stopMeasuringAsync();
List<BlockState> specialStates = StreamSupport.stream(Block.BLOCK_STATE_REGISTRY.spliterator(), false)
.filter(state -> PRECACHED_COLLISION_SHAPES.contains(state.getBlock().getRegistryName().getNamespace())).collect(Collectors.toList());
CompletableFuture.runAsync(() -> {
specialStates.parallelStream()
.forEach(state -> {
/* Force these blocks to compute their shapes ahead of time on worker threads */
state.getBlock().getCollisionShape(state, EmptyBlockReader.INSTANCE, BlockPos.ZERO, ISelectionContext.empty());
state.getBlock().getOcclusionShape(state, EmptyBlockReader.INSTANCE, BlockPos.ZERO);
});
}, Util.backgroundExecutor()).join();
Block.BLOCK_STATE_REGISTRY.forEach(state -> handleStateCacheParallel(state, force));
realtimeStopwatch.stop();
ModernFix.LOGGER.info("CPU time spent rebuilding blockstate cache: " + cpuStopwatch.getCpuTime()/1000f + " seconds");
ModernFix.LOGGER.info("Real time spent rebuilding blockstate cache: " + realtimeStopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds");
}
}

View File

@ -0,0 +1,26 @@
package org.embeddedt.modernfix.mixin.perf.parallel_potentially_unsafe.parallel_blockstate_cache_rebuild;
import com.refinedmods.refinedstorage.block.shape.ShapeCache;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.shapes.VoxelShape;
import org.embeddedt.modernfix.ModernFix;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Mixin(ShapeCache.class)
public class ShapeCacheMixin {
@Shadow private static Map<BlockState, VoxelShape> CACHE;
@Inject(method = "<clinit>", at = @At("TAIL"))
private static void useConcurrentMap(CallbackInfo ci) {
CACHE = new ConcurrentHashMap<>();
ModernFix.LOGGER.info("Successfully replaced ShapeCache with concurrent map");
}
}

View File

@ -2,24 +2,14 @@ package org.embeddedt.modernfix.registry;
import com.google.common.base.Stopwatch;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.ModWorkManager;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
import net.minecraftforge.forgespi.language.IModInfo;
import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.util.AsyncStopwatch;
import org.embeddedt.modernfix.util.CachedSupplier;
import org.embeddedt.modernfix.util.OrderedParallelModDispatcher;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;
public class DeferredRegisterBaker {
@ -36,6 +26,7 @@ public class DeferredRegisterBaker {
public static void bakeSuppliers(ResourceLocation registry) {
synchronized (supplierMap) {
Set<String> modErrors = Collections.synchronizedSet(new HashSet<>());
HashMap<String, List<CachedSupplier<?>>> registrySupplierMap = supplierMap.get(registry);
if(registrySupplierMap == null)
return;
@ -51,12 +42,15 @@ public class DeferredRegisterBaker {
try {
supplier.compute();
} catch(RuntimeException e) {
e.printStackTrace();
ModernFix.LOGGER.debug("Exception encountered while caching supplier", e);
modErrors.add(modId);
}
}
cpuStopwatch.stopMeasuringAsync();
});
realtimeStopwatch.stop();
if(modErrors.size() > 0)
ModernFix.LOGGER.warn("The following mods had errors while caching suppliers (this is likely safe): [" + String.join(", ", modErrors) + "]");
ModernFix.LOGGER.info("CPU time spent constructing " + registry + " suppliers: " + cpuStopwatch.getCpuTime()/1000f + " seconds");
ModernFix.LOGGER.info("Real time spent constructing " + registry + " suppliers: " + realtimeStopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds");
}

View File

@ -20,7 +20,10 @@
"perf.thread_priorities.UtilMixin",
"perf.preload_block_classes.GameDataMixin",
"perf.parallel_potentially_unsafe.parallel_deferred_suppliers.DeferredRegisterMixin",
"perf.parallel_potentially_unsafe.parallel_deferred_suppliers.GameDataMixin"
"perf.parallel_potentially_unsafe.parallel_deferred_suppliers.GameDataMixin",
"perf.parallel_potentially_unsafe.parallel_blockstate_cache_rebuild.BlocksMixin",
"perf.parallel_potentially_unsafe.parallel_blockstate_cache_rebuild.BlockCallbacksMixin",
"perf.parallel_potentially_unsafe.parallel_blockstate_cache_rebuild.ShapeCacheMixin"
],
"client": [
"perf.skip_first_datapack_reload.MinecraftMixin",