From 2f92ba525c4e499e31cbab0f4ae3989c40eb7f80 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 24 Jun 2016 12:11:15 +0200 Subject: [PATCH] O(1) smart pointer removal --- .../SmartPsiElementPointersTest.java | 21 ++- .../SmartPointerManagerImpl.java | 138 +++++++++--------- .../SmartPsiElementPointerImpl.java | 1 + 3 files changed, 86 insertions(+), 74 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java b/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java index 37ea37f6c233..b9d84f386d96 100644 --- a/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java @@ -58,10 +58,7 @@ import org.junit.Assert; import java.io.IOException; import java.lang.ref.SoftReference; -import java.util.Collections; -import java.util.List; -import java.util.Random; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; @PlatformTestCase.WrapInCommand @@ -915,4 +912,20 @@ public class SmartPsiElementPointersTest extends CodeInsightTestCase { assertEquals(psiClass.getNameIdentifier().getTextRange(), TextRange.create(range)); } + public void testManySmartPointersCreationDeletionPerformance() throws Exception { + String text = StringUtil.repeatSymbol(' ', 100000); + PsiFile file = createFile("a.txt", text); + + PlatformTestUtil.startPerformanceTest("", 2000, () -> { + List pointers = new ArrayList<>(); + for (int i = 0; i < text.length() - 1; i++) { + pointers.add(getPointerManager().createSmartPsiFileRangePointer(file, new TextRange(i, i + 1))); + } + Collections.shuffle(pointers); + for (SmartPsiFileRange pointer : pointers) { + getPointerManager().removePointer(pointer); + } + }).cpuBound().assertTiming(); + } + } diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java index d18fb3ed192c..28a99999203e 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java @@ -29,7 +29,7 @@ import com.intellij.psi.*; import com.intellij.psi.impl.PsiDocumentManagerBase; import com.intellij.psi.util.PsiUtilCore; import com.intellij.reference.SoftReference; -import com.intellij.util.ArrayUtil; +import com.intellij.util.CommonProcessors; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -68,7 +68,7 @@ public class SmartPointerManagerImpl extends SmartPointerManager { FilePointersList pointers = reference.file.getUserData(reference.key); if (pointers != null) { - pointers.remove(reference); + pointers.removeReference(reference); } } } @@ -160,12 +160,13 @@ public class SmartPointerManagerImpl extends SmartPointerManager { SmartPointerElementInfo info = pointer.getElementInfo(); if (!(info instanceof SelfElementInfo)) return; + PointerReference reference = new PointerReference(pointer, containingFile, POINTERS_KEY); while (true) { FilePointersList pointers = getPointers(containingFile); if (pointers == null) { pointers = containingFile.putUserDataIfAbsent(POINTERS_KEY, new FilePointersList()); } - if (pointers.add(new PointerReference(pointer, containingFile, ourQueue, POINTERS_KEY))) { + if (pointers.add(reference)) { if (((SelfElementInfo)info).hasRange()) { pointers.markerCache.rangeChanged(); } @@ -193,8 +194,10 @@ public class SmartPointerManagerImpl extends SmartPointerManager { if (containingFile == null) return; VirtualFile vFile = containingFile.getViewProvider().getVirtualFile(); FilePointersList pointers = getPointers(vFile); - if (pointers == null) return; - pointers.remove(pointer); + PointerReference reference = ((SmartPsiElementPointerImpl)pointer).pointerReference; + if (pointers != null && reference != null) { + pointers.removeReference(reference); + } } } @@ -264,17 +267,18 @@ public class SmartPointerManagerImpl extends SmartPointerManager { return myPsiDocManager; } - private static class PointerReference extends WeakReference { + static class PointerReference extends WeakReference { @NotNull private final VirtualFile file; @NotNull private final Key key; + private int index = -2; private PointerReference(@NotNull SmartPsiElementPointerImpl pointer, @NotNull VirtualFile containingFile, - @NotNull ReferenceQueue queue, @NotNull Key key) { - super(pointer, queue); + super(pointer, ourQueue); file = containingFile; this.key = key; + pointer.pointerReference = this; } } @@ -292,104 +296,98 @@ public class SmartPointerManagerImpl extends SmartPointerManager { } if (nextAvailableIndex >= references.length || nextAvailableIndex > size*2) { // overflow or too many dead refs - int newCapacity = nextAvailableIndex >= references.length ? references.length * 3/2 +1 : size * 3/2+1; - PointerReference[] newReferences = new PointerReference[newCapacity]; + int newCapacity = (nextAvailableIndex >= references.length ? references.length : size) * 3 / 2 + 1; + final PointerReference[] newReferences = new PointerReference[newCapacity]; - int o = 0; - for (PointerReference oldRef : references) { - if (SoftReference.dereference(oldRef) != null) { - newReferences[o++] = oldRef; + final int[] o = {0}; + processAlivePointers(new Processor() { + @Override + public boolean process(SmartPsiElementPointerImpl pointer) { + storePointerReference(newReferences, o[0]++, pointer.pointerReference); + return true; } - } + }); references = newReferences; - size = nextAvailableIndex = o; + size = nextAvailableIndex = o[0]; } - references[nextAvailableIndex++] = reference; + assert references[nextAvailableIndex] == null : references[nextAvailableIndex]; + storePointerReference(references, nextAvailableIndex++, reference); size++; mySorted = false; return true; } - private synchronized void remove(@NotNull PointerReference reference) { - int index = ArrayUtil.indexOf(references, reference, 0, nextAvailableIndex); - if (index != -1) { - removeReference(reference, index); - } - } + private synchronized void removeReference(@NotNull PointerReference reference) { + int index = reference.index; + if (index < 0) return; - private synchronized void remove(@NotNull SmartPsiElementPointer smartPointer) { - for (int i = 0; i < nextAvailableIndex; i++) { - PointerReference reference = references[i]; - if (reference != null && reference.get() == smartPointer) { - removeReference(reference, i); - return; - } - } - } - - private void removeReference(@NotNull PointerReference reference, int index) { + assert references[index] == reference : "At " + index + " expected " + reference + ", found " + references[index]; + references[index].index = -1; references[index] = null; if (--size == 0) { reference.file.replace(reference.key, this, null); } } - boolean processAlivePointers(@NotNull Processor processor) { + synchronized boolean processAlivePointers(@NotNull Processor processor) { for (int i = 0; i < nextAvailableIndex; i++) { - SmartPsiElementPointerImpl pointer = SoftReference.dereference(references[i]); - if (pointer != null && !processor.process(pointer)) { + PointerReference ref = references[i]; + if (ref == null) continue; + + SmartPsiElementPointerImpl pointer = ref.get(); + if (pointer == null) { + removeReference(ref); + continue; + } + + if (!processor.process(pointer)) { return false; } } return true; } - synchronized List getSortedInfos() { + private void ensureSorted() { if (!mySorted) { - List hardRefs = ContainerUtil.newArrayListWithCapacity(size); - for (int i = 0; i < nextAvailableIndex; i++) { - PointerReference reference = references[i]; - if (reference == null) continue; + List pointers = new ArrayList(); + processAlivePointers(new CommonProcessors.CollectProcessor(pointers)); + assert size == pointers.size(); - SmartPsiElementPointerImpl pointer = reference.get(); - if (pointer != null) { - hardRefs.add(pointer); - } - else { - removeReference(reference, i); - if (size == 0) { - return Collections.emptyList(); - } - } - } - assert size == hardRefs.size(); - - Arrays.sort(references, 0, nextAvailableIndex, new Comparator() { + Collections.sort(pointers, new Comparator() { @Override - public int compare(PointerReference o1, PointerReference o2) { - SmartPsiElementPointerImpl p1 = SoftReference.dereference(o1); - SmartPsiElementPointerImpl p2 = SoftReference.dereference(o2); - if (p1 == null || p2 == null) { - return p1 != null ? -1 : p2 != null ? 1 : 0; // null references to the end - } + public int compare(SmartPsiElementPointerImpl p1, SmartPsiElementPointerImpl p2) { return MarkerCache.INFO_COMPARATOR.compare((SelfElementInfo)p1.getElementInfo(), (SelfElementInfo)p2.getElementInfo()); } }); - nextAvailableIndex = hardRefs.size(); + + for (int i = 0; i < pointers.size(); i++) { + storePointerReference(references, i, pointers.get(i).pointerReference); + } + Arrays.fill(references, pointers.size(), nextAvailableIndex, null); + nextAvailableIndex = pointers.size(); mySorted = true; } + } - List infos = ContainerUtil.newArrayListWithCapacity(size); - for (int i = 0; i < nextAvailableIndex; i++) { - Reference reference = references[i]; - SmartPsiElementPointerImpl pointer = SoftReference.dereference(reference); - if (pointer != null) { + private static void storePointerReference(PointerReference[] references, int index, PointerReference ref) { + references[index] = ref; + ref.index = index; + } + + synchronized List getSortedInfos() { + ensureSorted(); + + final List infos = ContainerUtil.newArrayListWithCapacity(size); + processAlivePointers(new Processor() { + @Override + public boolean process(SmartPsiElementPointerImpl pointer) { SelfElementInfo info = (SelfElementInfo)pointer.getElementInfo(); - if (!info.hasRange()) break; + if (!info.hasRange()) return false; infos.add(info); + return true; } - } + }); return infos; } diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java index ee7c4c9c249a..01e43e3b8d35 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java @@ -46,6 +46,7 @@ class SmartPsiElementPointerImpl implements SmartPointerEx private final SmartPointerElementInfo myElementInfo; private final Class myElementClass; private byte myReferenceCount = 1; + @Nullable SmartPointerManagerImpl.PointerReference pointerReference; SmartPsiElementPointerImpl(@NotNull Project project, @NotNull E element, @Nullable PsiFile containingFile, boolean forInjected) { this(element, createElementInfo(project, element, containingFile, forInjected), element.getClass());