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
|
||||
|
||||
import com.intellij.util.containers.StringInterner
|
||||
import gnu.trove.THashMap
|
||||
import gnu.trove.THashSet
|
||||
import org.jetbrains.kotlin.daemon.common.CompilerCallbackServicesFacade
|
||||
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
|
||||
|
||||
|
||||
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 lookups = THashSet<LookupInfo>()
|
||||
// Map: FileName -> (ScopeFqName -> Set<Name[String] | LookupInfo>)
|
||||
private val lookups = THashMap<String, MutableMap<String, MutableSet<Any>>>()
|
||||
private val interner = StringInterner()
|
||||
|
||||
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) {
|
||||
if (isDoNothing) return
|
||||
|
||||
val internedFilePath = interner.intern(filePath)
|
||||
val internedScopeFqName = interner.intern(scopeFqName)
|
||||
val internedSymbolFqName = interner.intern(scopeFqName)
|
||||
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 {
|
||||
@@ -50,11 +61,24 @@ class RemoteLookupTrackerClient(val facade: CompilerCallbackServicesFacade, even
|
||||
}
|
||||
|
||||
private fun flush() {
|
||||
if (isDoNothing || lookups.isEmpty()) return
|
||||
if (isDoNothing || lookups.isEmpty) return
|
||||
|
||||
profiler.withMeasure(this) {
|
||||
// Converting to list is intentional because THashSet may lead to serialization-related problems
|
||||
facade.lookupTracker_record(lookups.toList())
|
||||
facade.lookupTracker_record(
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user