From 017784d24f5750436d453441c4b32576eba6e094 Mon Sep 17 00:00:00 2001 From: Marco Pennekamp Date: Tue, 25 Jul 2023 18:39:44 +0200 Subject: [PATCH] [LL FIR] KT-60570 Use `emptySet` in Kotlin symbol names provider to conserve memory - As mentioned in KT-60570, memory consumption in cached symbol names providers is caused partially by lots of instances of empty mutable sets. This commit replaces such sets with the `emptySet()` singleton. - This improvement also makes sense for callable names because the IDE "package names with top-level callables" set currently contains all package names in the project. - In my local tests of the `setUp` Find Usages performance test, the retained size of `FirDelegatingCachedSymbolNamesProvider` is cut down from 208MB to 170MB by the empty class name set optimization, and from 170MB to 115MB by the empty callable name set optimization. --- .../utils/collections/collectionUtils.kt | 16 ++++++++++++++++ .../fir/util/LLFirKotlinSymbolNamesProvider.kt | 5 +++-- 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/collections/collectionUtils.kt diff --git a/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/collections/collectionUtils.kt b/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/collections/collectionUtils.kt new file mode 100644 index 00000000000..453ad9fc6aa --- /dev/null +++ b/analysis/analysis-internal-utils/src/org/jetbrains/kotlin/analysis/utils/collections/collectionUtils.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.analysis.utils.collections + +/** + * Maps all elements of this non-empty collection with the given [transform] function to a new mutable set, or returns [emptySet] if this + * collection is empty. + * + * [mapToSet] should be preferred over `collection.mapTo(mutableSetOf()) { ... }` when `collection` may be empty and the resulting set may + * be cached, because [mapToSet] saves memory by avoiding the creation of an empty mutable set. + */ +public inline fun Collection.mapToSet(transform: (T) -> R): Set = + if (isNotEmpty()) mapTo(mutableSetOf(), transform) else emptySet() diff --git a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirKotlinSymbolNamesProvider.kt b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirKotlinSymbolNamesProvider.kt index 0a336379590..84afb90a3db 100644 --- a/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirKotlinSymbolNamesProvider.kt +++ b/analysis/low-level-api-fir/src/org/jetbrains/kotlin/analysis/low/level/api/fir/util/LLFirKotlinSymbolNamesProvider.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.analysis.low.level.api.fir.util import org.jetbrains.kotlin.analysis.providers.KotlinDeclarationProvider +import org.jetbrains.kotlin.analysis.utils.collections.mapToSet import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolNamesProvider import org.jetbrains.kotlin.fir.resolve.providers.FirDelegatingCachedSymbolNamesProvider @@ -22,13 +23,13 @@ internal open class LLFirKotlinSymbolNamesProvider( override fun getTopLevelClassifierNamesInPackage(packageFqName: FqName): Set = declarationProvider .getTopLevelKotlinClassLikeDeclarationNamesInPackage(packageFqName) - .mapTo(mutableSetOf()) { it.asString() } + .mapToSet { it.asString() } override fun getPackageNamesWithTopLevelCallables(): Set? = declarationProvider.computePackageSetWithTopLevelCallableDeclarations() override fun getTopLevelCallableNamesInPackage(packageFqName: FqName): Set = - declarationProvider.getTopLevelCallableNamesInPackage(packageFqName) + declarationProvider.getTopLevelCallableNamesInPackage(packageFqName).ifEmpty { emptySet() } companion object { fun cached(session: FirSession, declarationProvider: KotlinDeclarationProvider): FirCachedSymbolNamesProvider =