Optimize memory usage in SlicedMapImpl (used in BindingContext)

In short:
before these changes: Map<SlicedMapKey<slice, key>, value>
after: Map<key, Map<slice, value>> // where for nested Map used lightweight storage UserDataHolder

Before these changes it stored map from SlicedMapKey to value.
Where SlicedMapKey created for each record and store slice and key.
And now it sored map from key to UserDataHolder.
UserDataHolder store map from slice to value.
This commit is contained in:
Zalim Bashorov
2015-05-12 14:32:06 +03:00
parent c7175e1858
commit ecb824386e
11 changed files with 154 additions and 152 deletions
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.codegen;
import com.intellij.openapi.util.Pair;
import kotlin.Function3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
@@ -28,7 +29,6 @@ import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.commons.Method;
import java.util.Collection;
import java.util.Map;
public final class JvmSerializationBindings {
public static final SerializationMappingSlice<FunctionDescriptor, Method> METHOD_FOR_FUNCTION =
@@ -82,13 +82,18 @@ public final class JvmSerializationBindings {
@NotNull
public static JvmSerializationBindings union(@NotNull Collection<JvmSerializationBindings> bindings) {
MutableSlicedMap result = SlicedMapImpl.create();
final MutableSlicedMap result = SlicedMapImpl.create();
for (JvmSerializationBindings binding : bindings) {
for (Map.Entry<SlicedMapKey<?, ?>, ?> entry : binding.map) {
SlicedMapKey<?, ?> key = entry.getKey();
result.put((WritableSlice) key.getSlice(), key.getKey(), entry.getValue());
}
binding.map.forEach(new Function3<WritableSlice, Object, Object, Void>() {
@Override
public Void invoke(WritableSlice slice, Object key, Object value) {
result.put(slice, key, value);
return null;
}
});
}
return new JvmSerializationBindings(result);
}
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import kotlin.Function3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
@@ -161,19 +162,17 @@ public class DelegatingBindingTrace implements BindingTrace {
clear();
}
public void addAllMyDataTo(@NotNull BindingTrace trace, @Nullable TraceEntryFilter filter, boolean commitDiagnostics) {
for (Map.Entry<SlicedMapKey<?, ?>, ?> entry : map) {
SlicedMapKey slicedMapKey = entry.getKey();
public void addAllMyDataTo(@NotNull final BindingTrace trace, @Nullable final TraceEntryFilter filter, boolean commitDiagnostics) {
map.forEach(new Function3<WritableSlice, Object, Object, Void>() {
@Override
public Void invoke(WritableSlice slice, Object key, Object value) {
if (filter == null || filter.accept(slice, key)) {
trace.record(slice, key, value);
}
WritableSlice slice = slicedMapKey.getSlice();
Object key = slicedMapKey.getKey();
Object value = entry.getValue();
if (filter == null || filter.accept(slice, key)) {
//noinspection unchecked
trace.record(slice, key, value);
return null;
}
}
});
if (!commitDiagnostics) return;
@@ -16,12 +16,13 @@
package org.jetbrains.kotlin.util.slicedMap;
import com.intellij.openapi.util.Key;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class BasicWritableSlice<K, V> implements WritableSlice<K, V> {
public class BasicWritableSlice<K, V> extends Key<V> implements WritableSlice<K, V> {
public static Void initSliceDebugNames(Class<?> declarationOwner) {
for (Field field : declarationOwner.getFields()) {
@@ -48,13 +49,16 @@ public class BasicWritableSlice<K, V> implements WritableSlice<K, V> {
}
public BasicWritableSlice(RewritePolicy rewritePolicy, boolean isCollective) {
super("<BasicWritableSlice>");
this.rewritePolicy = rewritePolicy;
this.isCollective = isCollective;
}
@Override
public SlicedMapKey<K, V> makeKey(K key) {
return new SlicedMapKey<K, V>(this, key);
@NotNull
public Key<V> getKey() {
return this;
}
// True to put, false to skip
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.util.slicedMap;
import com.intellij.openapi.util.Key;
import org.jetbrains.annotations.NotNull;
/**
@@ -49,8 +50,9 @@ public class DelegatingSlice<K, V> implements WritableSlice<K, V> {
}
@Override
public SlicedMapKey<K, V> makeKey(K key) {
return delegate.makeKey(key);
@NotNull
public Key<V> getKey() {
return delegate.getKey();
}
@Override
@@ -16,8 +16,12 @@
package org.jetbrains.kotlin.util.slicedMap;
import com.intellij.openapi.util.Key;
import org.jetbrains.annotations.NotNull;
public interface ReadOnlySlice<K, V> {
SlicedMapKey<K, V> makeKey(K key);
@NotNull
Key<V> getKey();
V computeValue(SlicedMap map, K key, V value, boolean valueNotFound);
@@ -16,12 +16,13 @@
package org.jetbrains.kotlin.util.slicedMap;
import kotlin.Function3;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
public interface SlicedMap extends Iterable<Map.Entry<SlicedMapKey<?, ?>, ?>> {
public interface SlicedMap {
SlicedMap DO_NOTHING = new SlicedMap() {
@Override
@@ -35,8 +36,7 @@ public interface SlicedMap extends Iterable<Map.Entry<SlicedMapKey<?, ?>, ?>> {
}
@Override
public Iterator<Map.Entry<SlicedMapKey<?, ?>, ?>> iterator() {
return Collections.<Map.Entry<SlicedMapKey<?, ?>, ?>>emptySet().iterator();
public void forEach(@NotNull Function3<WritableSlice, Object, Object, Void> f) {
}
};
@@ -44,4 +44,6 @@ public interface SlicedMap extends Iterable<Map.Entry<SlicedMapKey<?, ?>, ?>> {
// slice.isCollective() must return true
<K, V> Collection<K> getKeys(WritableSlice<K, V> slice);
void forEach(@NotNull Function3<WritableSlice, Object, Object, Void> f);
}
@@ -18,47 +18,48 @@ package org.jetbrains.kotlin.util.slicedMap;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.UserDataHolder;
import gnu.trove.THashMap;
import gnu.trove.TIntObjectHashMap;
import kotlin.Function3;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
public class SlicedMapImpl implements MutableSlicedMap {
private static final TIntObjectHashMap<WritableSlice> keyIdToSlice = new TIntObjectHashMap<WritableSlice>();
public static SlicedMapImpl create() {
return new SlicedMapImpl(Maps.<SlicedMapKey<?, ?>, Object>newLinkedHashMap());
return new SlicedMapImpl();
}
public static SlicedMapImpl create(Map<SlicedMapKey<?, ?>, Object> map) {
return new SlicedMapImpl(map);
}
public static SlicedMapImpl create(MapSupplier mapSupplier) {
return new SlicedMapImpl(mapSupplier.<SlicedMapKey<?, ?>, Object>get());
}
private final Map<SlicedMapKey<?, ?>, Object> map;
private final Map<Object, UserDataHolderImpl> map = new THashMap<Object, UserDataHolderImpl>(0);
private final Multimap<WritableSlice<?, ?>, Object> collectiveSliceKeys = ArrayListMultimap.create();
protected SlicedMapImpl(Map<SlicedMapKey<?, ?>, Object> map) {
this.map = map;
}
@Override
public <K, V> void put(WritableSlice<K, V> slice, K key, V value) {
if (!slice.check(key, value)) {
return;
}
SlicedMapKey<K, V> slicedMapKey = slice.makeKey(key);
UserDataHolderImpl holder = map.get(key);
if (holder == null) {
holder = new UserDataHolderImpl();
map.put(key, holder);
}
Key<V> sliceKey = slice.getKey();
RewritePolicy rewritePolicy = slice.getRewritePolicy();
if (rewritePolicy.rewriteProcessingNeeded(key)) {
if (map.containsKey(slicedMapKey)) {
V oldValue = holder.getUserData(sliceKey);
if (oldValue != null) {
//noinspection unchecked
if (!rewritePolicy.processRewrite(slice, key, (V) map.get(slicedMapKey), value)) {
if (!rewritePolicy.processRewrite(slice, key, oldValue, value)) {
return;
}
}
@@ -68,7 +69,12 @@ public class SlicedMapImpl implements MutableSlicedMap {
collectiveSliceKeys.put(slice, key);
}
map.put(slicedMapKey, value);
int sliceKeyId = sliceKey.hashCode();
if (!keyIdToSlice.contains(sliceKeyId)) {
keyIdToSlice.put(sliceKeyId, slice);
}
holder.putUserData(sliceKey, value);
slice.afterPut(this, key, value);
}
@@ -79,10 +85,11 @@ public class SlicedMapImpl implements MutableSlicedMap {
@Override
public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
SlicedMapKey<K, V> slicedMapKey = slice.makeKey(key);
//noinspection unchecked
V value = (V) map.get(slicedMapKey);
return slice.computeValue(this, key, value, value == null && !map.containsKey(slicedMapKey));
UserDataHolderImpl holder = map.get(key);
V value = holder == null ? null : holder.getUserData(slice.getKey());
return slice.computeValue(this, key, value, value == null);
}
@Override
@@ -94,24 +101,53 @@ public class SlicedMapImpl implements MutableSlicedMap {
@Override
public <K, V> V remove(RemovableSlice<K, V> slice, K key) {
//noinspection unchecked
return (V) map.remove(slice.makeKey(key));
UserDataHolderImpl holder = map.get(key);
if (holder == null) return null;
Key<V> sliceKey = slice.getKey();
V value = holder.getUserData(sliceKey);
holder.putUserData(sliceKey, null);
if (holder.isUserDataEmpty()) {
map.remove(key);
}
return value;
}
@NotNull
@Override
public Iterator<Map.Entry<SlicedMapKey<?, ?>, ?>> iterator() {
//noinspection unchecked
return (Iterator) map.entrySet().iterator();
public void forEach(@NotNull Function3<WritableSlice, Object, Object, Void> f) {
for (Map.Entry<Object, UserDataHolderImpl> entry : map.entrySet()) {
Object key = entry.getKey();
UserDataHolderImpl holder = entry.getValue();
if (holder == null) continue;
for (Key<?> sliceKey : holder.getKeys()) {
WritableSlice slice = keyIdToSlice.get(sliceKey.hashCode());
Object value = holder.getUserData(sliceKey);
f.invoke(slice, key, value);
}
}
}
@NotNull
@Override
public <K, V> ImmutableMap<K, V> getSliceContents(@NotNull ReadOnlySlice<K, V> slice) {
ImmutableMap.Builder<K, V> builder = ImmutableMap.builder();
for (Map.Entry<SlicedMapKey<?, ?>, ?> entry : map.entrySet()) {
if (entry.getKey().getSlice() == slice) {
builder.put((K) entry.getKey().getKey(), (V) entry.getValue());
for (Map.Entry<Object, UserDataHolderImpl> entry : map.entrySet()) {
UserDataHolder holder = entry.getValue();
V value = holder.getUserData(slice.getKey());
if (value != null) {
//noinspection unchecked
builder.put((K) entry.getKey(), value);
}
}
return builder.build();
@@ -1,63 +0,0 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.kotlin.util.slicedMap;
import org.jetbrains.annotations.NotNull;
public final class SlicedMapKey<K, V> {
private final WritableSlice<K, V> slice;
private final K key;
public SlicedMapKey(@NotNull WritableSlice<K, V> slice, K key) {
this.slice = slice;
this.key = key;
}
public WritableSlice<K, V> getSlice() {
return slice;
}
public K getKey() {
return key;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SlicedMapKey)) return false;
SlicedMapKey that = (SlicedMapKey) o;
if (key != null ? !key.equals(that.key) : that.key != null) return false;
if (!slice.equals(that.slice)) return false;
return true;
}
@Override
public int hashCode() {
int result = slice.hashCode();
result = 31 * result + (key != null ? key.hashCode() : 0);
return result;
}
@Override
public String toString() {
return slice + " -> " + key;
}
}
@@ -186,14 +186,6 @@ public class Slices {
map.put(opposite, value, key);
}
}
@Override
public SlicedMapKey<K, V> makeKey(K key) {
if (keyNormalizer == null) {
return super.makeKey(key);
}
return super.makeKey(keyNormalizer.normalize(key));
}
}
public static class SetSlice<K> extends BasicRemovableSlice<K, Boolean> {
@@ -18,12 +18,13 @@ package org.jetbrains.kotlin.util.slicedMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.intellij.openapi.util.Key;
import kotlin.Function3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.kotlin.utils.Printer;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
public class TrackingSlicedMap extends SlicedMapImpl {
@@ -31,7 +32,6 @@ public class TrackingSlicedMap extends SlicedMapImpl {
private final boolean trackWithStackTraces;
public TrackingSlicedMap(boolean trackWithStackTraces) {
super(Maps.<SlicedMapKey<?, ?>, Object>newLinkedHashMap());
this.trackWithStackTraces = trackWithStackTraces;
}
@@ -55,21 +55,15 @@ public class TrackingSlicedMap extends SlicedMapImpl {
return super.getKeys(wrapSlice(slice));
}
@NotNull
@Override
public Iterator<Map.Entry<SlicedMapKey<?, ?>, ?>> iterator() {
Map<SlicedMapKey<?, ?>, Object> map = Maps.newHashMap();
Iterator<Map.Entry<SlicedMapKey<?, ?>, ?>> iterator = super.iterator();
//noinspection WhileLoopReplaceableByForEach
while (iterator.hasNext()) {
Map.Entry<SlicedMapKey<?, ?>, ?> entry = iterator.next();
map.put(entry.getKey(), ((TrackableValue<?>) entry.getValue()).value);
}
//noinspection unchecked
return (Iterator) map.entrySet().iterator();
public void forEach(@NotNull final Function3<WritableSlice, Object, Object, Void> f) {
super.forEach(new Function3<WritableSlice, Object, Object, Void>() {
@Override
public Void invoke(WritableSlice slice, Object key, Object value) {
f.invoke(slice, key, ((TrackableValue<?>) value).value);
return null;
}
});
}
@Override
@@ -143,20 +137,21 @@ public class TrackingSlicedMap extends SlicedMapImpl {
}
}
public class SliceWithStackTrace<K, V> implements RemovableSlice<K, TrackableValue<V>> {
private class SliceWithStackTrace<K, V> extends Key<TrackableValue<V>> implements RemovableSlice<K, TrackableValue<V>> {
private final ReadOnlySlice<K, V> delegate;
private SliceWithStackTrace(@NotNull ReadOnlySlice<K, V> delegate) {
super(delegate.toString());
this.delegate = delegate;
}
// Methods of ReadOnlySlice
@Override
public SlicedMapKey<K, TrackableValue<V>> makeKey(K key) {
//noinspection unchecked
return (SlicedMapKey) delegate.makeKey(key);
@NotNull
public Key<TrackableValue<V>> getKey() {
return this;
}
@Override
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* 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 org.jetbrains.kotlin.util.slicedMap
import com.intellij.openapi.util.Key
import org.jetbrains.kotlin.util.userDataHolder.UserDataHolderBase
import org.jetbrains.kotlin.util.userDataHolder.keyFMap.KeyFMap
class UserDataHolderImpl : UserDataHolderBase() {
val keys: Array<Key<*>>
get() = getUserMap().getKeys()
}