[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.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<IrField, IrExpression> = mutableMapOf()
val fieldToInitializer = WeakHashMap<IrField, IrExpression>()
val localClassNames: MutableMap<IrClass, String> = mutableMapOf()
val localClassNames = WeakHashMap<IrClass, String>()
val minimizedNameGenerator: MinimizedNameGenerator =
MinimizedNameGenerator()
@@ -83,7 +84,7 @@ class JsIrBackendContext(
val keeper: Keeper =
Keeper(keep)
val fieldDataCache = mutableMapOf<IrClass, Map<IrField, String>>()
val fieldDataCache = WeakHashMap<IrClass, Map<IrField, String>>()
override val builtIns = module.builtIns
@@ -387,7 +388,7 @@ class JsIrBackendContext(
print(message)
}
private val outlinedJsCodeFunctions = mutableMapOf<IrFunctionSymbol, JsFunction>()
private val outlinedJsCodeFunctions = WeakHashMap<IrFunctionSymbol, JsFunction>()
fun addOutlinedJsCode(symbol: IrSimpleFunctionSymbol, outlinedJsCode: JsFunction) {
outlinedJsCodeFunctions[symbol] = outlinedJsCode
@@ -119,7 +119,7 @@ class CacheUpdater(
) : KotlinSourceFileExports() {
override fun getExportedSignatures(): Set<IdSignature> = allExportedSignatures
val allExportedSignatures = mutableSetOf<IdSignature>()
val allExportedSignatures = hashSetOf<IdSignature>()
}
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<KotlinSourceFileMetadata> {
val removedFilesMetadata = mutableMapOf<KotlinLibraryFile, Map<KotlinSourceFile, KotlinSourceFileMetadata>>()
val removedFilesMetadata = hashMapOf<KotlinLibraryFile, Map<KotlinSourceFile, KotlinSourceFileMetadata>>()
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<KotlinSourceFileExports>()
for ((libFile, srcFiles) in dirtyFiles) {
val exportedSymbolFiles = mutableMapOf<KotlinSourceFile, KotlinSourceFileExports>()
val exportedSymbolFiles = HashMap<KotlinSourceFile, KotlinSourceFileExports>(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<IdSignature, IrSymbol>): Map<IdSignature, IrSymbol> {
return buildMap(deserializedSymbols.size) {
return HashMap<IdSignature, IrSymbol>(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<KotlinLibrary>()
val visited = hashSetOf<KotlinLibrary>()
val artifacts = mutableListOf<ModuleArtifact>()
fun addArtifact(lib: KotlinLibrary) {
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> {
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> {
return buildMap(to) { repeat(to) { builderAction(it) } }
return HashMap<K, V>(to).apply { repeat(to) { builderAction(it) } }
}
internal class StopwatchIC {
@@ -21,12 +21,12 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid
class IdSignatureHashCalculator {
private val flatHashes = mutableMapOf<IrFunction, ICHash>()
private val inlineFunctionCallGraph: MutableMap<IrFunction, Set<IrFunction>> = mutableMapOf()
private val processingFunctions = mutableSetOf<IrFunction>()
private val functionTransitiveHashes = mutableMapOf<IrFunction, ICHash>()
private val flatHashes = hashMapOf<IrFunction, ICHash>()
private val inlineFunctionCallGraph = hashMapOf<IrFunction, Set<IrFunction>>()
private val processingFunctions = hashSetOf<IrFunction>()
private val functionTransitiveHashes = hashMapOf<IrFunction, ICHash>()
private val allIdSignatureHashes = mutableMapOf<IdSignature, ICHash>()
private val allIdSignatureHashes = hashMapOf<IdSignature, ICHash>()
private inner class FlatHashCalculator : IrElementVisitorVoid {
@@ -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<KotlinSourceFile, MutableMap<IdSignature, Int>>()
private val signatureToIndexMappingFromMetadata = hashMapOf<KotlinSourceFile, MutableMap<IdSignature, Int>>()
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())
}
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")
@@ -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<KotlinSourceFile>()
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) {
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)
}
@@ -55,7 +55,7 @@ internal class JsIrLinkerLoader(
}
private fun loadModules(): Map<ModuleDescriptor, KotlinLibrary> {
val descriptors = mutableMapOf<KotlinLibrary, ModuleDescriptorImpl>()
val descriptors = hashMapOf<KotlinLibrary, ModuleDescriptorImpl>()
var runtimeModule: ModuleDescriptorImpl? = null
// 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())
private val headerToCachedInfo = mutableMapOf<JsIrModuleHeader, CachedModuleInfo>()
private val headerToCachedInfo = hashMapOf<JsIrModuleHeader, CachedModuleInfo>()
private fun ModuleArtifact.fetchModuleInfo() = File(artifactsDir, JS_MODULE_HEADER).useCodedInputIfExists {
val definitions = mutableSetOf<String>()
@@ -53,17 +53,17 @@ open class KotlinSourceFileMap<out T>(files: Map<KotlinLibraryFile, Map<KotlinSo
}
class KotlinSourceFileMutableMap<T>(
private val files: MutableMap<KotlinLibraryFile, MutableMap<KotlinSourceFile, T>> = mutableMapOf()
private val files: MutableMap<KotlinLibraryFile, MutableMap<KotlinSourceFile, T>> = hashMapOf()
) : KotlinSourceFileMap<T>(files) {
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)
fun getOrPutFiles(libFile: KotlinLibraryFile) = files.getOrPut(libFile) { mutableMapOf() }
fun getOrPutFiles(libFile: KotlinLibraryFile) = files.getOrPut(libFile) { hashMapOf() }
fun copyFilesFrom(other: KotlinSourceFileMap<T>) {
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> {
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> {
val allSignatures = mutableSetOf<IdSignature>()
val allSignatures = hashSetOf<IdSignature>()
forEachFile { _, _, signatures -> allSignatures += signatures }
return allSignatures
}
@@ -106,7 +106,7 @@ class JsNameLinkingNamer(private val context: JsIrBackendContext, private val mi
private fun IrClass.fieldData(): Map<IrField, String> {
return context.fieldDataCache.getOrPut(this) {
val nameCnt = mutableMapOf<String, Int>()
val nameCnt = hashMapOf<String, Int>()
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<IrField, String>()
val result = hashMapOf<IrField, String>()
if (minimizedMemberNames) {
allClasses.reversed().forEach {
@@ -187,4 +187,4 @@ private fun List<JsName>.makeRef(): JsNameRef {
result = JsNameRef(this[i], result)
}
return result
}
}
@@ -7,9 +7,9 @@ package org.jetbrains.kotlin.ir.backend.js.utils
class MinimizedNameGenerator {
private var index = 0
private val functionSignatureToName = mutableMapOf<String, String>()
private val reservedNames = mutableSetOf<String>()
private val keptNames = mutableSetOf<String>()
private val functionSignatureToName = hashMapOf<String, String>()
private val reservedNames = hashSetOf<String>()
private val keptNames = hashSetOf<String>()
fun generateNextName(): String {
var candidate = index++.toJsIdentifier()
@@ -39,4 +39,4 @@ class MinimizedNameGenerator {
functionSignatureToName.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
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(
@@ -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<IrDeclaration, IdSignature>()
private val declarationToSignature = WeakHashMap<IrDeclaration, IdSignature>()
private fun <T : IrDeclaration> 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()
}
}
}