[workspace-model] Optimize memory consumption by new VirtualFileUrlManagerImpl

GitOrigin-RevId: 97900049061297e1c32feb8c79570f4a6f0f2abf
This commit is contained in:
Mikhail Mazurkevich
2020-04-24 13:02:19 +00:00
committed by intellij-monorepo-bot
parent 325c9cf27e
commit 25af0077d0
4 changed files with 59 additions and 49 deletions
@@ -29,7 +29,7 @@ class VirtualFileNameStoreTest {
Assert.assertEquals(3, store.generateIdForName("c"))
store.removeName("b")
store.removeName("a")
Assert.assertEquals(2, store.generateIdForName("e"))
Assert.assertEquals(1, store.generateIdForName("f"))
Assert.assertEquals(4, store.generateIdForName("e"))
Assert.assertEquals(5, store.generateIdForName("f"))
}
}
@@ -1,18 +0,0 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.workspace.ide
import org.jetbrains.annotations.TestOnly
import java.util.*
internal class IdGenerator {
private val freeIdsQueue: Queue<Int> = LinkedList()
private var generator: Int = 0
fun generateId() = freeIdsQueue.poll() ?: ++generator
fun releaseId(id: Int) = freeIdsQueue.add(id)
@TestOnly
fun clear() {
generator = 0
freeIdsQueue.clear()
}
}
@@ -1,47 +1,45 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.workspace.ide
import com.intellij.util.containers.BidirectionalMap
import gnu.trove.THashMap
import org.jetbrains.annotations.TestOnly
internal class VirtualFileNameStore {
private val generator = IdGenerator()
private val nameStore = BidirectionalMap<String, IdPerCount>()
private val name2IdStore = THashMap<String, IdPerCount>()
private val id2NameStore = THashMap<Int, String>()
fun generateIdForName(name: String): Int {
val idPerCount = nameStore[name]
val idPerCount = name2IdStore[name]
if (idPerCount != null) {
idPerCount.usageCount++
return idPerCount.id
} else {
val id = generator.generateId()
nameStore[name] = IdPerCount(id, 1)
name2IdStore[name] = IdPerCount(id, 1)
id2NameStore[id] = name
return id
}
}
fun removeName(name: String) {
val idPerCount = nameStore[name] ?: return
val idPerCount = name2IdStore[name] ?: return
if (idPerCount.usageCount == 1L) {
nameStore.remove(name)
generator.releaseId(idPerCount.id)
name2IdStore.remove(name)
id2NameStore.remove(idPerCount.id)
} else {
idPerCount.usageCount--
}
}
fun getNameForId(id: Int): String? {
val list = nameStore.getKeysByValue(IdPerCount(id, 1)) ?: return null
if (list.isEmpty()) return null
assert(list.size == 1)
return list[0]
}
fun getNameForId(id: Int): String? = id2NameStore[id]
fun getIdForName(name: String) = nameStore[name]?.id
fun getIdForName(name: String) = name2IdStore[name]?.id
@TestOnly
fun clear() {
nameStore.clear()
name2IdStore.clear()
id2NameStore.clear()
generator.clear()
}
}
@@ -58,4 +56,14 @@ private data class IdPerCount(val id: Int, var usageCount: Long) {
}
override fun hashCode() = 31 * id.hashCode()
}
internal class IdGenerator {
private var generator: Int = 0
fun generateId() = ++generator
@TestOnly
fun clear() {
generator = 0
}
}
@@ -4,16 +4,18 @@ package com.intellij.workspace.ide
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.SmartList
import com.intellij.workspace.api.VirtualFileUrl
import com.intellij.workspace.api.VirtualFileUrlManager
import gnu.trove.THashMap
import kotlin.collections.set
class VirtualFileUrlManagerImpl: VirtualFileUrlManager() {
private val idGenerator= IdGenerator()
private val EMPTY_URL = VirtualFileUrl(0, this)
private val fileNameStore = VirtualFileNameStore()
private val id2NodeMapping = HashMap<Int, FilePathNode>()
private var segmentId2RootNodeMapping = mutableMapOf<Int, FilePathNode>()
private val id2NodeMapping = THashMap<Int, FilePathNode>()
private var segmentId2RootNodeMapping = THashMap<Int, FilePathNode>()
companion object {
fun getInstance(project: Project): VirtualFileUrlManager = project.service<VirtualFileUrlManagerImpl>()
@@ -82,12 +84,12 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() {
}
}
val node = latestNode.children.find { it.contentId == nameId }
val node = latestNode.findChild(nameId)
if (node == null) {
val nodeId = idGenerator.generateId()
val newNode = FilePathNode(nodeId, nameId, latestNode)
id2NodeMapping[nodeId] = newNode
latestNode.children.add(newNode)
latestNode.addChild(newNode)
latestNode = newNode
// If it's the latest name of folder or files, save entity Id as node value
if (index == latestElement) return VirtualFileUrl(nodeId, this)
@@ -106,27 +108,25 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() {
println("File not found")
return
}
if (node.children.isNotEmpty()) return
if (!node.isEmpty()) return
var currentNode: FilePathNode = node
do {
val parent = currentNode.parent
if (parent == null) {
if (currentNode === findRootNode(currentNode.contentId) && currentNode.children.isEmpty()) {
if (currentNode === findRootNode(currentNode.contentId) && currentNode.isEmpty()) {
removeNameUsage(currentNode.contentId)
idGenerator.releaseId(currentNode.nodeId)
id2NodeMapping.remove(currentNode.nodeId)
segmentId2RootNodeMapping.remove(currentNode.contentId)
}
return
}
parent.children.remove(currentNode)
parent.removeChild(currentNode)
removeNameUsage(currentNode.contentId)
idGenerator.releaseId(currentNode.nodeId)
id2NodeMapping.remove(currentNode.nodeId)
currentNode = parent
} while (currentNode.children.isEmpty())
} while (currentNode.isEmpty())
}
internal fun update(oldPath: String, newPath: String) {
@@ -157,7 +157,7 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() {
}
}
latestNode.children.find { it.contentId == nameId }?.let {
latestNode.findChild(nameId)?.let {
if (index == latestElement) return it
latestNode = it
} ?: return null
@@ -202,7 +202,28 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() {
}
private inner class FilePathNode(val nodeId: Int, val contentId: Int, val parent: FilePathNode? = null) {
val children: MutableSet<FilePathNode> = mutableSetOf()
private var children: MutableList<FilePathNode>? = null
internal fun findChild(nameId: Int): FilePathNode? {
// If search of child node will be slow, replace SmartList to THashSet
// For now SmartList reduce 500Kb memory on IDEA project
return children?.find { it.contentId == nameId }
}
internal fun addChild(newNode: FilePathNode) {
createChildrenList()
children!!.add(newNode)
}
internal fun removeChild(node: FilePathNode) {
children?.remove(node)
}
internal fun isEmpty() = children == null || children!!.isEmpty()
private fun createChildrenList() {
if (children == null) children = SmartList()
}
override fun toString(): String {
val buffer = StringBuilder()
@@ -213,7 +234,7 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() {
private fun print(buffer: StringBuilder, prefix: String, childrenPrefix: String) {
val name = this@VirtualFileUrlManagerImpl.fileNameStore.getNameForId(contentId)
buffer.append("$prefix $name\n")
val iterator = children.iterator()
val iterator = children?.iterator() ?: return
while (iterator.hasNext()) {
val next = iterator.next()
if (iterator.hasNext()) {
@@ -225,5 +246,4 @@ class VirtualFileUrlManagerImpl: VirtualFileUrlManager() {
}
}
}
}