From 65bc41281fe7a4009511543d5dfdb56d6fd1d1c1 Mon Sep 17 00:00:00 2001 From: Marc Hermans Date: Thu, 25 Feb 2021 03:36:51 +0100 Subject: [PATCH] Add support for caching of configuration values. (#7608) --- .../common/ForgeConfigSpec.java | 36 ++++- .../fml/config/ConfigFileTypeHandler.java | 1 + .../common/ForgeConfigSpecTest.java | 140 ++++++++++++++++++ 3 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 src/test/java/net/minecraftforge/common/ForgeConfigSpecTest.java diff --git a/src/main/java/net/minecraftforge/common/ForgeConfigSpec.java b/src/main/java/net/minecraftforge/common/ForgeConfigSpec.java index fa5d4cb3af..f605140260 100644 --- a/src/main/java/net/minecraftforge/common/ForgeConfigSpec.java +++ b/src/main/java/net/minecraftforge/common/ForgeConfigSpec.java @@ -42,6 +42,7 @@ import java.util.stream.Collectors; import javax.annotation.Nonnull; +import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; @@ -90,6 +91,7 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper configValues) { + configValues.forEach(value -> { + if (value instanceof ConfigValue) { + final ConfigValue configValue = (ConfigValue) value; + configValue.clearCache(); + } else if (value instanceof Config) { + final Config innerConfig = (Config) value; + this.resetCaches(innerConfig.valueMap().values()); + } + }); + } + public void save() { Preconditions.checkNotNull(childConfig, "Cannot save config value without assigned Config object present"); @@ -690,10 +708,15 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper { + @VisibleForTesting + static boolean USE_CACHES = true; + private final Builder parent; private final List path; private final Supplier defaultSupplier; + private T cachedValue = null; + private ForgeConfigSpec spec; ConfigValue(Builder parent, List path, Supplier defaultSupplier) @@ -714,7 +737,13 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper path, Supplier defaultSupplier) @@ -739,6 +768,11 @@ public class ForgeConfigSpec extends UnmodifiableConfigWrapper void executeSpeedTest(final String configKey, final T defaultKeyValue, final String testName) throws IOException + { + final ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder(); + final ForgeConfigSpec.ConfigValue simpleValue = builder.define(configKey, defaultKeyValue); + final ForgeConfigSpec spec = builder.build(); + + final String configPath = String.format(TEST_CONFIG_PATH_TEMPLATE, testName); + final File configFile = new File(configPath); + configFile.getParentFile().mkdirs(); + configFile.createNewFile(); + final CommentedFileConfig configData = CommentedFileConfig.builder(configPath) + .sync() + .preserveInsertionOrder() + .onFileNotFound((newfile, configFormat) -> { + Files.createFile(newfile); + return true; + }) + .writingMode(WritingMode.REPLACE) + .build(); + spec.setConfig(configData); + + final List results = runTestHarness(defaultKeyValue, testName, simpleValue, spec, 3, 20); + final double averageEnabled = results.stream().mapToLong(value -> value.enabledWatch.elapsed(TimeUnit.NANOSECONDS)).average().getAsDouble(); + final double averageDisabled = results.stream().mapToLong(value -> value.disabledWatch.elapsed(TimeUnit.NANOSECONDS)).average().getAsDouble(); + final double maxEnabledDeviation = results.stream().mapToLong(value -> value.enabledWatch.elapsed(TimeUnit.NANOSECONDS)) + .mapToDouble(e -> e - averageEnabled) + .map(Math::abs) + .max() + .getAsDouble(); + final double maxDisabledDeviation = results.stream().mapToLong(value -> value.disabledWatch.elapsed(TimeUnit.NANOSECONDS)) + .mapToDouble(e -> e - averageDisabled) + .map(Math::abs) + .max() + .getAsDouble(); + + System.out.printf("Computed test results for: %s:%n", testName); + System.out.printf(" > Enabled: ~%10.2f ns. +- %10.2f ns.%n", averageEnabled, maxEnabledDeviation); + System.out.printf(" > Disabled: ~%10.2f ns. +- %10.2f ns.%n", averageDisabled, maxDisabledDeviation); + + configFile.delete(); + } + + private List runTestHarness(final T defaultKeyValue, final String testName, final ForgeConfigSpec.ConfigValue simpleValue, final ForgeConfigSpec spec, final int warmupRounds, final int testRounds) + { + final List results = new ArrayList<>(); + + for (int i = 0; i < warmupRounds; i++) + { + //Run a bunch of warm up rounds and ignore the results. + runTestOnce(defaultKeyValue, testName, simpleValue, spec); + } + + for (int i = 0; i < testRounds; i++) + { + results.add(runTestOnce(defaultKeyValue, testName, simpleValue, spec)); + } + + return results; + } + + private TestResult runTestOnce(final T defaultKeyValue, final String testName, final ForgeConfigSpec.ConfigValue simpleValue, final ForgeConfigSpec spec) + { + spec.afterReload(); + ForgeConfigSpec.ConfigValue.USE_CACHES = false; + final Stopwatch watchDisabled = Stopwatch.createStarted(); + for (int i = 0; i < TEST_SIZE; i++) + { + Assert.assertEquals(defaultKeyValue, simpleValue.get()); + } + watchDisabled.stop(); + + spec.afterReload(); + ForgeConfigSpec.ConfigValue.USE_CACHES = true; + final Stopwatch watchEnabled = Stopwatch.createStarted(); + for (int i = 0; i < TEST_SIZE; i++) + { + Assert.assertEquals(defaultKeyValue, simpleValue.get()); + } + watchEnabled.stop(); + + final TestResult result = new TestResult(watchEnabled, watchDisabled); + + System.out.printf("Test result for: %s (Disabled: %s vs. Enabled: %s)%n", testName, watchDisabled.toString(), watchEnabled.toString()); + return result; + } + + private final static class TestResult { + + private final Stopwatch enabledWatch; + private final Stopwatch disabledWatch; + + public TestResult(final Stopwatch enabledWatch, final Stopwatch disabledWatch) + { + this.enabledWatch = enabledWatch; + this.disabledWatch = disabledWatch; + } + } +}