Skip to content

Commit

Permalink
modernize the simulator to use jdk21 (unrelated to the core library)
Browse files Browse the repository at this point in the history
As dependencies are moving on from jdk11, it makes sense for the
analysis tools to upgrade as well. This was already executing as 21
to support those dependencies and now simply requires it at the
language level. The user-facing library and jmh benchmarks remain jdk11
compatible.

A profile of the execution showed excessive garbage, which is now
reduced to improve runtimes.
- A direct mapped cache of boxed Long keys are used to reduce the
allocations by caching libraries. The research policies use primitive
based data structures, but user-facing libraries are not as fortunate.
- Using cache listeners to track eviction counts can require the
libraries allocate, e.g. copy the entry. This is safe enough to trust
their stats because its usually the hit rate that is accidentally
miscalculated and we verify that matches. The eviction count is easily
derrived (miss count - size) so just a sanity check.
* This particular sped up Ehcache from an 18.25m run to 17m, because
if any event type is registered against then it emits and does work for
the unregistered event types too. This may seem like low-hanging fruit
for them to fix, but every other cache runs in seconds (7s for
Caffeine) so it is already unusable. Their response was that Ehcache v3
is not intended to be used in scenarios where LRU eviction might occur,
only as a safety net, so any eviction is considered user error.

Some nice simplifications due to language improvements:
- Replaced AutoValue with Records
- More concise instanceof casting
- More concise switch expressions
  • Loading branch information
