From 6e416c11d7d5042a92fbb38f2f50b64b202ca084 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 18 Mar 2024 16:21:12 +0100 Subject: [PATCH] Reduce memory consumption in dependency analyzer --- .../teavm/dependency/SuperClassFilter.java | 45 +++++++++++++++---- .../teavm/dependency/VirtualCallConsumer.java | 34 ++++++++++++-- 2 files changed, 68 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/org/teavm/dependency/SuperClassFilter.java b/core/src/main/java/org/teavm/dependency/SuperClassFilter.java index 11917393e..280d83b02 100644 --- a/core/src/main/java/org/teavm/dependency/SuperClassFilter.java +++ b/core/src/main/java/org/teavm/dependency/SuperClassFilter.java @@ -15,13 +15,16 @@ */ package org.teavm.dependency; +import com.carrotsearch.hppc.IntIntHashMap; import java.util.BitSet; import org.teavm.common.OptionalPredicate; class SuperClassFilter implements DependencyTypeFilter { + private static final int SMALL_CACHE_THRESHOLD = 16; private OptionalPredicate predicate; - private BitSet knownTypes = new BitSet(); - private BitSet cache = new BitSet(); + private IntIntHashMap smallCache; + private BitSet knownTypes; + private BitSet cache; SuperClassFilter(DependencyAnalyzer dependencyAnalyzer, DependencyType superType) { predicate = dependencyAnalyzer.getClassHierarchy().getSuperclassPredicate(superType.getName()); @@ -29,12 +32,38 @@ class SuperClassFilter implements DependencyTypeFilter { @Override public boolean match(DependencyType type) { - if (knownTypes.get(type.index)) { - return cache.get(type.index); + if (knownTypes != null) { + if (knownTypes.get(type.index)) { + return cache.get(type.index); + } + boolean result = predicate.test(type.getName(), false); + knownTypes.set(type.index); + cache.set(type.index, result); + return result; } - boolean result = predicate.test(type.getName(), false); - knownTypes.set(type.index); - cache.set(type.index, result); - return result; + + if (smallCache == null) { + smallCache = new IntIntHashMap(); + } + + var result = smallCache.getOrDefault(type.index, -1); + if (result != -1) { + return result != 0; + } + + var value = predicate.test(type.getName(), false); + smallCache.put(type.index, value ? 1 : 0); + if (smallCache.size() > SMALL_CACHE_THRESHOLD) { + knownTypes = new BitSet(); + cache = new BitSet(); + for (var entry : smallCache) { + knownTypes.set(entry.key); + if (entry.value != 0) { + cache.set(entry.key); + } + } + smallCache = null; + } + return value; } } diff --git a/core/src/main/java/org/teavm/dependency/VirtualCallConsumer.java b/core/src/main/java/org/teavm/dependency/VirtualCallConsumer.java index 6b3b2ec7f..7b7f454c8 100644 --- a/core/src/main/java/org/teavm/dependency/VirtualCallConsumer.java +++ b/core/src/main/java/org/teavm/dependency/VirtualCallConsumer.java @@ -15,17 +15,21 @@ */ package org.teavm.dependency; +import com.carrotsearch.hppc.IntHashSet; +import com.carrotsearch.hppc.IntSet; import java.util.BitSet; import org.teavm.model.CallLocation; import org.teavm.model.MethodDescriptor; class VirtualCallConsumer implements DependencyConsumer { + private static int SMALL_TYPES_THRESHOLD = 16; private final MethodDescriptor methodDesc; private final DependencyAnalyzer analyzer; private final DependencyNode[] parameters; private final DependencyNode result; private final CallLocation location; - private final BitSet knownTypes = new BitSet(); + private IntSet smallKnownTypes; + private BitSet knownTypes; private DependencyGraphBuilder.ExceptionConsumer exceptionConsumer; private DependencyTypeFilter filter; private boolean isPolymorphic; @@ -43,16 +47,40 @@ class VirtualCallConsumer implements DependencyConsumer { this.exceptionConsumer = exceptionConsumer; } + private boolean addKnownType(int index) { + if (knownTypes != null) { + if (knownTypes.get(index)) { + return false; + } else { + knownTypes.set(index); + return true; + } + } + if (smallKnownTypes == null) { + smallKnownTypes = new IntHashSet(); + } + if (smallKnownTypes.add(index)) { + if (smallKnownTypes.size() > SMALL_TYPES_THRESHOLD) { + knownTypes = new BitSet(); + for (var cursor : smallKnownTypes) { + knownTypes.set(cursor.value); + } + smallKnownTypes = null; + } + return true; + } + return false; + } + @Override public void consume(DependencyType type) { if (!filter.match(type)) { return; } - if (knownTypes.get(type.index)) { + if (!addKnownType(type.index)) { return; } - knownTypes.set(type.index); String className = type.getName();