Skip to content

Commit

Permalink
Remove Snappy dependency (#5092)
Browse files Browse the repository at this point in the history
The `JavaTypeCache` now internally uses the `AdaptiveRadixTree` instead of a `HashMap` with Snappy-compressed keys.

Overall this results in a lower memory footprint:

```
Retained AdaptiveRadixTree size:    7316248 bytes
Retained Snappy size:               8575104 bytes
Retained HashMap size:              9105560 bytes
```

Here are the JMH benchmark measurements showing an overall increase in performance (the tests with the `AddOpens` suffix started the JVM using `--add-opens java.base/java.lang=ALL-UNNAMED` to skip some `byte[]` allocations for increased performance):

```
Benchmark                                                              Mode  Cnt         Score   Error   Units
JavaTypeCacheBenchmark.readAdaptiveRadix                              thrpt    2       794.740           ops/s
JavaTypeCacheBenchmark.readAdaptiveRadix:gc.alloc.rate                thrpt    2      2239.494          MB/sec
JavaTypeCacheBenchmark.readAdaptiveRadix:gc.alloc.rate.norm           thrpt    2   2956386.085            B/op
JavaTypeCacheBenchmark.readAdaptiveRadix:gc.count                     thrpt    2       104.000          counts
JavaTypeCacheBenchmark.readAdaptiveRadix:gc.time                      thrpt    2        37.000              ms
JavaTypeCacheBenchmark.readAdaptiveRadixAddOpens                      thrpt    2       886.947           ops/s
JavaTypeCacheBenchmark.readAdaptiveRadixAddOpens:gc.alloc.rate        thrpt    2         0.002          MB/sec
JavaTypeCacheBenchmark.readAdaptiveRadixAddOpens:gc.alloc.rate.norm   thrpt    2         1.842            B/op
JavaTypeCacheBenchmark.readAdaptiveRadixAddOpens:gc.count             thrpt    2           ≈ 0          counts
JavaTypeCacheBenchmark.readSnappy                                     thrpt    2       344.614           ops/s
JavaTypeCacheBenchmark.readSnappy:gc.alloc.rate                       thrpt    2      3134.230          MB/sec
JavaTypeCacheBenchmark.readSnappy:gc.alloc.rate.norm                  thrpt    2   9544228.824            B/op
JavaTypeCacheBenchmark.readSnappy:gc.count                            thrpt    2       144.000          counts
JavaTypeCacheBenchmark.readSnappy:gc.time                             thrpt    2        61.000              ms
JavaTypeCacheBenchmark.writeAdaptiveRadix                             thrpt    2       650.472           ops/s
JavaTypeCacheBenchmark.writeAdaptiveRadix:gc.alloc.rate               thrpt    2      4941.345          MB/sec
JavaTypeCacheBenchmark.writeAdaptiveRadix:gc.alloc.rate.norm          thrpt    2   7969122.563            B/op
JavaTypeCacheBenchmark.writeAdaptiveRadix:gc.count                    thrpt    2       112.000          counts
JavaTypeCacheBenchmark.writeAdaptiveRadix:gc.time                     thrpt    2       101.000              ms
JavaTypeCacheBenchmark.writeAdaptiveRadixAddOpens                     thrpt    2       754.388           ops/s
JavaTypeCacheBenchmark.writeAdaptiveRadixAddOpens:gc.alloc.rate       thrpt    2      3604.748          MB/sec
JavaTypeCacheBenchmark.writeAdaptiveRadixAddOpens:gc.alloc.rate.norm  thrpt    2   5012698.210            B/op
JavaTypeCacheBenchmark.writeAdaptiveRadixAddOpens:gc.count            thrpt    2       100.000          counts
JavaTypeCacheBenchmark.writeAdaptiveRadixAddOpens:gc.time             thrpt    2        81.000              ms
JavaTypeCacheBenchmark.writeSnappy                                    thrpt    2       324.280           ops/s
JavaTypeCacheBenchmark.writeSnappy:gc.alloc.rate                      thrpt    2      3355.699          MB/sec
JavaTypeCacheBenchmark.writeSnappy:gc.alloc.rate.norm                 thrpt    2  10859397.103            B/op
JavaTypeCacheBenchmark.writeSnappy:gc.count                           thrpt    2        77.000          counts
JavaTypeCacheBenchmark.writeSnappy:gc.time                            thrpt    2       102.000              ms
```
  • Loading branch information
knutwannheden authored Feb 25, 2025
1 parent 9e9594d commit 777d6e1
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 176 deletions.
1 change: 1 addition & 0 deletions rewrite-benchmarks/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies {
jmh("org.openjdk.jmh:jmh-core:latest.release")
jmh("org.openjdk.jol:jol-core:latest.release")
jmh("io.github.fastfilter:fastfilter:latest.release")
jmh("org.xerial.snappy:snappy-java:1.1.10.7")

// Nebula doesn't like having jmhAnnotationProcessor without jmh so we just add it twice.
jmh("org.openjdk.jmh:jmh-generator-annprocess:latest.release")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@
import org.openrewrite.SourceFile;
import org.openrewrite.internal.InMemoryLargeSourceSet;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.internal.AdaptiveRadixJavaTypeCache;
import org.openrewrite.java.internal.JavaTypeCache;
import org.xerial.snappy.Snappy;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
Expand All @@ -36,17 +39,20 @@
import java.util.Map;
import java.util.stream.Collectors;

@SuppressWarnings("NotNullFieldNotInitialized")
@State(Scope.Benchmark)
public class JavaCompilationUnitState {
JavaParser.Builder<? extends JavaParser, ?> javaParser;
List<SourceFile> sourceFiles;
List<Path> inputs;
JavaTypeCache snappyTypeCache;
AdaptiveRadixJavaTypeCache radixMapTypeCache;
SnappyJavaTypeCache snappyTypeCache;
JavaTypeCache radixMapTypeCache;
MapJavaTypeCache typeCache;

public static void main(String[] args) throws URISyntaxException {
new JavaCompilationUnitState().setup();
JavaCompilationUnitState javaCompiler = new JavaCompilationUnitState();
javaCompiler.setup();
javaCompiler.printMemory();
}

@Setup(Level.Trial)
Expand All @@ -57,6 +63,7 @@ public void setup() throws URISyntaxException {
inputs = Arrays.asList(
rewriteRoot.resolve("rewrite-core/src/main/java/org/openrewrite/internal/lang/Nullable.java"),
rewriteRoot.resolve("rewrite-core/src/main/java/org/openrewrite/internal/lang/NullUtils.java"),
rewriteRoot.resolve("rewrite-core/src/main/java/org/openrewrite/internal/AdaptiveRadixTree.java"),
rewriteRoot.resolve("rewrite-core/src/main/java/org/openrewrite/internal/MetricsHelper.java"),
rewriteRoot.resolve("rewrite-core/src/main/java/org/openrewrite/internal/ListUtils.java"),
rewriteRoot.resolve("rewrite-core/src/main/java/org/openrewrite/internal/PropertyPlaceholderHelper.java"),
Expand All @@ -70,6 +77,7 @@ public void setup() throws URISyntaxException {
rewriteRoot.resolve("rewrite-core/src/main/java/org/openrewrite/style/Style.java"),
rewriteRoot.resolve("rewrite-core/src/main/java/org/openrewrite/config/DeclarativeNamedStyles.java"),
rewriteRoot.resolve("rewrite-core/src/main/java/org/openrewrite/style/NamedStyles.java"),
rewriteRoot.resolve("rewrite-core/src/main/java/org/openrewrite/Incubating.java"),
rewriteRoot.resolve("rewrite-core/src/main/java/org/openrewrite/Option.java"),
rewriteRoot.resolve("rewrite-core/src/main/java/org/openrewrite/config/OptionDescriptor.java"),
rewriteRoot.resolve("rewrite-core/src/main/java/org/openrewrite/config/RecipeDescriptor.java"),
Expand Down Expand Up @@ -99,12 +107,12 @@ public void setup() throws URISyntaxException {
.parse(inputs, null, new InMemoryExecutionContext())
.collect(Collectors.toList());

radixMapTypeCache = new AdaptiveRadixJavaTypeCache();
radixMapTypeCache = new JavaTypeCache();
for (Map.Entry<String, Object> entry : typeCache.map().entrySet()) {
radixMapTypeCache.put(entry.getKey(), entry.getValue());
}

snappyTypeCache = new JavaTypeCache();
snappyTypeCache = new SnappyJavaTypeCache();
for (Map.Entry<String, Object> entry : typeCache.map().entrySet()) {
snappyTypeCache.put(entry.getKey(), entry.getValue());
}
Expand All @@ -115,6 +123,8 @@ void printMemory() {
System.out.printf("Retained AdaptiveRadixTree size: %10d bytes\n", retainedSize);
retainedSize = GraphLayout.parseInstance(snappyTypeCache).totalSize();
System.out.printf("Retained Snappy size: %10d bytes\n", retainedSize);
retainedSize = GraphLayout.parseInstance(typeCache).totalSize();
System.out.printf("Retained HashMap size: %10d bytes\n", retainedSize);
}

@TearDown(Level.Trial)
Expand Down Expand Up @@ -155,13 +165,64 @@ public void clear() {
}

@Override
public int size() {
return typeCache.size();
public MapJavaTypeCache clone() {
MapJavaTypeCache clone = (MapJavaTypeCache) super.clone();
clone.typeCache = new HashMap<>(this.typeCache);
return clone;
}
}

static class SnappyJavaTypeCache extends JavaTypeCache {

// empirical value: below this size, the compressed key is larger or only slightly smaller
// although also note that a String object has a 24 bytes overhead vs. the 16 bytes of a BytesKey object
public static final int COMPRESSION_THRESHOLD = 50;

@SuppressWarnings("ClassCanBeRecord")
private static class BytesKey {
private final byte[] data;
BytesKey(byte[] data) {
this.data = data;
}
}

Map<Object, Object> typeCache = new HashMap<>();

@Override
public MapJavaTypeCache clone() {
MapJavaTypeCache clone = (MapJavaTypeCache) super.clone();
public <T> @Nullable T get(String signature) {
//noinspection unchecked
return (T) typeCache.get(key(signature));
}

@Override
public void put(String signature, Object o) {
typeCache.put(key(signature), o);
}

private static boolean snappyUsable = true;

private Object key(String signature) {
if (signature.length() > COMPRESSION_THRESHOLD && snappyUsable) {
try {
return new BytesKey(Snappy.compress(signature.getBytes(StandardCharsets.UTF_8)));
} catch (IOException e) {
throw new UncheckedIOException(e);
} catch (NoClassDefFoundError e) {
// Some systems fail to load Snappy native components, so fall back to not compressing
snappyUsable = false;
}
}
return signature;
}

@Override
public void clear() {
typeCache.clear();
}

@Override
public SnappyJavaTypeCache clone() {
SnappyJavaTypeCache clone = (SnappyJavaTypeCache) super.clone();
clone.typeCache = new HashMap<>(this.typeCache);
return clone;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openrewrite.InMemoryExecutionContext;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.internal.AdaptiveRadixJavaTypeCache;
import org.openrewrite.java.internal.JavaTypeCache;

import java.net.URISyntaxException;
Expand All @@ -40,7 +39,7 @@ public class JavaParserBenchmark {

@Benchmark
public void snappy(JavaCompilationUnitState state, Blackhole bh) {
JavaTypeCache typeCache = new JavaTypeCache();
JavaTypeCache typeCache = new JavaCompilationUnitState.SnappyJavaTypeCache();
JavaParser parser = state.javaParser.typeCache(typeCache).build();
parser
.parse(state.inputs, null, new InMemoryExecutionContext())
Expand All @@ -49,7 +48,7 @@ public void snappy(JavaCompilationUnitState state, Blackhole bh) {

@Benchmark
public void adaptiveRadix(JavaCompilationUnitState state, Blackhole bh) {
AdaptiveRadixJavaTypeCache typeCache = new AdaptiveRadixJavaTypeCache();
JavaTypeCache typeCache = new JavaTypeCache();
JavaParser parser = state.javaParser.typeCache(typeCache).build();
parser
.parse(state.inputs, null, new InMemoryExecutionContext())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,48 @@
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openrewrite.java.internal.AdaptiveRadixJavaTypeCache;
import org.openrewrite.java.internal.JavaTypeCache;

import java.net.URISyntaxException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Fork(1)
@Measurement(iterations = 2)
@Warmup(iterations = 2)
@Fork(value = 1)
@Measurement(iterations = 3, time = 5)
@Warmup(iterations = 3, time = 5)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@Threads(4)
public class JavaTypeCacheBenchmark {

@Benchmark
public void writeSnappy(JavaCompilationUnitState state, Blackhole bh) {
JavaTypeCache typeCache = new JavaTypeCache();
JavaTypeCache typeCache = new JavaCompilationUnitState.SnappyJavaTypeCache();
for (Map.Entry<String, Object> entry : state.typeCache.map().entrySet()) {
typeCache.put(entry.getKey(), entry.getValue());
}
}

// @Benchmark
public void writeHash(JavaCompilationUnitState state, Blackhole bh) {
JavaTypeCache typeCache = new JavaCompilationUnitState.MapJavaTypeCache();
for (Map.Entry<String, Object> entry : state.typeCache.map().entrySet()) {
typeCache.put(new String(entry.getKey()), entry.getValue());
}
}

@Benchmark
public void writeAdaptiveRadix(JavaCompilationUnitState state, Blackhole bh) {
AdaptiveRadixJavaTypeCache typeCache = new AdaptiveRadixJavaTypeCache();
JavaTypeCache typeCache = new JavaTypeCache();
for (Map.Entry<String, Object> entry : state.typeCache.map().entrySet()) {
typeCache.put(entry.getKey(), entry.getValue());
}
}

@Benchmark
@Fork(value = 1, jvmArgsAppend = {"--add-opens", "java.base/java.lang=ALL-UNNAMED"})
public void writeAdaptiveRadixAddOpens(JavaCompilationUnitState state, Blackhole bh) {
JavaTypeCache typeCache = new JavaTypeCache();
for (Map.Entry<String, Object> entry : state.typeCache.map().entrySet()) {
typeCache.put(entry.getKey(), entry.getValue());
}
Expand All @@ -60,13 +76,28 @@ public void readSnappy(JavaCompilationUnitState state, Blackhole bh) {
}
}

// @Benchmark
public void readHash(JavaCompilationUnitState state, Blackhole bh) {
for (Map.Entry<String, Object> entry : state.typeCache.map().entrySet()) {
bh.consume(state.typeCache.get(entry.getKey()));
}
}

@Benchmark
public void readAdaptiveRadix(JavaCompilationUnitState state, Blackhole bh) {
for (Map.Entry<String, Object> entry : state.typeCache.map().entrySet()) {
bh.consume(state.radixMapTypeCache.get(entry.getKey()));
}
}

@Benchmark
@Fork(value = 1, jvmArgsAppend = {"--add-opens", "java.base/java.lang=ALL-UNNAMED"})
public void readAdaptiveRadixAddOpens(JavaCompilationUnitState state, Blackhole bh) {
for (Map.Entry<String, Object> entry : state.typeCache.map().entrySet()) {
bh.consume(state.radixMapTypeCache.get(entry.getKey()));
}
}

public static void main(String[] args) throws RunnerException, URISyntaxException {
Options opt = new OptionsBuilder()
.include(JavaTypeCacheBenchmark.class.getSimpleName())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,7 @@ public AdaptiveRadixTree<V> copy() {

public void clear() {
root = null;
keyTable.clear();
}

private static class KeyTable {
Expand Down Expand Up @@ -710,5 +711,10 @@ private void ensureCapacity(int additional) {
public byte get(int offset) {
return storage[offset];
}

public void clear() {
storage = new byte[INITIAL_CAPACITY];
size = 0;
}
}
}
2 changes: 0 additions & 2 deletions rewrite-java/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ dependencies {
implementation("org.apache.commons:commons-text:latest.release")
implementation("io.github.classgraph:classgraph:latest.release")

implementation("org.xerial.snappy:snappy-java:1.1.10.+")

api("com.fasterxml.jackson.core:jackson-annotations")

// these are required for now so that `ChangeType` and `ChangePackage` can use the `Reference` trait
Expand Down
Loading

0 comments on commit 777d6e1

Please sign in to comment.