Optimize memory-footprint for RemoteLookupTrackerClient
Observations: - In case of !requiresPosition we don't need to track position - Also, in the latter case we don't need scopeKind (implicit contract) - There were a lot of LookupInfo instances having two references (fileName/scopeFqName) pointing to the same strings. Looks like in common case (reasonable amount of files/fqnames) it's more sensible to group lookups in the two-level-tree (implemented with nested maps) It's expected to decrease memory consumption up to 15M in case of compiling `idea` module in Kotlin project
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.daemon
|
package org.jetbrains.kotlin.daemon
|
||||||
|
|
||||||
import com.intellij.util.containers.StringInterner
|
import com.intellij.util.containers.StringInterner
|
||||||
|
import gnu.trove.THashMap
|
||||||
import gnu.trove.THashSet
|
import gnu.trove.THashSet
|
||||||
import org.jetbrains.kotlin.daemon.common.CompilerCallbackServicesFacade
|
import org.jetbrains.kotlin.daemon.common.CompilerCallbackServicesFacade
|
||||||
import org.jetbrains.kotlin.daemon.common.DummyProfiler
|
import org.jetbrains.kotlin.daemon.common.DummyProfiler
|
||||||
@@ -27,10 +28,15 @@ import org.jetbrains.kotlin.incremental.components.Position
|
|||||||
import org.jetbrains.kotlin.incremental.components.ScopeKind
|
import org.jetbrains.kotlin.incremental.components.ScopeKind
|
||||||
|
|
||||||
|
|
||||||
class RemoteLookupTrackerClient(val facade: CompilerCallbackServicesFacade, eventManager: EventManager, val profiler: Profiler = DummyProfiler()) : LookupTracker {
|
class RemoteLookupTrackerClient(
|
||||||
|
val facade: CompilerCallbackServicesFacade,
|
||||||
|
eventManager: EventManager,
|
||||||
|
val profiler: Profiler = DummyProfiler()
|
||||||
|
) : LookupTracker {
|
||||||
private val isDoNothing = profiler.withMeasure(this) { facade.lookupTracker_isDoNothing() }
|
private val isDoNothing = profiler.withMeasure(this) { facade.lookupTracker_isDoNothing() }
|
||||||
|
|
||||||
private val lookups = THashSet<LookupInfo>()
|
// Map: FileName -> (ScopeFqName -> Set<Name[String] | LookupInfo>)
|
||||||
|
private val lookups = THashMap<String, MutableMap<String, MutableSet<Any>>>()
|
||||||
private val interner = StringInterner()
|
private val interner = StringInterner()
|
||||||
|
|
||||||
override val requiresPosition: Boolean = profiler.withMeasure(this) { facade.lookupTracker_requiresPosition() }
|
override val requiresPosition: Boolean = profiler.withMeasure(this) { facade.lookupTracker_requiresPosition() }
|
||||||
@@ -38,11 +44,16 @@ class RemoteLookupTrackerClient(val facade: CompilerCallbackServicesFacade, even
|
|||||||
override fun record(filePath: String, position: Position, scopeFqName: String, scopeKind: ScopeKind, name: String) {
|
override fun record(filePath: String, position: Position, scopeFqName: String, scopeKind: ScopeKind, name: String) {
|
||||||
if (isDoNothing) return
|
if (isDoNothing) return
|
||||||
|
|
||||||
val internedFilePath = interner.intern(filePath)
|
val internedSymbolFqName = interner.intern(scopeFqName)
|
||||||
val internedScopeFqName = interner.intern(scopeFqName)
|
|
||||||
val internedName = interner.intern(name)
|
val internedName = interner.intern(name)
|
||||||
|
|
||||||
lookups.add(LookupInfo(internedFilePath, position, internedScopeFqName, scopeKind, internedName))
|
val objectToPut: Any =
|
||||||
|
if (requiresPosition)
|
||||||
|
LookupInfo(filePath, position, scopeFqName, scopeKind, name)
|
||||||
|
else
|
||||||
|
internedName
|
||||||
|
|
||||||
|
lookups.getOrPut(filePath, ::THashMap).getOrPut(internedSymbolFqName, ::THashSet).add(objectToPut)
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -50,11 +61,24 @@ class RemoteLookupTrackerClient(val facade: CompilerCallbackServicesFacade, even
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun flush() {
|
private fun flush() {
|
||||||
if (isDoNothing || lookups.isEmpty()) return
|
if (isDoNothing || lookups.isEmpty) return
|
||||||
|
|
||||||
profiler.withMeasure(this) {
|
profiler.withMeasure(this) {
|
||||||
// Converting to list is intentional because THashSet may lead to serialization-related problems
|
facade.lookupTracker_record(
|
||||||
facade.lookupTracker_record(lookups.toList())
|
lookups.flatMap { (filePath, lookupsByFile) ->
|
||||||
|
lookupsByFile.flatMap { (scopeFqName, lookupsByScopeFqName) ->
|
||||||
|
lookupsByScopeFqName.map { lookupInfoOrString ->
|
||||||
|
if (requiresPosition)
|
||||||
|
lookupInfoOrString as LookupInfo
|
||||||
|
else
|
||||||
|
LookupInfo(
|
||||||
|
filePath, Position.NO_POSITION, scopeFqName, ScopeKind.CLASSIFIER,
|
||||||
|
lookupInfoOrString as String
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
lookups.clear()
|
lookups.clear()
|
||||||
|
|||||||
Reference in New Issue
Block a user