ben-manes committed Feb 16, 2025
1 parent 2fee4bd commit 7ccb55c
Show file tree
Hide file tree
Showing 37 changed files with 454 additions and 487 deletions.
2 changes: 2 additions & 0 deletions .github/actions/run-gradle/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ runs:
with:
add-job-summary: never
cache-read-only: false
cache-overwrite-existing: true
gradle-home-cache-cleanup: true
gradle-home-cache-strict-match: true
gradle-home-cache-includes: |
caches
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/spelling.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ jobs:
with:
persist-credentials: false
- name: Typos
uses: crate-ci/typos@11ca4583f2f3f74c7e7785c0ecb20fe2c99a4308 # v1.29.5
uses: crate-ci/typos@51f257b946f503b768e522781f56e9b7b5570d48 # v1.29.7
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ public void deschedule(Node<K, V> node) {
@SuppressWarnings("Varifier")
Node<K, V> findBucket(@Var long time) {
long duration = Math.max(0L, time - nanos);
if (duration <= 0L) {
if (duration == 0L) {
time = nanos;
}

Expand Down
11 changes: 4 additions & 7 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[versions]
asm = "9.7.1"
auto-value = "1.11.0"
awaitility = "4.2.2"
bcel = "6.10.0"
bnd = "7.1.0"
Expand Down Expand Up @@ -40,7 +39,7 @@ hamcrest = "3.0"
hazelcast = "5.5.0"
httpclient = "4.5.14"
idea = "1.1.10"
jackrabbit = "1.74.0"
jackrabbit = "1.76.0"
jackson = "2.18.2"
jacoco = "0.8.12"
jakarta-inject = "2.0.1"
Expand Down Expand Up @@ -82,15 +81,15 @@ pmd = "7.10.0"
protobuf = "4.29.3"
slf4j = "2.0.16"
slf4j-test = "3.0.1"
snakeyaml = "2.3"
snakeyaml = "2.4"
sigstore = "1.2.0"
sonarqube = "6.0.1.5171"
spotbugs = "4.9.1"
spotbugs-contrib = "7.6.9"
spotbugs-plugin = "6.1.4"
spotbugs-plugin = "6.1.5"
stream = "2.9.8"
tcache = "2.0.1"
testng = "7.10.2"
testng = "7.11.0"
truth = "1.4.4"
univocity-parsers = "2.9.1"
versions = "0.52.0"
Expand All @@ -101,8 +100,6 @@ zstd = "1.5.6-10"

[libraries]
asm-bom = { module = "org.ow2.asm:asm-bom", version.ref = "asm" }
auto-value-annotations = { module = "com.google.auto.value:auto-value-annotations", version.ref = "auto-value" }
auto-value-processor = { module = "com.google.auto.value:auto-value", version.ref = "auto-value" }
awaitility = { module = "org.awaitility:awaitility", version.ref = "awaitility" }
bcel = { module = "org.apache.bcel:bcel", version.ref = "bcel" }
bouncycastle-jdk18on = { module = "org.bouncycastle:bcprov-jdk18on", version.ref = "bouncycastle-jdk18on" }
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,16 @@ dependencies {
annotationProcessor(platform(libs.kotlin.bom))
}

val javaVersion = JavaLanguageVersion.of(System.getenv("JAVA_VERSION")?.toIntOrNull() ?: 11)
val javaVendor = System.getenv("JAVA_VENDOR")?.let { JvmVendorSpec.matching(it) }
val javaRuntimeVersion = maxOf(javaVersion, JavaLanguageVersion.of(21))
java.toolchain {
languageVersion = javaVersion
vendor = javaVendor
languageVersion = JavaLanguageVersion.of(System.getenv("JAVA_VERSION")?.toIntOrNull() ?: 11)
vendor = System.getenv("JAVA_VENDOR")?.let { JvmVendorSpec.matching(it) }
}
val javaRuntimeVersion: Provider<JavaLanguageVersion> =
java.toolchain.languageVersion.map { maxOf(it, JavaLanguageVersion.of(21)) }

tasks.withType<JavaCompile>().configureEach {
inputs.property("javaVendor", javaVendor.toString())
sourceCompatibility = javaVersion.toString()
targetCompatibility = javaVersion.toString()
options.release = javaVersion.asInt()
inputs.property("javaVendor", java.toolchain.vendor.get().toString())
options.release = java.toolchain.languageVersion.get().asInt()

javaCompiler = javaToolchains.compilerFor {
// jdk 17+ is required by compiler plugins, e.g. error-prone
Expand All @@ -44,7 +41,7 @@ tasks.withType<JavaCompile>().configureEach {
if (isCI()) {
compilerArgs.add("-Werror")
}
if (javaVersion.canCompileOrRun(21)) {
if (java.toolchain.languageVersion.get().canCompileOrRun(21)) {
compilerArgs.add("-proc:full")
}
encoding = "UTF-8"
Expand Down Expand Up @@ -80,10 +77,10 @@ tasks.jar {
properties.empty()
bnd(mapOf(
"Bundle-License" to "https://www.apache.org/licenses/LICENSE-2.0",
"Build-Jdk-Spec" to java.toolchain.languageVersion.get(),
"Implementation-Title" to project.description,
"Bundle-Description" to project.description,
"Implementation-Version" to version,
"Build-Jdk-Spec" to javaVersion,
"-noextraheaders" to true,
"-reproducible" to true,
"-snapshot" to "SNAPSHOT"))
Expand All @@ -96,14 +93,14 @@ tasks.withType<Javadoc>().configureEach {
use()
quiet()
noTimestamp()
addStringOption("-release", javaVersion.toString())
addStringOption("-release", java.toolchain.languageVersion.get().toString())
addStringOption("-link-modularity-mismatch", "info")
links(
"https://jspecify.dev/docs/api/",
"https://errorprone.info/api/latest/",
"https://lightbend.github.io/config/latest/api/",
"https://docs.oracle.com/en/java/javase/$javaVersion/docs/api/",
"https://guava.dev/releases/${libs.versions.guava.get()}/api/docs/")
"https://guava.dev/releases/${libs.versions.guava.get()}/api/docs/",
"https://docs.oracle.com/en/java/javase/${java.toolchain.languageVersion.get()}/docs/api/")

if (project != project(":caffeine")) {
linksOffline("https://static.javadoc.io/$group/caffeine/$version/",
Expand Down
5 changes: 4 additions & 1 deletion simulator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import net.ltgt.gradle.nullaway.nullaway

plugins {
id("application")
id("auto-value.caffeine")
id("java-library.caffeine")
}

Expand Down Expand Up @@ -47,6 +46,10 @@ application {
mainClass = "com.github.benmanes.caffeine.cache.simulator.Simulator"
}

java.toolchain {
languageVersion = maxOf(languageVersion.get(), JavaLanguageVersion.of(21))
}

forbiddenApis {
bundledSignatures.addAll(listOf("commons-io-unsafe-2.15.1", "jdk-deprecated",
"jdk-internal", "jdk-non-portable", "jdk-reflection", "jdk-unsafe"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

import java.util.stream.LongStream;

import com.github.benmanes.caffeine.cache.simulator.BasicSettings.SyntheticSettings.HotspotSettings;
import com.github.benmanes.caffeine.cache.simulator.BasicSettings.TraceSettings;
import com.github.benmanes.caffeine.cache.simulator.parser.TraceReader.KeyOnlyTraceReader;

Expand All @@ -46,32 +45,33 @@ private Synthetic() {}
/** Returns a sequence of events based on the setting's distribution. */
public static KeyOnlyTraceReader generate(TraceSettings settings) {
int events = settings.synthetic().events();
switch (settings.synthetic().distribution().toLowerCase(US)) {
case "counter":
return counter(settings.synthetic().counter().start(), events);
case "repeating":
return repeating(settings.synthetic().repeating().items(), events);
case "uniform":
var uniform = settings.synthetic().uniform();
return uniform(uniform.lowerBound(), uniform.upperBound(), events);
case "exponential":
return exponential(settings.synthetic().exponential().mean(), events);
case "hotspot":
HotspotSettings hotspot = settings.synthetic().hotspot();
return Synthetic.hotspot(hotspot.lowerBound(), hotspot.upperBound(),
return switch (settings.synthetic().distribution().toLowerCase(US)) {
case "counter" ->
counter(settings.synthetic().counter().start(), events);
case "repeating" ->
repeating(settings.synthetic().repeating().items(), events);
case "uniform" ->
uniform(settings.synthetic().uniform().lowerBound(),
settings.synthetic().uniform().upperBound(), events);
case "exponential" ->
exponential(settings.synthetic().exponential().mean(), events);
case "hotspot" -> {
var hotspot = settings.synthetic().hotspot();
yield hotspot(hotspot.lowerBound(), hotspot.upperBound(),
hotspot.hotOpnFraction(), hotspot.hotsetFraction(), events);
case "zipfian":
return zipfian(settings.synthetic().zipfian().items(),
}
case "zipfian" ->
zipfian(settings.synthetic().zipfian().items(),
settings.synthetic().zipfian().constant(), events);
case "scrambled-zipfian":
return scrambledZipfian(settings.synthetic().zipfian().items(),
case "scrambled-zipfian" ->
scrambledZipfian(settings.synthetic().zipfian().items(),
settings.synthetic().zipfian().constant(), events);
case "skewed-zipfian-latest":
return skewedZipfianLatest(settings.synthetic().zipfian().items(), events);
default:
case "skewed-zipfian-latest" ->
skewedZipfianLatest(settings.synthetic().zipfian().items(), events);
default ->
throw new IllegalStateException("Unknown distribution: "
+ settings.synthetic().distribution());
}
};
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,22 +86,22 @@ public boolean admit(long candidateKey, long victimKey) {
/** Returns the frequency histogram. */
private static Frequency makeSketch(BasicSettings settings) {
String type = settings.tinyLfu().sketch();
switch (type.toLowerCase(US)) {
case "count-min-4": {
return 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);
}
yield switch (reset.toLowerCase(US)) {
case "climber" -> new ClimberResetCountMin4(settings.config());
case "periodic" -> new PeriodicResetCountMin4(settings.config());
case "indicator" -> new IndicatorResetCountMin4(settings.config());
case "incremental" -> 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);
}
case "tiny-table" -> new TinyCacheAdapter(settings.config());
case "count-min-64" -> new CountMin64TinyLfu(settings.config());
case "perfect-table" -> new PerfectFrequency(settings.config());
case "random-table" -> new RandomRemovalFrequencyTable(settings.config());
default -> throw new IllegalStateException("Unknown sketch type: " + type);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,27 +67,27 @@ protected BufferedInputStream readFile() {

@SuppressWarnings("PMD.CloseResource")
protected BufferedInputStream readInput(InputStream input) {
@Var BufferedInputStream buffered = null;
@Var BufferedInputStream bufferedStream = null;
try {
buffered = new BufferedInputStream(input, BUFFER_SIZE);
bufferedStream = new BufferedInputStream(input, BUFFER_SIZE);
var extractors = List.<UnaryOperator<InputStream>>of(
AbstractTraceReader::tryXz, AbstractTraceReader::tryCompressed, this::tryArchived);
for (var extractor : extractors) {
buffered.mark(100);
InputStream next = extractor.apply(buffered);
bufferedStream.mark(100);
InputStream next = extractor.apply(bufferedStream);
if (next == null) {
buffered.reset();
} else if (next instanceof BufferedInputStream) {
buffered = (BufferedInputStream) next;
bufferedStream.reset();
} else if (next instanceof BufferedInputStream buffered) {
bufferedStream = buffered;
} else {
buffered = new BufferedInputStream(next, BUFFER_SIZE);
bufferedStream = new BufferedInputStream(next, BUFFER_SIZE);
}
}
return buffered;
return bufferedStream;
} catch (Throwable t) {
try {
if (buffered != null) {
buffered.close();
if (bufferedStream != null) {
bufferedStream.close();
}
} catch (IOException e) {
t.addSuppressed(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import com.google.common.base.CaseFormat;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import com.google.errorprone.annotations.Var;

import picocli.CommandLine;
Expand Down Expand Up @@ -91,7 +92,7 @@ public void run() {
}

private static String[] argumentsWithDefaults(String[] args) {
var params = Lists.newArrayList(args);
var params = new ArrayList<>(Arrays.asList(args));
if (!params.contains("--inputFormat")) {
@Var boolean found = false;
@Var boolean defaultFormat = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package com.github.benmanes.caffeine.cache.simulator.parser.wikipedia;

import static java.util.Objects.requireNonNull;

import java.util.Objects;
import java.util.stream.LongStream;

Expand All @@ -23,7 +25,6 @@

import com.github.benmanes.caffeine.cache.simulator.parser.TextTraceReader;
import com.github.benmanes.caffeine.cache.simulator.parser.TraceReader.KeyOnlyTraceReader;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.hash.Hashing;
import com.google.errorprone.annotations.Var;
Expand All @@ -42,8 +43,8 @@ public final class WikipediaTraceReader extends TextTraceReader implements KeyOn
"wiki/Special:Search", "w/query.php", "wiki/Talk:", "wiki/Special:AutoLogin",
"Special:UserLogin", "w/api.php", "error:");
private static final ImmutableList<Replacement> REPLACEMENTS = ImmutableList.of(
Replacement.of("%2F", "/"), Replacement.of("%20", " "),
Replacement.of("&amp;", "&"), Replacement.of("%3A", ":"));
new Replacement("%2F", "/"), new Replacement("%20", " "),
new Replacement("&amp;", "&"), new Replacement("%3A", ":"));

public WikipediaTraceReader(String filePath) {
super(filePath);
Expand Down Expand Up @@ -133,13 +134,10 @@ public static boolean isAllowed(String path) {
return true;
}

@AutoValue
abstract static class Replacement {
abstract String search();
abstract String replace();

static Replacement of(String search, String replace) {
return new AutoValue_WikipediaTraceReader_Replacement(search, replace);
record Replacement(String search, String replace) {
Replacement {
requireNonNull(search);
requireNonNull(replace);
}
}
}
Loading

0 comments on commit 7ccb55c

Please sign in to comment.