From 2525a64313b0998f4dbf6858d9af98ca3010dbbd Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 6 Jan 2023 21:57:44 -0500 Subject: [PATCH] Move mod dispatching logic into its own class --- .../registry/DeferredRegisterBaker.java | 67 +++-------------- .../util/OrderedParallelModDispatcher.java | 75 +++++++++++++++++++ 2 files changed, 87 insertions(+), 55 deletions(-) create mode 100644 src/main/java/org/embeddedt/modernfix/util/OrderedParallelModDispatcher.java diff --git a/src/main/java/org/embeddedt/modernfix/registry/DeferredRegisterBaker.java b/src/main/java/org/embeddedt/modernfix/registry/DeferredRegisterBaker.java index ec07e74d..78f01bc6 100644 --- a/src/main/java/org/embeddedt/modernfix/registry/DeferredRegisterBaker.java +++ b/src/main/java/org/embeddedt/modernfix/registry/DeferredRegisterBaker.java @@ -11,6 +11,7 @@ import net.minecraftforge.fml.loading.moddiscovery.ModInfo; import net.minecraftforge.forgespi.language.IModInfo; import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.util.CachedSupplier; +import org.embeddedt.modernfix.util.OrderedParallelModDispatcher; import java.util.*; import java.util.concurrent.CompletableFuture; @@ -37,64 +38,20 @@ public class DeferredRegisterBaker { HashMap>> registrySupplierMap = supplierMap.get(registry); if(registrySupplierMap == null) return; - HashSet finishedMods = new HashSet<>(); - finishedMods.add("minecraft"); - finishedMods.add("forge"); - HashMap> submittedFutures = new HashMap<>(); - int numMods = ModList.get().getMods().size(); - Semaphore jobWaitingSemaphore = new Semaphore(0); - ArrayList remainingModList = new ArrayList<>(ModList.get().getMods()); Stopwatch realtimeStopwatch = Stopwatch.createStarted(); AtomicLong cpuLong = new AtomicLong(0); - while(finishedMods.size() < numMods) { - remainingModList.removeIf(modInfo -> { - if(finishedMods.contains(modInfo.getModId())) - return true; - boolean allDependenciesLoaded = true; - for(IModInfo.ModVersion dep : modInfo.getDependencies()) { - if(dep.isMandatory() && !finishedMods.contains(dep.getModId())) { - allDependenciesLoaded = false; - break; - } - } - if(!allDependenciesLoaded) - return false; - /* Submit job */ - List> suppliersToCompute = registrySupplierMap.get(modInfo.getModId()); - if (suppliersToCompute == null || suppliersToCompute.size() == 0) { - finishedMods.add(modInfo.getModId()); - return true; - } - Optional modContainerOpt = ModList.get().getModContainerById(modInfo.getModId()); - if(!modContainerOpt.isPresent()) - throw new IllegalStateException("Can't find mod container"); - ModContainer container = modContainerOpt.get(); - submittedFutures.put(modInfo.getModId(), CompletableFuture.runAsync(() -> { - Supplier contextExtension = ObfuscationReflectionHelper.getPrivateValue(ModContainer.class, container, "contextExtension"); - ModLoadingContext.get().setActiveContainer(container, contextExtension.get()); - Stopwatch stopwatch = Stopwatch.createStarted(); - for (CachedSupplier supplier : suppliersToCompute) { - supplier.compute(); - } - stopwatch.stop(); - cpuLong.addAndGet(stopwatch.elapsed(TimeUnit.MILLISECONDS)); - jobWaitingSemaphore.release(); - }, ModWorkManager.parallelExecutor())); - return true; - }); - try { - jobWaitingSemaphore.acquire(); - } catch(InterruptedException e) { - throw new RuntimeException("Unexpected interruption", e); + OrderedParallelModDispatcher.dispatchBlocking(modId -> { + List> suppliersToCompute = registrySupplierMap.get(modId); + if (suppliersToCompute == null || suppliersToCompute.size() == 0) { + return; } - submittedFutures.entrySet().removeIf(entry -> { - if(entry.getValue().isDone()) { - finishedMods.add(entry.getKey()); - return true; - } - return false; - }); - } + Stopwatch stopwatch = Stopwatch.createStarted(); + for (CachedSupplier supplier : suppliersToCompute) { + supplier.compute(); + } + stopwatch.stop(); + cpuLong.addAndGet(stopwatch.elapsed(TimeUnit.MILLISECONDS)); + }); realtimeStopwatch.stop(); ModernFix.LOGGER.info("CPU time spent constructing " + registry + " suppliers: " + cpuLong.get()/1000f + " seconds"); ModernFix.LOGGER.info("Real time spent constructing " + registry + " suppliers: " + realtimeStopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds"); diff --git a/src/main/java/org/embeddedt/modernfix/util/OrderedParallelModDispatcher.java b/src/main/java/org/embeddedt/modernfix/util/OrderedParallelModDispatcher.java new file mode 100644 index 00000000..55f18685 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/util/OrderedParallelModDispatcher.java @@ -0,0 +1,75 @@ +package org.embeddedt.modernfix.util; + +import com.google.common.base.Stopwatch; +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 java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Iterates over all mods in the game, parallelizing where possible while preserving dependency ordering. + * + * Can also be given a list of mods to skip. + */ +public class OrderedParallelModDispatcher { + public static void dispatchBlocking(Consumer task, Collection modIDsToFilter) { + HashSet finishedMods = new HashSet<>(modIDsToFilter); + HashMap> submittedFutures = new HashMap<>(); + int numMods = ModList.get().getMods().size(); + Semaphore jobWaitingSemaphore = new Semaphore(0); + ArrayList remainingModList = new ArrayList<>(ModList.get().getMods()); + while(finishedMods.size() < numMods) { + remainingModList.removeIf(modInfo -> { + if(finishedMods.contains(modInfo.getModId())) + return true; + boolean allDependenciesLoaded = true; + for(IModInfo.ModVersion dep : modInfo.getDependencies()) { + if(dep.isMandatory() && !finishedMods.contains(dep.getModId())) { + allDependenciesLoaded = false; + break; + } + } + if(!allDependenciesLoaded) + return false; + Optional modContainerOpt = ModList.get().getModContainerById(modInfo.getModId()); + if(!modContainerOpt.isPresent()) + throw new IllegalStateException("Can't find mod container"); + ModContainer container = modContainerOpt.get(); + submittedFutures.put(modInfo.getModId(), CompletableFuture.runAsync(() -> { + Supplier contextExtension = ObfuscationReflectionHelper.getPrivateValue(ModContainer.class, container, "contextExtension"); + ModLoadingContext.get().setActiveContainer(container, contextExtension.get()); + task.accept(modInfo.getModId()); + jobWaitingSemaphore.release(); + }, ModWorkManager.parallelExecutor())); + return true; + }); + try { + jobWaitingSemaphore.acquire(); + } catch(InterruptedException e) { + throw new RuntimeException("Unexpected interruption", e); + } + submittedFutures.entrySet().removeIf(entry -> { + if(entry.getValue().isDone()) { + finishedMods.add(entry.getKey()); + return true; + } + return false; + }); + } + } + + public static void dispatchBlocking(Consumer task) { + dispatchBlocking(task, Collections.emptyList()); + } +}