From 98895ffbb5e06b345d69fa969fc116c625b70217 Mon Sep 17 00:00:00 2001 From: Ben Manes Date: Wed, 24 Jul 2024 23:48:35 -0700 Subject: [PATCH] add a clairvoyant admission policy to the simulator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Like Bélády's optimal policy for eviction, this estimates the upper bound for an admission policy by using future knowledge to make the best decision. The candidate entry is only admitted if its next access time is less than the victim's. While the optimal replacement policy is able to dictate the working set, the optimal admission policy is only able to advise whether to keep the given candidate or victim entry. Thus, the eviction policy's choices may result in a sequence where a more naive admission policy outperforms the clairvoyant one. The intent is to show the upper bounds of an adaptive admission policy based on its best guess rather than the upper bound of the overall cache hit rate. --- gradle/libs.versions.toml | 18 +-- .../src/main/kotlin/ProjectExtensions.kt | 15 ++- .../kotlin/analyze/jmh.caffeine.gradle.kts | 9 +- .../lifecycle/testing.caffeine.gradle.kts | 5 +- .../quality/errorprone.caffeine.gradle.kts | 1 + simulator/build.gradle.kts | 2 +- .../cache/simulator/admission/Admission.java | 25 ++-- .../cache/simulator/admission/TinyLfu.java | 51 ++++---- .../admission/clairvoyant/Clairvoyant.java | 112 ++++++++++++++++++ .../policy/sketch/WindowTinyLfuPolicy.java | 4 +- .../HillClimberWindowTinyLfuPolicy.java | 4 +- .../feedback/FeedbackTinyLfuPolicy.java | 7 +- .../feedback/FeedbackWindowTinyLfuPolicy.java | 7 +- .../FullySegmentedWindowTinyLfuPolicy.java | 4 +- .../segment/LruWindowTinyLfuPolicy.java | 4 +- .../segment/RandomWindowTinyLfuPolicy.java | 4 +- .../sketch/segment/S4WindowTinyLfuPolicy.java | 4 +- simulator/src/main/resources/reference.conf | 3 + 18 files changed, 199 insertions(+), 80 deletions(-) create mode 100644 simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/clairvoyant/Clairvoyant.java diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6dcf131488..3467b1b2c7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ asm = "9.7" auto-value = "1.11.0" awaitility = "4.2.1" -bcel = "6.9.0" +bcel = "6.10.0" bnd = "7.0.0" bouncycastle-jdk18on = "1.78.1" cache2k = "2.6.1.Final" @@ -13,18 +13,18 @@ coherence = "22.06.2" commons-collections4 = "4.4" commons-compress = "1.26.2" commons-io = "2.16.1" -commons-lang3 = "3.14.0" +commons-lang3 = "3.15.0" commons-math3 = "3.6.1" commons-text = "1.12.0" concurrentlinkedhashmap = "1.4.2" config = "1.4.3" coveralls = "2.12.2" -dependency-check = "10.0.2" +dependency-check = "10.0.3" eclipse-collections = "12.0.0.M3" ehcache3 = "3.10.8" -errorprone = "2.28.0" +errorprone = "2.29.2" errorprone-plugin = "4.0.1" -errorprone-support = "0.16.1" +errorprone-support = "0.17.0" expiring-map = "0.5.11" fast-filter = "1.0.2" fastutil = "8.5.13" @@ -36,8 +36,8 @@ forbidden-apis = "3.7" google-java-format = "1.22.0" guava = "33.2.1-jre" guice = "6.0.0" -h2 = "2.2.224" -hamcrest = "2.2" +h2 = "2.3.230" +hamcrest = "3.0-rc1" hazelcast = "5.3.7" httpclient = "4.5.14" idea = "1.1.8" @@ -63,7 +63,7 @@ jsoup = "1.18.1" junit-testng = "1.0.5" junit4 = "4.13.2" junit5 = "5.11.0-M2" -jvm-dependency-conflict-resolution = "2.1.1" +jvm-dependency-conflict-resolution = "2.1.2" kotlin = "2.0.0" lincheck = "2.32" mockito = "5.12.0" @@ -96,7 +96,7 @@ versions = "0.51.0" xz = "1.9" ycsb = "0.17.0" zero-allocation-hashing = "0.26ea0" -zstd = "1.5.6-3" +zstd = "1.5.6-4" [libraries] asm-bom = { module = "org.ow2.asm:asm-bom", version.ref = "asm" } diff --git a/gradle/plugins/src/main/kotlin/ProjectExtensions.kt b/gradle/plugins/src/main/kotlin/ProjectExtensions.kt index 103216de70..09f88e1b92 100644 --- a/gradle/plugins/src/main/kotlin/ProjectExtensions.kt +++ b/gradle/plugins/src/main/kotlin/ProjectExtensions.kt @@ -9,11 +9,16 @@ fun Project.version(major: Int, minor: Int, patch: Int, releaseBuild: Boolean) { version = "$major.$minor.$patch" + if (releaseBuild) "" else "-SNAPSHOT" } -fun Project.javaExecJvmArgs(): List { - val arguments = findProperty("jvmArgs") as String? +fun Project.defaultJvmArgs(): List { val jvmArgs = mutableListOf("-Xmx4g") + if (System.getenv("GRAALVM") == "true") { + jvmArgs += listOf( + "-XX:+UnlockExperimentalVMOptions", "-Dgraal.ShowConfiguration=info", + "-XX:+EnableJVMCI", "-XX:+UseJVMCICompiler", "-XX:+EagerJVMCI") + } + val arguments = findProperty("jvmArgs") as String? if (arguments != null) { - jvmArgs.addAll(arguments.split(",")) + jvmArgs += arguments.split(",") } return jvmArgs } @@ -30,13 +35,13 @@ fun isCI(): Boolean { val DisableStrongEncapsulationJvmArgs = listOf( "--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", "--add-exports", "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", "--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", "--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", - "--add-exports", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", - "--add-exports", "jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", "--add-exports", "jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", + "--add-exports", "jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", "--add-opens", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", "--add-opens", "jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", "--add-opens", "jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED") diff --git a/gradle/plugins/src/main/kotlin/analyze/jmh.caffeine.gradle.kts b/gradle/plugins/src/main/kotlin/analyze/jmh.caffeine.gradle.kts index 546fa54299..a757fa643d 100644 --- a/gradle/plugins/src/main/kotlin/analyze/jmh.caffeine.gradle.kts +++ b/gradle/plugins/src/main/kotlin/analyze/jmh.caffeine.gradle.kts @@ -30,6 +30,7 @@ jmh { iterations = 3 timeUnit = "s" + jvmArgs = defaultJvmArgs() failOnError = true forceGC = true fork = 1 @@ -37,14 +38,6 @@ jmh { resultsFile = layout.buildDirectory.file("reports/jmh/results.json") resultFormat = "json" - val jvmArguments = mutableListOf("-Xmx2G") - if (System.getenv("GRAALVM") == "true") { - jvmArguments += listOf( - "-XX:+UnlockExperimentalVMOptions", "-Dgraal.ShowConfiguration=info", - "-XX:+EnableJVMCI", "-XX:+UseJVMCICompiler", "-XX:+EagerJVMCI") - } - jvmArgs = jvmArguments - val includePattern: String? by project if (includePattern != null) { includes = listOf(includePattern) diff --git a/gradle/plugins/src/main/kotlin/lifecycle/testing.caffeine.gradle.kts b/gradle/plugins/src/main/kotlin/lifecycle/testing.caffeine.gradle.kts index 3b99a858d4..23f26299c2 100644 --- a/gradle/plugins/src/main/kotlin/lifecycle/testing.caffeine.gradle.kts +++ b/gradle/plugins/src/main/kotlin/lifecycle/testing.caffeine.gradle.kts @@ -32,10 +32,7 @@ tasks.withType().configureEach { if ("debug" in systemProperties) { jvmArgs("-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005") } - if (environment["GRAALVM"] == "true") { - jvmArgs("-XX:+UnlockExperimentalVMOptions", "-Dgraal.ShowConfiguration=info", - "-XX:+EnableJVMCI", "-XX:+UseJVMCICompiler", "-XX:+EagerJVMCI") - } + jvmArgs(defaultJvmArgs()) if (isCI()) { reports.junitXml.includeSystemOutLog = false reports.junitXml.includeSystemErrLog = false diff --git a/gradle/plugins/src/main/kotlin/quality/errorprone.caffeine.gradle.kts b/gradle/plugins/src/main/kotlin/quality/errorprone.caffeine.gradle.kts index 4470462fc6..c10088283b 100644 --- a/gradle/plugins/src/main/kotlin/quality/errorprone.caffeine.gradle.kts +++ b/gradle/plugins/src/main/kotlin/quality/errorprone.caffeine.gradle.kts @@ -111,6 +111,7 @@ fun enabledChecks() = listOf( "MissingDefault", "MixedArrayDimensions", "MissingDefault", + "MissingRuntimeRetention", "MockitoDoSetup", "MutableGuiceModule", "NoAllocation", diff --git a/simulator/build.gradle.kts b/simulator/build.gradle.kts index 7d4ef015e7..f23c4719ce 100644 --- a/simulator/build.gradle.kts +++ b/simulator/build.gradle.kts @@ -84,7 +84,7 @@ tasks.withType().configureEach { classpath(sourceSets["main"].runtimeClasspath) systemProperties(caffeineSystemProperties()) outputs.upToDateWhen { false } - jvmArgs(javaExecJvmArgs()) + jvmArgs(defaultJvmArgs()) } eclipse.classpath.file.beforeMerged { diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/Admission.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/Admission.java index 742e86da1e..b3eb4450c7 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/Admission.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/Admission.java @@ -15,11 +15,14 @@ */ package com.github.benmanes.caffeine.cache.simulator.admission; +import static java.util.Locale.US; + import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.UnaryOperator; +import com.github.benmanes.caffeine.cache.simulator.BasicSettings; +import com.github.benmanes.caffeine.cache.simulator.admission.clairvoyant.Clairvoyant; import com.github.benmanes.caffeine.cache.simulator.policy.PolicyStats; +import com.google.common.base.Enums; import com.typesafe.config.Config; /** @@ -29,15 +32,16 @@ */ @SuppressWarnings("ImmutableEnumChecker") public enum Admission { - ALWAYS((config, policyStats) -> Admittor.always(), UnaryOperator.identity()), - TINYLFU(TinyLfu::new, name -> name + "_TinyLfu"); + ALWAYS((config, policyStats) -> Admittor.always(), ""), + CLAIRVOYANT(Clairvoyant::new, "_Clairvoyant"), + TINYLFU(TinyLfu::new, "_TinyLfu"); private final BiFunction factory; - private final Function formatter; + private final String suffix; - Admission(BiFunction factory, UnaryOperator formatter) { - this.formatter = formatter; + Admission(BiFunction factory, String suffix) { this.factory = factory; + this.suffix = suffix; } /** @@ -48,11 +52,16 @@ public enum Admission { * @return an admission policy */ public Admittor from(Config config, PolicyStats policyStats) { + if (this == TINYLFU) { + var override = new BasicSettings(config).tinyLfu().sketch().toUpperCase(US); + return Enums.getIfPresent(Admission.class, override).or(this) + .factory.apply(config, policyStats); + } return factory.apply(config, policyStats); } /** Returns the policy's formatted name. */ public String format(String name) { - return formatter.apply(name); + return name + suffix; } } diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/TinyLfu.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/TinyLfu.java index 6d0c62f5eb..eceec74324 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/TinyLfu.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/TinyLfu.java @@ -15,6 +15,8 @@ */ package com.github.benmanes.caffeine.cache.simulator.admission; +import static java.util.Locale.US; + import java.util.Random; import com.github.benmanes.caffeine.cache.simulator.BasicSettings; @@ -57,33 +59,6 @@ public TinyLfu(Config config, PolicyStats policyStats) { } } - private Frequency makeSketch(BasicSettings settings) { - String type = settings.tinyLfu().sketch(); - if (type.equalsIgnoreCase("count-min-4")) { - String reset = settings.tinyLfu().countMin4().reset(); - if (reset.equalsIgnoreCase("periodic")) { - return new PeriodicResetCountMin4(settings.config()); - } else if (reset.equalsIgnoreCase("incremental")) { - return new IncrementalResetCountMin4(settings.config()); - } else if (reset.equalsIgnoreCase("climber")) { - return new ClimberResetCountMin4(settings.config()); - } else if (reset.equalsIgnoreCase("indicator")) { - return new IndicatorResetCountMin4(settings.config()); - } else { - throw new IllegalStateException("Unknown reset type: " + reset); - } - } else if (type.equalsIgnoreCase("count-min-64")) { - return new CountMin64TinyLfu(settings.config()); - } else if (type.equalsIgnoreCase("random-table")) { - return new RandomRemovalFrequencyTable(settings.config()); - } else if (type.equalsIgnoreCase("tiny-table")) { - return new TinyCacheAdapter(settings.config()); - } else if (type.equalsIgnoreCase("perfect-table")) { - return new PerfectFrequency(settings.config()); - } - throw new IllegalStateException("Unknown sketch type: " + type); - } - public int frequency(long key) { return sketch.frequency(key); } @@ -107,4 +82,26 @@ public boolean admit(long candidateKey, long victimKey) { policyStats.recordRejection(); return false; } + + /** Returns the frequency histogram. */ + private static Frequency makeSketch(BasicSettings settings) { + String type = settings.tinyLfu().sketch(); + switch (type.toLowerCase(US)) { + case "count-min-4": { + String reset = settings.tinyLfu().countMin4().reset(); + switch (reset.toLowerCase(US)) { + case "climber": return new ClimberResetCountMin4(settings.config()); + case "periodic": return new PeriodicResetCountMin4(settings.config()); + case "indicator": return new IndicatorResetCountMin4(settings.config()); + case "incremental": return new IncrementalResetCountMin4(settings.config()); + default: throw new IllegalStateException("Unknown reset type: " + reset); + } + } + case "tiny-table": return new TinyCacheAdapter(settings.config()); + case "count-min-64": return new CountMin64TinyLfu(settings.config()); + case "perfect-table": return new PerfectFrequency(settings.config()); + case "random-table": return new RandomRemovalFrequencyTable(settings.config()); + default: throw new IllegalStateException("Unknown sketch type: " + type); + } + } } diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/clairvoyant/Clairvoyant.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/clairvoyant/Clairvoyant.java new file mode 100644 index 0000000000..84d629da1b --- /dev/null +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/admission/clairvoyant/Clairvoyant.java @@ -0,0 +1,112 @@ +/* + * Copyright 2024 Ben Manes. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.github.benmanes.caffeine.cache.simulator.admission.clairvoyant; + +import static com.google.common.base.Preconditions.checkState; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; + +import com.github.benmanes.caffeine.cache.simulator.BasicSettings; +import com.github.benmanes.caffeine.cache.simulator.admission.Admittor.KeyOnlyAdmittor; +import com.github.benmanes.caffeine.cache.simulator.policy.AccessEvent; +import com.github.benmanes.caffeine.cache.simulator.policy.PolicyStats; +import com.typesafe.config.Config; + +import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.ints.IntPriorityQueue; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; + +/** + * {@literal Bélády's} optimal page replacement policy applied as an admission policy by comparing + * the keys using their next access times. + * + * @author ben.manes@gmail.com (Ben Manes) + */ +public final class Clairvoyant implements KeyOnlyAdmittor { + private static final AtomicReference> snapshot = new AtomicReference<>(); + + private final Long2ObjectMap accessTimes; + private final PolicyStats policyStats; + + public Clairvoyant(Config config, PolicyStats policyStats) { + if (snapshot.get() == null) { + snapshot.set(readAccessTimes(new BasicSettings(config))); + } + accessTimes = new Long2ObjectOpenHashMap<>(snapshot.get().size()); + for (var entry : snapshot.get().long2ObjectEntrySet()) { + var times = new IntArrayFIFOQueue(entry.getValue().size()); + accessTimes.put(entry.getLongKey(), times); + entry.getValue().forEach(times::enqueue); + } + this.policyStats = policyStats; + } + + @Override + public void record(long key) { + if (snapshot.get() != null) { + snapshot.set(null); + } + + var times = accessTimes.get(key); + if (times == null) { + return; + } + times.dequeueInt(); + if (times.isEmpty()) { + accessTimes.remove(key); + } + } + + @Override + public boolean admit(long candidateKey, long victimKey) { + var candidateTime = nextAccessTime(candidateKey); + var victimTime = nextAccessTime(victimKey); + if (candidateTime > victimTime) { + policyStats.recordRejection(); + return false; + } + policyStats.recordAdmission(); + return true; + } + + private int nextAccessTime(long key) { + var times = accessTimes.get(key); + return ((times == null) || times.isEmpty()) ? Integer.MAX_VALUE : times.firstInt(); + } + + private static Long2ObjectMap readAccessTimes(BasicSettings settings) { + checkState(!settings.trace().isSynthetic(), "Synthetic traces cannot be predicted"); + var accessTimes = new Long2ObjectOpenHashMap(); + var trace = settings.trace().traceFiles().format() + .readFiles(settings.trace().traceFiles().paths()); + try (Stream events = trace.events()) { + int[] tick = { 0 }; + events.forEach(event -> { + var times = accessTimes.get(event.key()); + if (times == null) { + times = new IntArrayList(); + accessTimes.put(event.key(), times); + } + times.add(++tick[0]); + }); + } + return accessTimes; + } +} diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/WindowTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/WindowTinyLfuPolicy.java index f1b4aff641..989848cd75 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/WindowTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/WindowTinyLfuPolicy.java @@ -22,8 +22,8 @@ import java.util.Set; import com.github.benmanes.caffeine.cache.simulator.BasicSettings; +import com.github.benmanes.caffeine.cache.simulator.admission.Admission; import com.github.benmanes.caffeine.cache.simulator.admission.Admittor; -import com.github.benmanes.caffeine.cache.simulator.admission.TinyLfu; import com.github.benmanes.caffeine.cache.simulator.policy.Policy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.KeyOnlyPolicy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.PolicySpec; @@ -70,7 +70,7 @@ public final class WindowTinyLfuPolicy implements KeyOnlyPolicy { public WindowTinyLfuPolicy(double percentMain, WindowTinyLfuSettings settings) { this.policyStats = new PolicyStats(name() + " (%.0f%%)", 100 * (1.0d - percentMain)); - this.admittor = new TinyLfu(settings.config(), policyStats); + this.admittor = Admission.TINYLFU.from(settings.config(), policyStats); this.maximumSize = Math.toIntExact(settings.maximumSize()); int maxMain = (int) (maximumSize * percentMain); diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/climbing/HillClimberWindowTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/climbing/HillClimberWindowTinyLfuPolicy.java index 42de0252f4..ef80f42816 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/climbing/HillClimberWindowTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/climbing/HillClimberWindowTinyLfuPolicy.java @@ -31,8 +31,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; import com.github.benmanes.caffeine.cache.simulator.BasicSettings; +import com.github.benmanes.caffeine.cache.simulator.admission.Admission; import com.github.benmanes.caffeine.cache.simulator.admission.Admittor; -import com.github.benmanes.caffeine.cache.simulator.admission.TinyLfu; import com.github.benmanes.caffeine.cache.simulator.policy.Policy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.KeyOnlyPolicy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.PolicySpec; @@ -89,7 +89,7 @@ public HillClimberWindowTinyLfuPolicy(HillClimberType strategy, double percentMa this.initialPercentMain = percentMain; this.policyStats = new PolicyStats(name() + " (%s %.0f%%)", strategy.name().toLowerCase(US), 100 * (1.0 - initialPercentMain)); - this.admittor = new TinyLfu(settings.config(), policyStats); + this.admittor = Admission.TINYLFU.from(settings.config(), policyStats); this.climber = strategy.create(settings.config()); printSegmentSizes(); diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackTinyLfuPolicy.java index a81b748212..4926d8931b 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackTinyLfuPolicy.java @@ -20,7 +20,8 @@ import java.util.Map; import com.github.benmanes.caffeine.cache.simulator.BasicSettings; -import com.github.benmanes.caffeine.cache.simulator.admission.TinyLfu; +import com.github.benmanes.caffeine.cache.simulator.admission.Admission; +import com.github.benmanes.caffeine.cache.simulator.admission.Admittor; import com.github.benmanes.caffeine.cache.simulator.membership.Membership; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.KeyOnlyPolicy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.PolicySpec; @@ -46,7 +47,7 @@ public final class FeedbackTinyLfuPolicy implements KeyOnlyPolicy { private final Long2ObjectMap data; private final PolicyStats policyStats; - private final TinyLfu admittor; + private final Admittor admittor; private final int maximumSize; private final Node head; @@ -65,7 +66,7 @@ public FeedbackTinyLfuPolicy(Config config) { this.policyStats = new PolicyStats(name()); FeedbackTinyLfuSettings settings = new FeedbackTinyLfuSettings(config); this.maximumSize = Math.toIntExact(settings.maximumSize()); - this.admittor = new TinyLfu(settings.config(), policyStats); + this.admittor = Admission.TINYLFU.from(settings.config(), policyStats); this.data = new Long2ObjectOpenHashMap<>(); this.head = new Node(); diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackWindowTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackWindowTinyLfuPolicy.java index 3a9c775652..934959b537 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackWindowTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/feedback/FeedbackWindowTinyLfuPolicy.java @@ -24,7 +24,8 @@ import java.util.Set; import com.github.benmanes.caffeine.cache.simulator.BasicSettings; -import com.github.benmanes.caffeine.cache.simulator.admission.TinyLfu; +import com.github.benmanes.caffeine.cache.simulator.admission.Admission; +import com.github.benmanes.caffeine.cache.simulator.admission.Admittor; import com.github.benmanes.caffeine.cache.simulator.membership.Membership; import com.github.benmanes.caffeine.cache.simulator.policy.Policy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.KeyOnlyPolicy; @@ -51,7 +52,7 @@ public final class FeedbackWindowTinyLfuPolicy implements KeyOnlyPolicy { private final Long2ObjectMap data; private final PolicyStats policyStats; - private final TinyLfu admittor; + private final Admittor admittor; private final int maximumSize; private final Node headWindow; @@ -81,7 +82,7 @@ public final class FeedbackWindowTinyLfuPolicy implements KeyOnlyPolicy { public FeedbackWindowTinyLfuPolicy(double percentMain, FeedbackWindowTinyLfuSettings settings) { this.policyStats = new PolicyStats(name() + " (%.0f%%)", 100 * (1.0d - percentMain)); - this.admittor = new TinyLfu(settings.config(), policyStats); + this.admittor = Admission.TINYLFU.from(settings.config(), policyStats); this.maximumSize = Math.toIntExact(settings.maximumSize()); int maxMain = (int) (maximumSize * percentMain); diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/FullySegmentedWindowTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/FullySegmentedWindowTinyLfuPolicy.java index 4600368c83..1794e931ec 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/FullySegmentedWindowTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/FullySegmentedWindowTinyLfuPolicy.java @@ -22,8 +22,8 @@ import java.util.Set; import com.github.benmanes.caffeine.cache.simulator.BasicSettings; +import com.github.benmanes.caffeine.cache.simulator.admission.Admission; import com.github.benmanes.caffeine.cache.simulator.admission.Admittor; -import com.github.benmanes.caffeine.cache.simulator.admission.TinyLfu; import com.github.benmanes.caffeine.cache.simulator.policy.Policy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.KeyOnlyPolicy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.PolicySpec; @@ -69,7 +69,7 @@ public FullySegmentedWindowTinyLfuPolicy( this.maxWindow = maximumSize - maxMain; this.maxMainProtected = (int) (maxMain * settings.percentMainProtected()); this.maxWindowProtected = (int) (maxWindow * settings.percentWindowProtected()); - this.admittor = new TinyLfu(settings.config(), policyStats); + this.admittor = Admission.TINYLFU.from(settings.config(), policyStats); this.data = new Long2ObjectOpenHashMap<>(); this.headWindowProbation = new Node(); this.headWindowProtected = new Node(); diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/LruWindowTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/LruWindowTinyLfuPolicy.java index a29310bd34..401d884b46 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/LruWindowTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/LruWindowTinyLfuPolicy.java @@ -22,8 +22,8 @@ import java.util.Set; import com.github.benmanes.caffeine.cache.simulator.BasicSettings; +import com.github.benmanes.caffeine.cache.simulator.admission.Admission; import com.github.benmanes.caffeine.cache.simulator.admission.Admittor; -import com.github.benmanes.caffeine.cache.simulator.admission.TinyLfu; import com.github.benmanes.caffeine.cache.simulator.policy.Policy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.KeyOnlyPolicy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.PolicySpec; @@ -56,7 +56,7 @@ public final class LruWindowTinyLfuPolicy implements KeyOnlyPolicy { public LruWindowTinyLfuPolicy(double percentMain, LruWindowTinyLfuSettings settings) { this.policyStats = new PolicyStats(name() + " (%.0f%%)", 100 * (1.0d - percentMain)); int maximumSize = Math.toIntExact(settings.maximumSize()); - this.admittor = new TinyLfu(settings.config(), policyStats); + this.admittor = Admission.TINYLFU.from(settings.config(), policyStats); this.maxMain = (int) (maximumSize * percentMain); this.data = new Long2ObjectOpenHashMap<>(); this.maxWindow = maximumSize - maxMain; diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/RandomWindowTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/RandomWindowTinyLfuPolicy.java index 592d69557b..c98be82fd7 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/RandomWindowTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/RandomWindowTinyLfuPolicy.java @@ -22,8 +22,8 @@ import java.util.Set; import com.github.benmanes.caffeine.cache.simulator.BasicSettings; +import com.github.benmanes.caffeine.cache.simulator.admission.Admission; import com.github.benmanes.caffeine.cache.simulator.admission.Admittor; -import com.github.benmanes.caffeine.cache.simulator.admission.TinyLfu; import com.github.benmanes.caffeine.cache.simulator.policy.Policy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.KeyOnlyPolicy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.PolicySpec; @@ -55,7 +55,7 @@ public final class RandomWindowTinyLfuPolicy implements KeyOnlyPolicy { public RandomWindowTinyLfuPolicy(double percentMain, RandomWindowTinyLfuSettings settings) { policyStats = new PolicyStats(name() + " (%.0f%%)", 100 * (1.0d - percentMain)); maximumSize = Math.toIntExact(settings.maximumSize()); - admittor = new TinyLfu(settings.config(), policyStats); + admittor = Admission.TINYLFU.from(settings.config(), policyStats); random = new Random(settings.randomSeed()); data = new Long2ObjectOpenHashMap<>(); diff --git a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/S4WindowTinyLfuPolicy.java b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/S4WindowTinyLfuPolicy.java index 1a8ef765fb..4b380f69ad 100644 --- a/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/S4WindowTinyLfuPolicy.java +++ b/simulator/src/main/java/com/github/benmanes/caffeine/cache/simulator/policy/sketch/segment/S4WindowTinyLfuPolicy.java @@ -23,8 +23,8 @@ import java.util.Set; import com.github.benmanes.caffeine.cache.simulator.BasicSettings; +import com.github.benmanes.caffeine.cache.simulator.admission.Admission; import com.github.benmanes.caffeine.cache.simulator.admission.Admittor; -import com.github.benmanes.caffeine.cache.simulator.admission.TinyLfu; import com.github.benmanes.caffeine.cache.simulator.policy.Policy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.KeyOnlyPolicy; import com.github.benmanes.caffeine.cache.simulator.policy.Policy.PolicySpec; @@ -58,7 +58,7 @@ public final class S4WindowTinyLfuPolicy implements KeyOnlyPolicy { public S4WindowTinyLfuPolicy(double percentMain, S4WindowTinyLfuSettings settings) { this.policyStats = new PolicyStats(name() + " (%.0f%%)", 100 * (1.0d - percentMain)); - this.admittor = new TinyLfu(settings.config(), policyStats); + this.admittor = Admission.TINYLFU.from(settings.config(), policyStats); this.maximumSize = Math.toIntExact(settings.maximumSize()); this.maxMain = (int) (maximumSize * percentMain); this.maxWindow = maximumSize - maxMain; diff --git a/simulator/src/main/resources/reference.conf b/simulator/src/main/resources/reference.conf index 0a196a4c9a..04eae47eb6 100644 --- a/simulator/src/main/resources/reference.conf +++ b/simulator/src/main/resources/reference.conf @@ -113,6 +113,7 @@ caffeine.simulator { admission = [ Always, TinyLfu, + Clairvoyant, ] # The membership filter @@ -191,6 +192,8 @@ caffeine.simulator { tiny-lfu { # CountMinSketch: count-min-4 (4-bit), count-min-64 (64-bit) # Table: random-table, tiny-table, perfect-table + # Clairvoyant: next access times comparison + # Always: always admits (disablement) sketch = count-min-4 # If increments are conservative by only updating the minimum counters for CountMin sketches