From e2109c3f8f20065dca5363b1353334b2838d1314 Mon Sep 17 00:00:00 2001 From: Matthew Gharrity Date: Tue, 12 Jan 2021 01:51:08 +0100 Subject: [PATCH] ShadowedDeclarationsFilter: check for equivalence If there are multiple copies of the same library on the classpath, then ShadowedDeclarationsFilter becomes very slow because it encounters many equal-signature declarations and thus has to resolve a lot of calls in order to pick among them. Having multiple copies of the same library on the classpath is somewhat common in real-world projects. It occurs in the JetBrains/intellij-kotlin project, for example. In that project, ShadowedDeclarationsFilter ends up resolving thousands of calls, accounting for around 80% of completion time when there are many completion results (see KT-44276). We can optimize ShadowedDeclarationsFilter by checking whether the descriptors in an equal-signature group are structurally equivalent. If they are, we can just pick one rather than running resolve. Testing on a small project with Kotlin stdlib duplicated on the classpath, this change reduces overhead in ShadowedDeclarationsFilter from 1200 ms to 20 ms when running completion on the prefix 'a'. End-to-end completion time is cut in half. Test: JvmBasicCompletionTestGenerated.Common.Shadowing --- .../kotlin/idea/util/ShadowedDeclarationsFilter.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/scripting/scripting-ide-common/src/org/jetbrains/kotlin/idea/util/ShadowedDeclarationsFilter.kt b/plugins/scripting/scripting-ide-common/src/org/jetbrains/kotlin/idea/util/ShadowedDeclarationsFilter.kt index 9b8f2b0083d..ca4a720591b 100644 --- a/plugins/scripting/scripting-ide-common/src/org/jetbrains/kotlin/idea/util/ShadowedDeclarationsFilter.kt +++ b/plugins/scripting/scripting-ide-common/src/org/jetbrains/kotlin/idea/util/ShadowedDeclarationsFilter.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.idea.resolve.getLanguageVersionSettings import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DelegatingBindingTrace +import org.jetbrains.kotlin.resolve.DescriptorEquivalenceForOverrides import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore import org.jetbrains.kotlin.resolve.calls.CallResolver import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext @@ -87,6 +88,12 @@ class ShadowedDeclarationsFilter( return listOf(first) } + // Optimization: if the descriptors are structurally equivalent then there is no need to run resolve. + // This can happen when the classpath contains multiple copies of the same library. + if (descriptors.all { DescriptorEquivalenceForOverrides.areEquivalent(first, it, allowCopiesFromTheSameDeclaration = true) }) { + return listOf(first) + } + val isFunction = first is FunctionDescriptor val name = when (first) { is ConstructorDescriptor -> first.constructedClass.name