From ecb824386e4f525dfea07a55f65f248794a3a503 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Tue, 12 May 2015 14:32:06 +0300 Subject: [PATCH] Optimize memory usage in SlicedMapImpl (used in BindingContext) In short: before these changes: Map, value> after: Map> // 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. --- .../codegen/JvmSerializationBindings.java | 17 +-- .../resolve/DelegatingBindingTrace.java | 21 ++-- .../util/slicedMap/BasicWritableSlice.java | 10 +- .../util/slicedMap/DelegatingSlice.java | 6 +- .../kotlin/util/slicedMap/ReadOnlySlice.java | 6 +- .../kotlin/util/slicedMap/SlicedMap.java | 12 ++- .../kotlin/util/slicedMap/SlicedMapImpl.java | 102 ++++++++++++------ .../kotlin/util/slicedMap/SlicedMapKey.java | 63 ----------- .../kotlin/util/slicedMap/Slices.java | 8 -- .../util/slicedMap/TrackingSlicedMap.java | 35 +++--- .../util/slicedMap/UserDataHolderImpl.kt | 26 +++++ 11 files changed, 154 insertions(+), 152 deletions(-) delete mode 100644 compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/SlicedMapKey.java create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/UserDataHolderImpl.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmSerializationBindings.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmSerializationBindings.java index 84e2889feda..35c6c550854 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmSerializationBindings.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmSerializationBindings.java @@ -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 METHOD_FOR_FUNCTION = @@ -82,13 +82,18 @@ public final class JvmSerializationBindings { @NotNull public static JvmSerializationBindings union(@NotNull Collection bindings) { - MutableSlicedMap result = SlicedMapImpl.create(); + final MutableSlicedMap result = SlicedMapImpl.create(); + for (JvmSerializationBindings binding : bindings) { - for (Map.Entry, ?> entry : binding.map) { - SlicedMapKey key = entry.getKey(); - result.put((WritableSlice) key.getSlice(), key.getKey(), entry.getValue()); - } + binding.map.forEach(new Function3() { + @Override + public Void invoke(WritableSlice slice, Object key, Object value) { + result.put(slice, key, value); + return null; + } + }); } + return new JvmSerializationBindings(result); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatingBindingTrace.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatingBindingTrace.java index d1adff35931..b45e7b9f3d7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatingBindingTrace.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatingBindingTrace.java @@ -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, ?> entry : map) { - SlicedMapKey slicedMapKey = entry.getKey(); + public void addAllMyDataTo(@NotNull final BindingTrace trace, @Nullable final TraceEntryFilter filter, boolean commitDiagnostics) { + map.forEach(new Function3() { + @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; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/BasicWritableSlice.java b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/BasicWritableSlice.java index 0e8fc7ff6e3..e08ec29385d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/BasicWritableSlice.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/BasicWritableSlice.java @@ -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 implements WritableSlice { +public class BasicWritableSlice extends Key implements WritableSlice { public static Void initSliceDebugNames(Class declarationOwner) { for (Field field : declarationOwner.getFields()) { @@ -48,13 +49,16 @@ public class BasicWritableSlice implements WritableSlice { } public BasicWritableSlice(RewritePolicy rewritePolicy, boolean isCollective) { + super(""); + this.rewritePolicy = rewritePolicy; this.isCollective = isCollective; } @Override - public SlicedMapKey makeKey(K key) { - return new SlicedMapKey(this, key); + @NotNull + public Key getKey() { + return this; } // True to put, false to skip diff --git a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/DelegatingSlice.java b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/DelegatingSlice.java index 71a69887ec7..6a58a3b0103 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/DelegatingSlice.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/DelegatingSlice.java @@ -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 implements WritableSlice { } @Override - public SlicedMapKey makeKey(K key) { - return delegate.makeKey(key); + @NotNull + public Key getKey() { + return delegate.getKey(); } @Override diff --git a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/ReadOnlySlice.java b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/ReadOnlySlice.java index d12b83b5331..2157f21a880 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/ReadOnlySlice.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/ReadOnlySlice.java @@ -16,8 +16,12 @@ package org.jetbrains.kotlin.util.slicedMap; +import com.intellij.openapi.util.Key; +import org.jetbrains.annotations.NotNull; + public interface ReadOnlySlice { - SlicedMapKey makeKey(K key); + @NotNull + Key getKey(); V computeValue(SlicedMap map, K key, V value, boolean valueNotFound); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/SlicedMap.java b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/SlicedMap.java index 23f2a261fb2..affc58d0c67 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/SlicedMap.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/SlicedMap.java @@ -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, ?>> { +public interface SlicedMap { SlicedMap DO_NOTHING = new SlicedMap() { @Override @@ -35,8 +36,7 @@ public interface SlicedMap extends Iterable, ?>> { } @Override - public Iterator, ?>> iterator() { - return Collections., ?>>emptySet().iterator(); + public void forEach(@NotNull Function3 f) { } }; @@ -44,4 +44,6 @@ public interface SlicedMap extends Iterable, ?>> { // slice.isCollective() must return true Collection getKeys(WritableSlice slice); + + void forEach(@NotNull Function3 f); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/SlicedMapImpl.java b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/SlicedMapImpl.java index fdf61422231..fc23f100d01 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/SlicedMapImpl.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/SlicedMapImpl.java @@ -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 keyIdToSlice = new TIntObjectHashMap(); + public static SlicedMapImpl create() { - return new SlicedMapImpl(Maps., Object>newLinkedHashMap()); + return new SlicedMapImpl(); } - public static SlicedMapImpl create(Map, Object> map) { - return new SlicedMapImpl(map); - } - - public static SlicedMapImpl create(MapSupplier mapSupplier) { - return new SlicedMapImpl(mapSupplier., Object>get()); - } - - private final Map, Object> map; + private final Map map = new THashMap(0); private final Multimap, Object> collectiveSliceKeys = ArrayListMultimap.create(); - protected SlicedMapImpl(Map, Object> map) { - this.map = map; - } - @Override public void put(WritableSlice slice, K key, V value) { if (!slice.check(key, value)) { return; } - SlicedMapKey slicedMapKey = slice.makeKey(key); + UserDataHolderImpl holder = map.get(key); + if (holder == null) { + holder = new UserDataHolderImpl(); + map.put(key, holder); + } + + Key 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 V get(ReadOnlySlice slice, K key) { - SlicedMapKey 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 V remove(RemovableSlice slice, K key) { - //noinspection unchecked - return (V) map.remove(slice.makeKey(key)); + UserDataHolderImpl holder = map.get(key); + + if (holder == null) return null; + + Key sliceKey = slice.getKey(); + V value = holder.getUserData(sliceKey); + + holder.putUserData(sliceKey, null); + + if (holder.isUserDataEmpty()) { + map.remove(key); + } + + return value; } - @NotNull @Override - public Iterator, ?>> iterator() { - //noinspection unchecked - return (Iterator) map.entrySet().iterator(); + public void forEach(@NotNull Function3 f) { + for (Map.Entry 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 ImmutableMap getSliceContents(@NotNull ReadOnlySlice slice) { ImmutableMap.Builder builder = ImmutableMap.builder(); - for (Map.Entry, ?> entry : map.entrySet()) { - if (entry.getKey().getSlice() == slice) { - builder.put((K) entry.getKey().getKey(), (V) entry.getValue()); + + for (Map.Entry 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(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/SlicedMapKey.java b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/SlicedMapKey.java deleted file mode 100644 index 1cefeff2a6c..00000000000 --- a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/SlicedMapKey.java +++ /dev/null @@ -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 { - - private final WritableSlice slice; - private final K key; - - public SlicedMapKey(@NotNull WritableSlice slice, K key) { - this.slice = slice; - this.key = key; - } - - public WritableSlice 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; - } -} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/Slices.java b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/Slices.java index 633e7064e5c..f5bcb047b1a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/Slices.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/Slices.java @@ -186,14 +186,6 @@ public class Slices { map.put(opposite, value, key); } } - @Override - public SlicedMapKey makeKey(K key) { - if (keyNormalizer == null) { - return super.makeKey(key); - } - return super.makeKey(keyNormalizer.normalize(key)); - } - } public static class SetSlice extends BasicRemovableSlice { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/TrackingSlicedMap.java b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/TrackingSlicedMap.java index acc5bf6042e..935608f8130 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/TrackingSlicedMap.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/TrackingSlicedMap.java @@ -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., Object>newLinkedHashMap()); this.trackWithStackTraces = trackWithStackTraces; } @@ -55,21 +55,15 @@ public class TrackingSlicedMap extends SlicedMapImpl { return super.getKeys(wrapSlice(slice)); } - @NotNull @Override - public Iterator, ?>> iterator() { - Map, Object> map = Maps.newHashMap(); - - Iterator, ?>> iterator = super.iterator(); - - //noinspection WhileLoopReplaceableByForEach - while (iterator.hasNext()) { - Map.Entry, ?> entry = iterator.next(); - map.put(entry.getKey(), ((TrackableValue) entry.getValue()).value); - } - - //noinspection unchecked - return (Iterator) map.entrySet().iterator(); + public void forEach(@NotNull final Function3 f) { + super.forEach(new Function3() { + @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 implements RemovableSlice> { + private class SliceWithStackTrace extends Key> implements RemovableSlice> { private final ReadOnlySlice delegate; private SliceWithStackTrace(@NotNull ReadOnlySlice delegate) { + super(delegate.toString()); this.delegate = delegate; } // Methods of ReadOnlySlice @Override - public SlicedMapKey> makeKey(K key) { - //noinspection unchecked - return (SlicedMapKey) delegate.makeKey(key); + @NotNull + public Key> getKey() { + return this; } @Override diff --git a/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/UserDataHolderImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/UserDataHolderImpl.kt new file mode 100644 index 00000000000..4c4d3f6d275 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/util/slicedMap/UserDataHolderImpl.kt @@ -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> + get() = getUserMap().getKeys() +}