From 95e305be3a3036a1f4dba0190ab440bf0a4691ed Mon Sep 17 00:00:00 2001 From: Alexander Korepanov Date: Fri, 28 Oct 2022 14:50:37 +0200 Subject: [PATCH] [JS IR] Simple memory optimizations - Use hash maps and hash sets instead of linked implementations - Use weak hash maps for caches with IR elements as keys - Intern JS function signatures --- .../kotlin/ir/backend/js/JsIrBackendContext.kt | 9 +++++---- .../kotlin/ir/backend/js/ic/CacheUpdater.kt | 18 +++++++++--------- .../kotlin/ir/backend/js/ic/ICUtils.kt | 4 ++-- .../backend/js/ic/IdSignatureHashCalculator.kt | 10 +++++----- .../ir/backend/js/ic/IncrementalCache.kt | 10 +++++----- .../ir/backend/js/ic/JsIrLinkerLoader.kt | 2 +- .../ir/backend/js/ic/JsMultiModuleCache.kt | 2 +- .../backend/js/ic/KotlinSourceFileMetadata.kt | 10 +++++----- .../transformers/irToJs/JsNameLinkingNamer.kt | 6 +++--- .../backend/js/utils/MinimizedNameGenerator.kt | 8 ++++---- .../kotlin/ir/backend/js/utils/NameTables.kt | 8 +++----- .../declarations/impl/IrFactoryImplForJsIC.kt | 7 ++++--- 12 files changed, 47 insertions(+), 47 deletions(-) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index 2bb3457c77d..fd651a5dfd9 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -52,6 +52,7 @@ import org.jetbrains.kotlin.types.isNullable import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceMapNotNull import org.jetbrains.kotlin.utils.addToStdlib.cast import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import java.util.WeakHashMap @OptIn(ObsoleteDescriptorBasedAPI::class) class JsIrBackendContext( @@ -73,9 +74,9 @@ class JsIrBackendContext( override val scriptMode: Boolean get() = false val polyfills = JsPolyfills() - val fieldToInitializer: MutableMap = mutableMapOf() + val fieldToInitializer = WeakHashMap() - val localClassNames: MutableMap = mutableMapOf() + val localClassNames = WeakHashMap() val minimizedNameGenerator: MinimizedNameGenerator = MinimizedNameGenerator() @@ -83,7 +84,7 @@ class JsIrBackendContext( val keeper: Keeper = Keeper(keep) - val fieldDataCache = mutableMapOf>() + val fieldDataCache = WeakHashMap>() override val builtIns = module.builtIns @@ -387,7 +388,7 @@ class JsIrBackendContext( print(message) } - private val outlinedJsCodeFunctions = mutableMapOf() + private val outlinedJsCodeFunctions = WeakHashMap() fun addOutlinedJsCode(symbol: IrSimpleFunctionSymbol, outlinedJsCode: JsFunction) { outlinedJsCodeFunctions[symbol] = outlinedJsCode diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/CacheUpdater.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/CacheUpdater.kt index 947b7487d3a..9b09a516f37 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/CacheUpdater.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/CacheUpdater.kt @@ -119,7 +119,7 @@ class CacheUpdater( ) : KotlinSourceFileExports() { override fun getExportedSignatures(): Set = allExportedSignatures - val allExportedSignatures = mutableSetOf() + val allExportedSignatures = hashSetOf() } private class DirtyFileMetadata( @@ -132,13 +132,13 @@ class CacheUpdater( ) : KotlinSourceFileMetadata() { fun addInverseDependency(lib: KotlinLibraryFile, src: KotlinSourceFile, signature: IdSignature) = when (val signatures = inverseDependencies[lib, src]) { - null -> inverseDependencies[lib, src] = mutableSetOf(signature) + null -> inverseDependencies[lib, src] = hashSetOf(signature) else -> signatures += signature } fun addDirectDependency(lib: KotlinLibraryFile, src: KotlinSourceFile, signature: IdSignature, hash: ICHash) = when (val signatures = directDependencies[lib, src]) { - null -> directDependencies[lib, src] = mutableMapOf(signature to hash) + null -> directDependencies[lib, src] = hashMapOf(signature to hash) else -> signatures[signature] = hash } } @@ -197,7 +197,7 @@ class CacheUpdater( } private fun loadModifiedFiles(): KotlinSourceFileMap { - val removedFilesMetadata = mutableMapOf>() + val removedFilesMetadata = hashMapOf>() val modifiedFiles = KotlinSourceFileMap(incrementalCaches.entries.associate { (lib, cache) -> val (dirtyFiles, removedFiles, newFiles, modifiedConfigFiles) = cache.collectModifiedFiles(configHash) @@ -228,7 +228,7 @@ class CacheUpdater( val exportedSymbols = KotlinSourceFileMutableMap() for ((libFile, srcFiles) in dirtyFiles) { - val exportedSymbolFiles = mutableMapOf() + val exportedSymbolFiles = HashMap(srcFiles.size) for ((srcFile, srcFileMetadata) in srcFiles) { val loadingFileExports = DirtyFileExports() for ((dependentLib, dependentFiles) in srcFileMetadata.inverseDependencies) { @@ -264,7 +264,7 @@ class CacheUpdater( } private fun collectImplementedSymbol(deserializedSymbols: Map): Map { - return buildMap(deserializedSymbols.size) { + return HashMap(deserializedSymbols.size).apply { for ((signature, symbol) in deserializedSymbols) { put(signature, symbol) @@ -351,7 +351,7 @@ class CacheUpdater( val incrementalCache = getLibIncrementalCache(libFile) for (fileDeserializer in moduleDeserializer.fileDeserializers()) { val reachableSignatures = fileDeserializer.symbolDeserializer.signatureDeserializer.signatureToIndexMapping() - val maybeImportedSignatures = reachableSignatures.keys.toMutableSet() + val maybeImportedSignatures = HashSet(reachableSignatures.keys) val implementedSymbols = collectImplementedSymbol(fileDeserializer.symbolDeserializer.deserializedSymbols) for ((signature, symbol) in implementedSymbols) { var symbolCanBeExported = maybeImportedSignatures.remove(signature) @@ -506,7 +506,7 @@ class CacheUpdater( dependentSignatures.keys != newSignatures -> { val newMetadata = addNewMetadata(dependentLibFile, dependentSrcFile, dependentSrcMetadata) - newMetadata.directDependencies[libFile, srcFile] = newSignatures.associateWith { + newMetadata.directDependencies[libFile, srcFile] = newSignatures.associateWithTo(HashMap(newSignatures.size)) { signatureHashCalculator[it] ?: notFoundIcError("signature $it hash", libFile, srcFile) } } @@ -571,7 +571,7 @@ class CacheUpdater( fragmentToLibName[it.irFile.module] ?: notFoundIcError("loaded fragment lib name", srcFile = KotlinSourceFile(it.irFile)) } - val visited = mutableSetOf() + val visited = hashSetOf() val artifacts = mutableListOf() fun addArtifact(lib: KotlinLibrary) { if (visited.add(lib)) { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/ICUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/ICUtils.kt index f45d75126f7..b05cc381fc5 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/ICUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/ICUtils.kt @@ -53,11 +53,11 @@ internal inline fun buildListUntil(to: Int, builderAction: MutableList.(I } internal inline fun buildSetUntil(to: Int, builderAction: MutableSet.(Int) -> Unit): Set { - return buildSet(to) { repeat(to) { builderAction(it) } } + return HashSet(to).apply { repeat(to) { builderAction(it) } } } internal inline fun buildMapUntil(to: Int, builderAction: MutableMap.(Int) -> Unit): Map { - return buildMap(to) { repeat(to) { builderAction(it) } } + return HashMap(to).apply { repeat(to) { builderAction(it) } } } internal class StopwatchIC { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IdSignatureHashCalculator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IdSignatureHashCalculator.kt index 74e2a58aef4..3712cd0da1d 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IdSignatureHashCalculator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IdSignatureHashCalculator.kt @@ -21,12 +21,12 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid class IdSignatureHashCalculator { - private val flatHashes = mutableMapOf() - private val inlineFunctionCallGraph: MutableMap> = mutableMapOf() - private val processingFunctions = mutableSetOf() - private val functionTransitiveHashes = mutableMapOf() + private val flatHashes = hashMapOf() + private val inlineFunctionCallGraph = hashMapOf>() + private val processingFunctions = hashSetOf() + private val functionTransitiveHashes = hashMapOf() - private val allIdSignatureHashes = mutableMapOf() + private val allIdSignatureHashes = hashMapOf() private inner class FlatHashCalculator : IrElementVisitorVoid { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IncrementalCache.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IncrementalCache.kt index ae03f0033dc..6c990e94055 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IncrementalCache.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/IncrementalCache.kt @@ -25,7 +25,7 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) { private var forceRebuildJs = false private val cacheDir = File(cachePath) - private val signatureToIndexMappingFromMetadata = mutableMapOf>() + private val signatureToIndexMappingFromMetadata = hashMapOf>() private val libraryFile = KotlinLibraryFile(library) @@ -69,7 +69,7 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) { override val directDependencies = KotlinSourceFileMap>(emptyMap()) } - private val kotlinLibrarySourceFileMetadata = mutableMapOf() + private val kotlinLibrarySourceFileMetadata = hashMapOf() private fun KotlinSourceFile.getCacheFile(suffix: String) = File(cacheDir, "${File(path).name}.${path.stringHashForIC()}.$suffix") @@ -123,11 +123,11 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) { } val cachedFingerprints = loadCachedFingerprints() - val deletedFiles = cachedFingerprints.keys.toMutableSet() + val deletedFiles = HashSet(cachedFingerprints.keys) val unknownFiles = mutableSetOf() val newFingerprints = kotlinLibraryHeader.sourceFiles.mapIndexed { index, file -> file to library.fingerprint(index) } - val modifiedFiles = buildMap(newFingerprints.size) { + val modifiedFiles = HashMap(newFingerprints.size).apply { for ((file, fileNewFingerprint) in newFingerprints) { val oldFingerprint = cachedFingerprints[file] if (oldFingerprint == null) { @@ -170,7 +170,7 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) { private fun fetchSourceFileMetadata(srcFile: KotlinSourceFile, loadSignatures: Boolean) = kotlinLibrarySourceFileMetadata.getOrPut(srcFile) { - val signatureToIndexMapping = signatureToIndexMappingFromMetadata.getOrPut(srcFile) { mutableMapOf() } + val signatureToIndexMapping = signatureToIndexMappingFromMetadata.getOrPut(srcFile) { hashMapOf() } val deserializer: IdSignatureDeserializer by lazy { kotlinLibraryHeader.signatureDeserializers[srcFile] ?: notFoundIcError("signature deserializer", libraryFile, srcFile) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsIrLinkerLoader.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsIrLinkerLoader.kt index ecd5dbfea9d..e4015ce2b7a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsIrLinkerLoader.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsIrLinkerLoader.kt @@ -55,7 +55,7 @@ internal class JsIrLinkerLoader( } private fun loadModules(): Map { - val descriptors = mutableMapOf() + val descriptors = hashMapOf() var runtimeModule: ModuleDescriptorImpl? = null // TODO: deduplicate this code using part from klib.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsMultiModuleCache.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsMultiModuleCache.kt index 4a9470e38e9..fe535703495 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsMultiModuleCache.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/JsMultiModuleCache.kt @@ -23,7 +23,7 @@ class JsMultiModuleCache(private val moduleArtifacts: List) { class CachedModuleInfo(val artifact: ModuleArtifact, val jsIrHeader: JsIrModuleHeader, var crossModuleReferencesHash: ICHash = ICHash()) - private val headerToCachedInfo = mutableMapOf() + private val headerToCachedInfo = hashMapOf() private fun ModuleArtifact.fetchModuleInfo() = File(artifactsDir, JS_MODULE_HEADER).useCodedInputIfExists { val definitions = mutableSetOf() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/KotlinSourceFileMetadata.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/KotlinSourceFileMetadata.kt index 4fa7b4ea119..d6c44f5b33c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/KotlinSourceFileMetadata.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ic/KotlinSourceFileMetadata.kt @@ -53,17 +53,17 @@ open class KotlinSourceFileMap(files: Map( - private val files: MutableMap> = mutableMapOf() + private val files: MutableMap> = hashMapOf() ) : KotlinSourceFileMap(files) { operator fun set(libFile: KotlinLibraryFile, sourceFile: KotlinSourceFile, data: T) = getOrPutFiles(libFile).put(sourceFile, data) operator fun set(libFile: KotlinLibraryFile, sourceFiles: MutableMap) = files.put(libFile, sourceFiles) - fun getOrPutFiles(libFile: KotlinLibraryFile) = files.getOrPut(libFile) { mutableMapOf() } + fun getOrPutFiles(libFile: KotlinLibraryFile) = files.getOrPut(libFile) { hashMapOf() } fun copyFilesFrom(other: KotlinSourceFileMap) { for ((libFile, srcFiles) in other) { - files.getOrPut(libFile) { mutableMapOf() } += srcFiles + files.getOrPut(libFile) { hashMapOf() } += srcFiles } } @@ -81,11 +81,11 @@ class KotlinSourceFileMutableMap( } fun KotlinSourceFileMap.toMutable(): KotlinSourceFileMutableMap { - return KotlinSourceFileMutableMap(entries.associateTo(mutableMapOf()) { it.key to it.value.toMutableMap() }) + return KotlinSourceFileMutableMap(entries.associateTo(HashMap(entries.size)) { it.key to HashMap(it.value) }) } fun KotlinSourceFileMap>.flatSignatures(): Set { - val allSignatures = mutableSetOf() + val allSignatures = hashSetOf() forEachFile { _, _, signatures -> allSignatures += signatures } return allSignatures } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsNameLinkingNamer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsNameLinkingNamer.kt index cce4037cd1f..a6a82b55445 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsNameLinkingNamer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsNameLinkingNamer.kt @@ -106,7 +106,7 @@ class JsNameLinkingNamer(private val context: JsIrBackendContext, private val mi private fun IrClass.fieldData(): Map { return context.fieldDataCache.getOrPut(this) { - val nameCnt = mutableMapOf() + val nameCnt = hashMapOf() val allClasses = DFS.topologicalOrder(listOf(this)) { node -> node.superTypes.mapNotNull { @@ -114,7 +114,7 @@ class JsNameLinkingNamer(private val context: JsIrBackendContext, private val mi } } - val result = mutableMapOf() + val result = hashMapOf() if (minimizedMemberNames) { allClasses.reversed().forEach { @@ -187,4 +187,4 @@ private fun List.makeRef(): JsNameRef { result = JsNameRef(this[i], result) } return result -} \ No newline at end of file +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/MinimizedNameGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/MinimizedNameGenerator.kt index f537b2fbb56..5df0400c9fa 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/MinimizedNameGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/MinimizedNameGenerator.kt @@ -7,9 +7,9 @@ package org.jetbrains.kotlin.ir.backend.js.utils class MinimizedNameGenerator { private var index = 0 - private val functionSignatureToName = mutableMapOf() - private val reservedNames = mutableSetOf() - private val keptNames = mutableSetOf() + private val functionSignatureToName = hashMapOf() + private val reservedNames = hashSetOf() + private val keptNames = hashSetOf() fun generateNextName(): String { var candidate = index++.toJsIdentifier() @@ -39,4 +39,4 @@ class MinimizedNameGenerator { functionSignatureToName.clear() reservedNames.clear() } -} \ No newline at end of file +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt index cb36ba4829c..b28f954df4c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/NameTables.kt @@ -149,13 +149,11 @@ fun calculateJsFunctionSignature(declaration: IrFunction, context: JsIrBackendCo } } - val signature = nameBuilder.toString() + val signature = abs(nameBuilder.toString().hashCode()).toString(Character.MAX_RADIX) // TODO: Use better hashCode - return sanitizeName( - declarationName, - withHash = false - ) + "_" + abs(signature.hashCode()).toString(Character.MAX_RADIX) + RESERVED_MEMBER_NAME_SUFFIX + val sanitizedName = sanitizeName(declarationName, withHash = false) + return "${sanitizedName}_$signature$RESERVED_MEMBER_NAME_SUFFIX".intern() } fun jsFunctionSignature( diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/impl/IrFactoryImplForJsIC.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/impl/IrFactoryImplForJsIC.kt index ea158c90fde..f7fdcb13467 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/impl/IrFactoryImplForJsIC.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/impl/IrFactoryImplForJsIC.kt @@ -13,14 +13,15 @@ import org.jetbrains.kotlin.ir.util.IdSignature import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource import org.jetbrains.kotlin.types.Variance +import java.util.* class IrFactoryImplForJsIC(override val stageController: StageController) : AbstractIrFactoryImpl(), IdSignatureRetriever { - private val declarationToSignature = mutableMapOf() + private val declarationToSignature = WeakHashMap() private fun T.register(): T { val parentSig = stageController.currentDeclaration?.let { declarationSignature(it) } ?: return this - stageController.createSignature(parentSig)?.let { declarationToSignature[this] = it} + stageController.createSignature(parentSig)?.let { declarationToSignature[this] = it } return this } @@ -392,4 +393,4 @@ class IrFactoryImplForJsIC(override val stageController: StageController) : Abst isAssignable, ).register() } -} \ No newline at end of file +}