[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
This commit is contained in:
Alexander Korepanov
2022-10-28 14:50:37 +02:00
committed by Space Team
parent 731dd9c3e8
commit 95e305be3a
12 changed files with 47 additions and 47 deletions
@@ -52,6 +52,7 @@ import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceMapNotNull import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceMapNotNull
import org.jetbrains.kotlin.utils.addToStdlib.cast import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.WeakHashMap
@OptIn(ObsoleteDescriptorBasedAPI::class) @OptIn(ObsoleteDescriptorBasedAPI::class)
class JsIrBackendContext( class JsIrBackendContext(
@@ -73,9 +74,9 @@ class JsIrBackendContext(
override val scriptMode: Boolean get() = false override val scriptMode: Boolean get() = false
val polyfills = JsPolyfills() val polyfills = JsPolyfills()
val fieldToInitializer: MutableMap<IrField, IrExpression> = mutableMapOf() val fieldToInitializer = WeakHashMap<IrField, IrExpression>()
val localClassNames: MutableMap<IrClass, String> = mutableMapOf() val localClassNames = WeakHashMap<IrClass, String>()
val minimizedNameGenerator: MinimizedNameGenerator = val minimizedNameGenerator: MinimizedNameGenerator =
MinimizedNameGenerator() MinimizedNameGenerator()
@@ -83,7 +84,7 @@ class JsIrBackendContext(
val keeper: Keeper = val keeper: Keeper =
Keeper(keep) Keeper(keep)
val fieldDataCache = mutableMapOf<IrClass, Map<IrField, String>>() val fieldDataCache = WeakHashMap<IrClass, Map<IrField, String>>()
override val builtIns = module.builtIns override val builtIns = module.builtIns
@@ -387,7 +388,7 @@ class JsIrBackendContext(
print(message) print(message)
} }
private val outlinedJsCodeFunctions = mutableMapOf<IrFunctionSymbol, JsFunction>() private val outlinedJsCodeFunctions = WeakHashMap<IrFunctionSymbol, JsFunction>()
fun addOutlinedJsCode(symbol: IrSimpleFunctionSymbol, outlinedJsCode: JsFunction) { fun addOutlinedJsCode(symbol: IrSimpleFunctionSymbol, outlinedJsCode: JsFunction) {
outlinedJsCodeFunctions[symbol] = outlinedJsCode outlinedJsCodeFunctions[symbol] = outlinedJsCode
@@ -119,7 +119,7 @@ class CacheUpdater(
) : KotlinSourceFileExports() { ) : KotlinSourceFileExports() {
override fun getExportedSignatures(): Set<IdSignature> = allExportedSignatures override fun getExportedSignatures(): Set<IdSignature> = allExportedSignatures
val allExportedSignatures = mutableSetOf<IdSignature>() val allExportedSignatures = hashSetOf<IdSignature>()
} }
private class DirtyFileMetadata( private class DirtyFileMetadata(
@@ -132,13 +132,13 @@ class CacheUpdater(
) : KotlinSourceFileMetadata() { ) : KotlinSourceFileMetadata() {
fun addInverseDependency(lib: KotlinLibraryFile, src: KotlinSourceFile, signature: IdSignature) = fun addInverseDependency(lib: KotlinLibraryFile, src: KotlinSourceFile, signature: IdSignature) =
when (val signatures = inverseDependencies[lib, src]) { when (val signatures = inverseDependencies[lib, src]) {
null -> inverseDependencies[lib, src] = mutableSetOf(signature) null -> inverseDependencies[lib, src] = hashSetOf(signature)
else -> signatures += signature else -> signatures += signature
} }
fun addDirectDependency(lib: KotlinLibraryFile, src: KotlinSourceFile, signature: IdSignature, hash: ICHash) = fun addDirectDependency(lib: KotlinLibraryFile, src: KotlinSourceFile, signature: IdSignature, hash: ICHash) =
when (val signatures = directDependencies[lib, src]) { 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 else -> signatures[signature] = hash
} }
} }
@@ -197,7 +197,7 @@ class CacheUpdater(
} }
private fun loadModifiedFiles(): KotlinSourceFileMap<KotlinSourceFileMetadata> { private fun loadModifiedFiles(): KotlinSourceFileMap<KotlinSourceFileMetadata> {
val removedFilesMetadata = mutableMapOf<KotlinLibraryFile, Map<KotlinSourceFile, KotlinSourceFileMetadata>>() val removedFilesMetadata = hashMapOf<KotlinLibraryFile, Map<KotlinSourceFile, KotlinSourceFileMetadata>>()
val modifiedFiles = KotlinSourceFileMap(incrementalCaches.entries.associate { (lib, cache) -> val modifiedFiles = KotlinSourceFileMap(incrementalCaches.entries.associate { (lib, cache) ->
val (dirtyFiles, removedFiles, newFiles, modifiedConfigFiles) = cache.collectModifiedFiles(configHash) val (dirtyFiles, removedFiles, newFiles, modifiedConfigFiles) = cache.collectModifiedFiles(configHash)
@@ -228,7 +228,7 @@ class CacheUpdater(
val exportedSymbols = KotlinSourceFileMutableMap<KotlinSourceFileExports>() val exportedSymbols = KotlinSourceFileMutableMap<KotlinSourceFileExports>()
for ((libFile, srcFiles) in dirtyFiles) { for ((libFile, srcFiles) in dirtyFiles) {
val exportedSymbolFiles = mutableMapOf<KotlinSourceFile, KotlinSourceFileExports>() val exportedSymbolFiles = HashMap<KotlinSourceFile, KotlinSourceFileExports>(srcFiles.size)
for ((srcFile, srcFileMetadata) in srcFiles) { for ((srcFile, srcFileMetadata) in srcFiles) {
val loadingFileExports = DirtyFileExports() val loadingFileExports = DirtyFileExports()
for ((dependentLib, dependentFiles) in srcFileMetadata.inverseDependencies) { for ((dependentLib, dependentFiles) in srcFileMetadata.inverseDependencies) {
@@ -264,7 +264,7 @@ class CacheUpdater(
} }
private fun collectImplementedSymbol(deserializedSymbols: Map<IdSignature, IrSymbol>): Map<IdSignature, IrSymbol> { private fun collectImplementedSymbol(deserializedSymbols: Map<IdSignature, IrSymbol>): Map<IdSignature, IrSymbol> {
return buildMap(deserializedSymbols.size) { return HashMap<IdSignature, IrSymbol>(deserializedSymbols.size).apply {
for ((signature, symbol) in deserializedSymbols) { for ((signature, symbol) in deserializedSymbols) {
put(signature, symbol) put(signature, symbol)
@@ -351,7 +351,7 @@ class CacheUpdater(
val incrementalCache = getLibIncrementalCache(libFile) val incrementalCache = getLibIncrementalCache(libFile)
for (fileDeserializer in moduleDeserializer.fileDeserializers()) { for (fileDeserializer in moduleDeserializer.fileDeserializers()) {
val reachableSignatures = fileDeserializer.symbolDeserializer.signatureDeserializer.signatureToIndexMapping() val reachableSignatures = fileDeserializer.symbolDeserializer.signatureDeserializer.signatureToIndexMapping()
val maybeImportedSignatures = reachableSignatures.keys.toMutableSet() val maybeImportedSignatures = HashSet(reachableSignatures.keys)
val implementedSymbols = collectImplementedSymbol(fileDeserializer.symbolDeserializer.deserializedSymbols) val implementedSymbols = collectImplementedSymbol(fileDeserializer.symbolDeserializer.deserializedSymbols)
for ((signature, symbol) in implementedSymbols) { for ((signature, symbol) in implementedSymbols) {
var symbolCanBeExported = maybeImportedSignatures.remove(signature) var symbolCanBeExported = maybeImportedSignatures.remove(signature)
@@ -506,7 +506,7 @@ class CacheUpdater(
dependentSignatures.keys != newSignatures -> { dependentSignatures.keys != newSignatures -> {
val newMetadata = addNewMetadata(dependentLibFile, dependentSrcFile, dependentSrcMetadata) 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) 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)) fragmentToLibName[it.irFile.module] ?: notFoundIcError("loaded fragment lib name", srcFile = KotlinSourceFile(it.irFile))
} }
val visited = mutableSetOf<KotlinLibrary>() val visited = hashSetOf<KotlinLibrary>()
val artifacts = mutableListOf<ModuleArtifact>() val artifacts = mutableListOf<ModuleArtifact>()
fun addArtifact(lib: KotlinLibrary) { fun addArtifact(lib: KotlinLibrary) {
if (visited.add(lib)) { if (visited.add(lib)) {
@@ -53,11 +53,11 @@ internal inline fun <E> buildListUntil(to: Int, builderAction: MutableList<E>.(I
} }
internal inline fun <E> buildSetUntil(to: Int, builderAction: MutableSet<E>.(Int) -> Unit): Set<E> { internal inline fun <E> buildSetUntil(to: Int, builderAction: MutableSet<E>.(Int) -> Unit): Set<E> {
return buildSet(to) { repeat(to) { builderAction(it) } } return HashSet<E>(to).apply { repeat(to) { builderAction(it) } }
} }
internal inline fun <K, V> buildMapUntil(to: Int, builderAction: MutableMap<K, V>.(Int) -> Unit): Map<K, V> { internal inline fun <K, V> buildMapUntil(to: Int, builderAction: MutableMap<K, V>.(Int) -> Unit): Map<K, V> {
return buildMap(to) { repeat(to) { builderAction(it) } } return HashMap<K, V>(to).apply { repeat(to) { builderAction(it) } }
} }
internal class StopwatchIC { internal class StopwatchIC {
@@ -21,12 +21,12 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid
class IdSignatureHashCalculator { class IdSignatureHashCalculator {
private val flatHashes = mutableMapOf<IrFunction, ICHash>() private val flatHashes = hashMapOf<IrFunction, ICHash>()
private val inlineFunctionCallGraph: MutableMap<IrFunction, Set<IrFunction>> = mutableMapOf() private val inlineFunctionCallGraph = hashMapOf<IrFunction, Set<IrFunction>>()
private val processingFunctions = mutableSetOf<IrFunction>() private val processingFunctions = hashSetOf<IrFunction>()
private val functionTransitiveHashes = mutableMapOf<IrFunction, ICHash>() private val functionTransitiveHashes = hashMapOf<IrFunction, ICHash>()
private val allIdSignatureHashes = mutableMapOf<IdSignature, ICHash>() private val allIdSignatureHashes = hashMapOf<IdSignature, ICHash>()
private inner class FlatHashCalculator : IrElementVisitorVoid { private inner class FlatHashCalculator : IrElementVisitorVoid {
@@ -25,7 +25,7 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
private var forceRebuildJs = false private var forceRebuildJs = false
private val cacheDir = File(cachePath) private val cacheDir = File(cachePath)
private val signatureToIndexMappingFromMetadata = mutableMapOf<KotlinSourceFile, MutableMap<IdSignature, Int>>() private val signatureToIndexMappingFromMetadata = hashMapOf<KotlinSourceFile, MutableMap<IdSignature, Int>>()
private val libraryFile = KotlinLibraryFile(library) private val libraryFile = KotlinLibraryFile(library)
@@ -69,7 +69,7 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
override val directDependencies = KotlinSourceFileMap<Map<IdSignature, ICHash>>(emptyMap()) override val directDependencies = KotlinSourceFileMap<Map<IdSignature, ICHash>>(emptyMap())
} }
private val kotlinLibrarySourceFileMetadata = mutableMapOf<KotlinSourceFile, KotlinSourceFileMetadata>() private val kotlinLibrarySourceFileMetadata = hashMapOf<KotlinSourceFile, KotlinSourceFileMetadata>()
private fun KotlinSourceFile.getCacheFile(suffix: String) = File(cacheDir, "${File(path).name}.${path.stringHashForIC()}.$suffix") 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 cachedFingerprints = loadCachedFingerprints()
val deletedFiles = cachedFingerprints.keys.toMutableSet() val deletedFiles = HashSet(cachedFingerprints.keys)
val unknownFiles = mutableSetOf<KotlinSourceFile>() val unknownFiles = mutableSetOf<KotlinSourceFile>()
val newFingerprints = kotlinLibraryHeader.sourceFiles.mapIndexed { index, file -> file to library.fingerprint(index) } val newFingerprints = kotlinLibraryHeader.sourceFiles.mapIndexed { index, file -> file to library.fingerprint(index) }
val modifiedFiles = buildMap(newFingerprints.size) { val modifiedFiles = HashMap<KotlinSourceFile, KotlinSourceFileMetadata>(newFingerprints.size).apply {
for ((file, fileNewFingerprint) in newFingerprints) { for ((file, fileNewFingerprint) in newFingerprints) {
val oldFingerprint = cachedFingerprints[file] val oldFingerprint = cachedFingerprints[file]
if (oldFingerprint == null) { if (oldFingerprint == null) {
@@ -170,7 +170,7 @@ class IncrementalCache(private val library: KotlinLibrary, cachePath: String) {
private fun fetchSourceFileMetadata(srcFile: KotlinSourceFile, loadSignatures: Boolean) = private fun fetchSourceFileMetadata(srcFile: KotlinSourceFile, loadSignatures: Boolean) =
kotlinLibrarySourceFileMetadata.getOrPut(srcFile) { kotlinLibrarySourceFileMetadata.getOrPut(srcFile) {
val signatureToIndexMapping = signatureToIndexMappingFromMetadata.getOrPut(srcFile) { mutableMapOf() } val signatureToIndexMapping = signatureToIndexMappingFromMetadata.getOrPut(srcFile) { hashMapOf() }
val deserializer: IdSignatureDeserializer by lazy { val deserializer: IdSignatureDeserializer by lazy {
kotlinLibraryHeader.signatureDeserializers[srcFile] ?: notFoundIcError("signature deserializer", libraryFile, srcFile) kotlinLibraryHeader.signatureDeserializers[srcFile] ?: notFoundIcError("signature deserializer", libraryFile, srcFile)
} }
@@ -55,7 +55,7 @@ internal class JsIrLinkerLoader(
} }
private fun loadModules(): Map<ModuleDescriptor, KotlinLibrary> { private fun loadModules(): Map<ModuleDescriptor, KotlinLibrary> {
val descriptors = mutableMapOf<KotlinLibrary, ModuleDescriptorImpl>() val descriptors = hashMapOf<KotlinLibrary, ModuleDescriptorImpl>()
var runtimeModule: ModuleDescriptorImpl? = null var runtimeModule: ModuleDescriptorImpl? = null
// TODO: deduplicate this code using part from klib.kt // TODO: deduplicate this code using part from klib.kt
@@ -23,7 +23,7 @@ class JsMultiModuleCache(private val moduleArtifacts: List<ModuleArtifact>) {
class CachedModuleInfo(val artifact: ModuleArtifact, val jsIrHeader: JsIrModuleHeader, var crossModuleReferencesHash: ICHash = ICHash()) class CachedModuleInfo(val artifact: ModuleArtifact, val jsIrHeader: JsIrModuleHeader, var crossModuleReferencesHash: ICHash = ICHash())
private val headerToCachedInfo = mutableMapOf<JsIrModuleHeader, CachedModuleInfo>() private val headerToCachedInfo = hashMapOf<JsIrModuleHeader, CachedModuleInfo>()
private fun ModuleArtifact.fetchModuleInfo() = File(artifactsDir, JS_MODULE_HEADER).useCodedInputIfExists { private fun ModuleArtifact.fetchModuleInfo() = File(artifactsDir, JS_MODULE_HEADER).useCodedInputIfExists {
val definitions = mutableSetOf<String>() val definitions = mutableSetOf<String>()
@@ -53,17 +53,17 @@ open class KotlinSourceFileMap<out T>(files: Map<KotlinLibraryFile, Map<KotlinSo
} }
class KotlinSourceFileMutableMap<T>( class KotlinSourceFileMutableMap<T>(
private val files: MutableMap<KotlinLibraryFile, MutableMap<KotlinSourceFile, T>> = mutableMapOf() private val files: MutableMap<KotlinLibraryFile, MutableMap<KotlinSourceFile, T>> = hashMapOf()
) : KotlinSourceFileMap<T>(files) { ) : KotlinSourceFileMap<T>(files) {
operator fun set(libFile: KotlinLibraryFile, sourceFile: KotlinSourceFile, data: T) = getOrPutFiles(libFile).put(sourceFile, data) operator fun set(libFile: KotlinLibraryFile, sourceFile: KotlinSourceFile, data: T) = getOrPutFiles(libFile).put(sourceFile, data)
operator fun set(libFile: KotlinLibraryFile, sourceFiles: MutableMap<KotlinSourceFile, T>) = files.put(libFile, sourceFiles) operator fun set(libFile: KotlinLibraryFile, sourceFiles: MutableMap<KotlinSourceFile, T>) = files.put(libFile, sourceFiles)
fun getOrPutFiles(libFile: KotlinLibraryFile) = files.getOrPut(libFile) { mutableMapOf() } fun getOrPutFiles(libFile: KotlinLibraryFile) = files.getOrPut(libFile) { hashMapOf() }
fun copyFilesFrom(other: KotlinSourceFileMap<T>) { fun copyFilesFrom(other: KotlinSourceFileMap<T>) {
for ((libFile, srcFiles) in other) { for ((libFile, srcFiles) in other) {
files.getOrPut(libFile) { mutableMapOf() } += srcFiles files.getOrPut(libFile) { hashMapOf() } += srcFiles
} }
} }
@@ -81,11 +81,11 @@ class KotlinSourceFileMutableMap<T>(
} }
fun <T> KotlinSourceFileMap<T>.toMutable(): KotlinSourceFileMutableMap<T> { fun <T> KotlinSourceFileMap<T>.toMutable(): KotlinSourceFileMutableMap<T> {
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<Set<IdSignature>>.flatSignatures(): Set<IdSignature> { fun KotlinSourceFileMap<Set<IdSignature>>.flatSignatures(): Set<IdSignature> {
val allSignatures = mutableSetOf<IdSignature>() val allSignatures = hashSetOf<IdSignature>()
forEachFile { _, _, signatures -> allSignatures += signatures } forEachFile { _, _, signatures -> allSignatures += signatures }
return allSignatures return allSignatures
} }
@@ -106,7 +106,7 @@ class JsNameLinkingNamer(private val context: JsIrBackendContext, private val mi
private fun IrClass.fieldData(): Map<IrField, String> { private fun IrClass.fieldData(): Map<IrField, String> {
return context.fieldDataCache.getOrPut(this) { return context.fieldDataCache.getOrPut(this) {
val nameCnt = mutableMapOf<String, Int>() val nameCnt = hashMapOf<String, Int>()
val allClasses = DFS.topologicalOrder(listOf(this)) { node -> val allClasses = DFS.topologicalOrder(listOf(this)) { node ->
node.superTypes.mapNotNull { node.superTypes.mapNotNull {
@@ -114,7 +114,7 @@ class JsNameLinkingNamer(private val context: JsIrBackendContext, private val mi
} }
} }
val result = mutableMapOf<IrField, String>() val result = hashMapOf<IrField, String>()
if (minimizedMemberNames) { if (minimizedMemberNames) {
allClasses.reversed().forEach { allClasses.reversed().forEach {
@@ -187,4 +187,4 @@ private fun List<JsName>.makeRef(): JsNameRef {
result = JsNameRef(this[i], result) result = JsNameRef(this[i], result)
} }
return result return result
} }
@@ -7,9 +7,9 @@ package org.jetbrains.kotlin.ir.backend.js.utils
class MinimizedNameGenerator { class MinimizedNameGenerator {
private var index = 0 private var index = 0
private val functionSignatureToName = mutableMapOf<String, String>() private val functionSignatureToName = hashMapOf<String, String>()
private val reservedNames = mutableSetOf<String>() private val reservedNames = hashSetOf<String>()
private val keptNames = mutableSetOf<String>() private val keptNames = hashSetOf<String>()
fun generateNextName(): String { fun generateNextName(): String {
var candidate = index++.toJsIdentifier() var candidate = index++.toJsIdentifier()
@@ -39,4 +39,4 @@ class MinimizedNameGenerator {
functionSignatureToName.clear() functionSignatureToName.clear()
reservedNames.clear() reservedNames.clear()
} }
} }
@@ -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 // TODO: Use better hashCode
return sanitizeName( val sanitizedName = sanitizeName(declarationName, withHash = false)
declarationName, return "${sanitizedName}_$signature$RESERVED_MEMBER_NAME_SUFFIX".intern()
withHash = false
) + "_" + abs(signature.hashCode()).toString(Character.MAX_RADIX) + RESERVED_MEMBER_NAME_SUFFIX
} }
fun jsFunctionSignature( fun jsFunctionSignature(
@@ -13,14 +13,15 @@ import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
import java.util.*
class IrFactoryImplForJsIC(override val stageController: StageController) : AbstractIrFactoryImpl(), IdSignatureRetriever { class IrFactoryImplForJsIC(override val stageController: StageController) : AbstractIrFactoryImpl(), IdSignatureRetriever {
private val declarationToSignature = mutableMapOf<IrDeclaration, IdSignature>() private val declarationToSignature = WeakHashMap<IrDeclaration, IdSignature>()
private fun <T : IrDeclaration> T.register(): T { private fun <T : IrDeclaration> T.register(): T {
val parentSig = stageController.currentDeclaration?.let { declarationSignature(it) } ?: return this 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 return this
} }
@@ -392,4 +393,4 @@ class IrFactoryImplForJsIC(override val stageController: StageController) : Abst
isAssignable, isAssignable,
).register() ).register()
} }
} }