Skip to content

Commit fbdb7cb

Browse files
authored
Reduce excess memory usage in TransparentCompiler (#17543)
1 parent 2ece164 commit fbdb7cb

12 files changed

+367
-79
lines changed

docs/release-notes/.FSharp.Compiler.Service/9.0.200.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* Fix locals allocating for the special `copyOfStruct` defensive copy ([PR #18025](https://github.com/dotnet/fsharp/pull/18025))
1616
* Fix lowering of computed array expressions when the expression consists of a simple mapping from a `uint64` or `unativeint` array. [PR #18081](https://github.com/dotnet/fsharp/pull/18081)
1717
* Add missing nullable-metadata for C# consumers of records,exceptions and DU subtypes generated from F# code. [PR #18079](https://github.com/dotnet/fsharp/pull/18079)
18+
* Reduce excess memory usage in TransparentCompiler. [PR #17543](https://github.com/dotnet/fsharp/pull/17543)
1819
* Fix a race condition in file book keeping in the compiler service ([#18008](https://github.com/dotnet/fsharp/pull/18008))
1920
* Fix trimming '%' characters when lowering interpolated string to a concat call [PR #18123](https://github.com/dotnet/fsharp/pull/18123)
2021
* Completion: fix qualified completion in sequence expressions [PR #18111](https://github.com/dotnet/fsharp/pull/18111)

src/Compiler/Service/FSharpProjectSnapshot.fs

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -747,26 +747,35 @@ and [<Experimental("This FCS API is experimental and subject to change.")>] FSha
747747

748748
FSharpProjectSnapshot.FromOptions(options, getFileSnapshot)
749749

750-
let rec internal snapshotToOptions (projectSnapshot: ProjectSnapshotBase<_>) =
751-
{
752-
ProjectFileName = projectSnapshot.ProjectFileName
753-
ProjectId = projectSnapshot.ProjectId
754-
SourceFiles = projectSnapshot.SourceFiles |> Seq.map (fun x -> x.FileName) |> Seq.toArray
755-
OtherOptions = projectSnapshot.CommandLineOptions |> List.toArray
756-
ReferencedProjects =
757-
projectSnapshot.ReferencedProjects
758-
|> Seq.map (function
759-
| FSharpReference(name, opts) -> FSharpReferencedProject.FSharpReference(name, opts.ProjectSnapshot |> snapshotToOptions)
760-
| PEReference(getStamp, reader) -> FSharpReferencedProject.PEReference(getStamp, reader)
761-
| ILModuleReference(name, getStamp, getReader) -> FSharpReferencedProject.ILModuleReference(name, getStamp, getReader))
762-
|> Seq.toArray
763-
IsIncompleteTypeCheckEnvironment = projectSnapshot.IsIncompleteTypeCheckEnvironment
764-
UseScriptResolutionRules = projectSnapshot.UseScriptResolutionRules
765-
LoadTime = projectSnapshot.LoadTime
766-
UnresolvedReferences = projectSnapshot.UnresolvedReferences
767-
OriginalLoadReferences = projectSnapshot.OriginalLoadReferences
768-
Stamp = projectSnapshot.Stamp
769-
}
750+
let internal snapshotTable =
751+
ConditionalWeakTable<ProjectSnapshot, FSharpProjectOptions>()
752+
753+
let rec internal snapshotToOptions (projectSnapshot: ProjectSnapshot) =
754+
snapshotTable.GetValue(
755+
projectSnapshot,
756+
fun projectSnapshot ->
757+
{
758+
ProjectFileName = projectSnapshot.ProjectFileName
759+
ProjectId = projectSnapshot.ProjectId
760+
SourceFiles = projectSnapshot.SourceFiles |> Seq.map (fun x -> x.FileName) |> Seq.toArray
761+
OtherOptions = projectSnapshot.CommandLineOptions |> List.toArray
762+
ReferencedProjects =
763+
projectSnapshot.ReferencedProjects
764+
|> Seq.map (function
765+
| FSharpReference(name, opts) ->
766+
FSharpReferencedProject.FSharpReference(name, opts.ProjectSnapshot |> snapshotToOptions)
767+
| PEReference(getStamp, reader) -> FSharpReferencedProject.PEReference(getStamp, reader)
768+
| ILModuleReference(name, getStamp, getReader) ->
769+
FSharpReferencedProject.ILModuleReference(name, getStamp, getReader))
770+
|> Seq.toArray
771+
IsIncompleteTypeCheckEnvironment = projectSnapshot.IsIncompleteTypeCheckEnvironment
772+
UseScriptResolutionRules = projectSnapshot.UseScriptResolutionRules
773+
LoadTime = projectSnapshot.LoadTime
774+
UnresolvedReferences = projectSnapshot.UnresolvedReferences
775+
OriginalLoadReferences = projectSnapshot.OriginalLoadReferences
776+
Stamp = projectSnapshot.Stamp
777+
}
778+
)
770779

771780
[<Extension>]
772781
type internal Extensions =

src/Compiler/Service/TransparentCompiler.fs

Lines changed: 125 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -259,44 +259,137 @@ module private TypeCheckingGraphProcessing =
259259
return finalFileResults, state
260260
}
261261

262-
type internal CompilerCaches(sizeFactor: int) =
262+
type CacheSizes =
263+
{
264+
ParseFileKeepStrongly: int
265+
ParseFileKeepWeakly: int
266+
ParseFileWithoutProjectKeepStrongly: int
267+
ParseFileWithoutProjectKeepWeakly: int
268+
ParseAndCheckFileInProjectKeepStrongly: int
269+
ParseAndCheckFileInProjectKeepWeakly: int
270+
ParseAndCheckAllFilesInProjectKeepStrongly: int
271+
ParseAndCheckAllFilesInProjectKeepWeakly: int
272+
ParseAndCheckProjectKeepStrongly: int
273+
ParseAndCheckProjectKeepWeakly: int
274+
FrameworkImportsKeepStrongly: int
275+
FrameworkImportsKeepWeakly: int
276+
BootstrapInfoStaticKeepStrongly: int
277+
BootstrapInfoStaticKeepWeakly: int
278+
BootstrapInfoKeepStrongly: int
279+
BootstrapInfoKeepWeakly: int
280+
TcLastFileKeepStrongly: int
281+
TcLastFileKeepWeakly: int
282+
TcIntermediateKeepStrongly: int
283+
TcIntermediateKeepWeakly: int
284+
DependencyGraphKeepStrongly: int
285+
DependencyGraphKeepWeakly: int
286+
ProjectExtrasKeepStrongly: int
287+
ProjectExtrasKeepWeakly: int
288+
AssemblyDataKeepStrongly: int
289+
AssemblyDataKeepWeakly: int
290+
SemanticClassificationKeepStrongly: int
291+
SemanticClassificationKeepWeakly: int
292+
ItemKeyStoreKeepStrongly: int
293+
ItemKeyStoreKeepWeakly: int
294+
ScriptClosureKeepStrongly: int
295+
ScriptClosureKeepWeakly: int
296+
}
297+
298+
static member Create sizeFactor =
299+
300+
{
301+
ParseFileKeepStrongly = 50 * sizeFactor
302+
ParseFileKeepWeakly = 20 * sizeFactor
303+
ParseFileWithoutProjectKeepStrongly = 5 * sizeFactor
304+
ParseFileWithoutProjectKeepWeakly = 2 * sizeFactor
305+
ParseAndCheckFileInProjectKeepStrongly = sizeFactor
306+
ParseAndCheckFileInProjectKeepWeakly = 2 * sizeFactor
307+
ParseAndCheckAllFilesInProjectKeepStrongly = sizeFactor
308+
ParseAndCheckAllFilesInProjectKeepWeakly = 2 * sizeFactor
309+
ParseAndCheckProjectKeepStrongly = sizeFactor
310+
ParseAndCheckProjectKeepWeakly = 2 * sizeFactor
311+
FrameworkImportsKeepStrongly = sizeFactor
312+
FrameworkImportsKeepWeakly = 2 * sizeFactor
313+
BootstrapInfoStaticKeepStrongly = sizeFactor
314+
BootstrapInfoStaticKeepWeakly = 2 * sizeFactor
315+
BootstrapInfoKeepStrongly = sizeFactor
316+
BootstrapInfoKeepWeakly = 2 * sizeFactor
317+
TcLastFileKeepStrongly = sizeFactor
318+
TcLastFileKeepWeakly = 2 * sizeFactor
319+
TcIntermediateKeepStrongly = 20 * sizeFactor
320+
TcIntermediateKeepWeakly = 20 * sizeFactor
321+
DependencyGraphKeepStrongly = sizeFactor
322+
DependencyGraphKeepWeakly = 2 * sizeFactor
323+
ProjectExtrasKeepStrongly = sizeFactor
324+
ProjectExtrasKeepWeakly = 2 * sizeFactor
325+
AssemblyDataKeepStrongly = sizeFactor
326+
AssemblyDataKeepWeakly = 2 * sizeFactor
327+
SemanticClassificationKeepStrongly = sizeFactor
328+
SemanticClassificationKeepWeakly = 2 * sizeFactor
329+
ItemKeyStoreKeepStrongly = sizeFactor
330+
ItemKeyStoreKeepWeakly = 2 * sizeFactor
331+
ScriptClosureKeepStrongly = sizeFactor
332+
ScriptClosureKeepWeakly = 2 * sizeFactor
333+
}
263334

264-
let sf = sizeFactor
335+
static member Default =
336+
let sizeFactor = 100
337+
CacheSizes.Create sizeFactor
265338

266-
member _.SizeFactor = sf
339+
type internal CompilerCaches(cacheSizes: CacheSizes) =
340+
let cs = cacheSizes
267341

268-
member val ParseFile = AsyncMemoize(keepStrongly = 50 * sf, keepWeakly = 20 * sf, name = "ParseFile")
342+
member _.CacheSizes = cs
343+
344+
member val ParseFile = AsyncMemoize(keepStrongly = cs.ParseFileKeepStrongly, keepWeakly = cs.ParseFileKeepWeakly, name = "ParseFile")
269345

270346
member val ParseFileWithoutProject =
271-
AsyncMemoize<string, string, FSharpParseFileResults>(keepStrongly = 5 * sf, keepWeakly = 2 * sf, name = "ParseFileWithoutProject")
347+
AsyncMemoize<string, string, FSharpParseFileResults>(
348+
cs.ParseFileWithoutProjectKeepStrongly,
349+
keepWeakly = cs.ParseFileWithoutProjectKeepWeakly,
350+
name = "ParseFileWithoutProject"
351+
)
272352

273-
member val ParseAndCheckFileInProject = AsyncMemoize(sf, 2 * sf, name = "ParseAndCheckFileInProject")
353+
member val ParseAndCheckFileInProject =
354+
AsyncMemoize(
355+
cs.ParseAndCheckFileInProjectKeepStrongly,
356+
cs.ParseAndCheckFileInProjectKeepWeakly,
357+
name = "ParseAndCheckFileInProject"
358+
)
274359

275-
member val ParseAndCheckAllFilesInProject = AsyncMemoizeDisabled(sf, 2 * sf, name = "ParseAndCheckFullProject")
360+
member val ParseAndCheckAllFilesInProject =
361+
AsyncMemoizeDisabled(
362+
cs.ParseAndCheckAllFilesInProjectKeepStrongly,
363+
cs.ParseAndCheckAllFilesInProjectKeepWeakly,
364+
name = "ParseAndCheckFullProject"
365+
)
276366

277-
member val ParseAndCheckProject = AsyncMemoize(sf, 2 * sf, name = "ParseAndCheckProject")
367+
member val ParseAndCheckProject =
368+
AsyncMemoize(cs.ParseAndCheckProjectKeepStrongly, cs.ParseAndCheckProjectKeepWeakly, name = "ParseAndCheckProject")
278369

279-
member val FrameworkImports = AsyncMemoize(sf, 2 * sf, name = "FrameworkImports")
370+
member val FrameworkImports = AsyncMemoize(cs.FrameworkImportsKeepStrongly, cs.FrameworkImportsKeepWeakly, name = "FrameworkImports")
280371

281-
member val BootstrapInfoStatic = AsyncMemoize(sf, 2 * sf, name = "BootstrapInfoStatic")
372+
member val BootstrapInfoStatic =
373+
AsyncMemoize(cs.BootstrapInfoStaticKeepStrongly, cs.BootstrapInfoStaticKeepWeakly, name = "BootstrapInfoStatic")
282374

283-
member val BootstrapInfo = AsyncMemoize(sf, 2 * sf, name = "BootstrapInfo")
375+
member val BootstrapInfo = AsyncMemoize(cs.BootstrapInfoKeepStrongly, cs.BootstrapInfoKeepWeakly, name = "BootstrapInfo")
284376

285-
member val TcLastFile = AsyncMemoizeDisabled(sf, 2 * sf, name = "TcLastFile")
377+
member val TcLastFile = AsyncMemoizeDisabled(cs.TcLastFileKeepStrongly, cs.TcLastFileKeepWeakly, name = "TcLastFile")
286378

287-
member val TcIntermediate = AsyncMemoize(20 * sf, 20 * sf, name = "TcIntermediate")
379+
member val TcIntermediate = AsyncMemoize(cs.TcIntermediateKeepStrongly, cs.TcIntermediateKeepWeakly, name = "TcIntermediate")
288380

289-
member val DependencyGraph = AsyncMemoize(sf, 2 * sf, name = "DependencyGraph")
381+
member val DependencyGraph = AsyncMemoize(cs.DependencyGraphKeepStrongly, cs.DependencyGraphKeepWeakly, name = "DependencyGraph")
290382

291-
member val ProjectExtras = AsyncMemoizeDisabled(sf, 2 * sf, name = "ProjectExtras")
383+
member val ProjectExtras = AsyncMemoizeDisabled(cs.ProjectExtrasKeepStrongly, cs.ProjectExtrasKeepWeakly, name = "ProjectExtras")
292384

293-
member val AssemblyData = AsyncMemoize(sf, 2 * sf, name = "AssemblyData")
385+
member val AssemblyData = AsyncMemoize(cs.AssemblyDataKeepStrongly, cs.AssemblyDataKeepWeakly, name = "AssemblyData")
294386

295-
member val SemanticClassification = AsyncMemoize(sf, 2 * sf, name = "SemanticClassification")
387+
member val SemanticClassification =
388+
AsyncMemoize(cs.SemanticClassificationKeepStrongly, cs.SemanticClassificationKeepWeakly, name = "SemanticClassification")
296389

297-
member val ItemKeyStore = AsyncMemoize(sf, 2 * sf, name = "ItemKeyStore")
390+
member val ItemKeyStore = AsyncMemoize(cs.ItemKeyStoreKeepStrongly, cs.ItemKeyStoreKeepWeakly, name = "ItemKeyStore")
298391

299-
member val ScriptClosure = AsyncMemoize(sf, 2 * sf, name = "ScriptClosure")
392+
member val ScriptClosure = AsyncMemoize(cs.ScriptClosureKeepStrongly, cs.ScriptClosureKeepWeakly, name = "ScriptClosure")
300393

301394
member this.Clear(projects: Set<FSharpProjectIdentifier>) =
302395
let shouldClear project = projects |> Set.contains project
@@ -326,7 +419,8 @@ type internal TransparentCompiler
326419
parallelReferenceResolution,
327420
captureIdentifiersWhenParsing,
328421
getSource: (string -> Async<ISourceText option>) option,
329-
useChangeNotifications
422+
useChangeNotifications,
423+
?cacheSizes
330424
) as self =
331425

332426
let documentSource =
@@ -337,8 +431,10 @@ type internal TransparentCompiler
337431
// Is having just one of these ok?
338432
let lexResourceManager = Lexhelp.LexResourceManager()
339433

434+
let cacheSizes = defaultArg cacheSizes CacheSizes.Default
435+
340436
// Mutable so we can easily clear them by creating a new instance
341-
let mutable caches = CompilerCaches(100)
437+
let mutable caches = CompilerCaches(cacheSizes)
342438

343439
// TODO: do we need this?
344440
//let maxTypeCheckingParallelism = max 1 (Environment.ProcessorCount / 2)
@@ -1371,17 +1467,6 @@ type internal TransparentCompiler
13711467
node,
13721468
(fun tcInfo ->
13731469

1374-
if tcInfo.stateContainsNodes |> Set.contains fileNode then
1375-
failwith $"Oops!"
1376-
1377-
//if
1378-
// tcInfo.stateContainsNodes
1379-
// Signature files don't have to be right above the impl file... if we need this check then
1380-
// we need to do it differently
1381-
// |> Set.contains (NodeToTypeCheck.ArtificialImplFile(index - 1))
1382-
//then
1383-
// failwith $"Oops???"
1384-
13851470
let partialResult, tcState = finisher tcInfo.tcState
13861471

13871472
let tcEnv, topAttribs, _checkImplFileOpt, ccuSigForFile = partialResult
@@ -1417,15 +1502,6 @@ type internal TransparentCompiler
14171502
fileNode,
14181503
(fun tcInfo ->
14191504

1420-
if tcInfo.stateContainsNodes |> Set.contains fileNode then
1421-
failwith $"Oops!"
1422-
1423-
// if
1424-
// tcInfo.stateContainsNodes
1425-
// |> Set.contains (NodeToTypeCheck.PhysicalFile(index + 1))
1426-
// then
1427-
// failwith $"Oops!!!"
1428-
14291505
let parsedInput = projectSnapshot.SourceFiles[index].ParsedInput
14301506
let prefixPathOpt = None
14311507
// Retrieve the type-checked signature information and add it to the TcEnvFromImpls.
@@ -2087,9 +2163,13 @@ type internal TransparentCompiler
20872163

20882164
member _.Caches = caches
20892165

2090-
member _.SetCacheSizeFactor(sizeFactor: int) =
2091-
if sizeFactor <> caches.SizeFactor then
2092-
caches <- CompilerCaches(sizeFactor)
2166+
member _.SetCacheSize(cacheSize: CacheSizes) =
2167+
if cacheSize <> caches.CacheSizes then
2168+
caches <- CompilerCaches(cacheSize)
2169+
2170+
member x.SetCacheSizeFactor(sizeFactor: int) =
2171+
let newCacheSize = CacheSizes.Create sizeFactor
2172+
x.SetCacheSize newCacheSize
20932173

20942174
interface IBackgroundCompiler with
20952175

@@ -2151,7 +2231,7 @@ type internal TransparentCompiler
21512231

21522232
member _.ClearCaches() : unit =
21532233
backgroundCompiler.ClearCaches()
2154-
caches <- CompilerCaches(100) // TODO: check
2234+
caches <- CompilerCaches(cacheSizes) // TODO: check
21552235

21562236
member _.DownsizeCaches() : unit = backgroundCompiler.DownsizeCaches()
21572237

src/Compiler/Service/TransparentCompiler.fsi

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,46 @@ type internal Extensions =
9898
fileSnapshots: #ProjectSnapshot.IFileSnapshot list * ?extraKeyFlag: DependencyGraphType ->
9999
ICacheKey<(DependencyGraphType option * byte array), string>
100100

101+
[<Experimental("This FCS type is experimental and will likely change or be removed in the future.")>]
102+
type CacheSizes =
103+
{ ParseFileKeepStrongly: int
104+
ParseFileKeepWeakly: int
105+
ParseFileWithoutProjectKeepStrongly: int
106+
ParseFileWithoutProjectKeepWeakly: int
107+
ParseAndCheckFileInProjectKeepStrongly: int
108+
ParseAndCheckFileInProjectKeepWeakly: int
109+
ParseAndCheckAllFilesInProjectKeepStrongly: int
110+
ParseAndCheckAllFilesInProjectKeepWeakly: int
111+
ParseAndCheckProjectKeepStrongly: int
112+
ParseAndCheckProjectKeepWeakly: int
113+
FrameworkImportsKeepStrongly: int
114+
FrameworkImportsKeepWeakly: int
115+
BootstrapInfoStaticKeepStrongly: int
116+
BootstrapInfoStaticKeepWeakly: int
117+
BootstrapInfoKeepStrongly: int
118+
BootstrapInfoKeepWeakly: int
119+
TcLastFileKeepStrongly: int
120+
TcLastFileKeepWeakly: int
121+
TcIntermediateKeepStrongly: int
122+
TcIntermediateKeepWeakly: int
123+
DependencyGraphKeepStrongly: int
124+
DependencyGraphKeepWeakly: int
125+
ProjectExtrasKeepStrongly: int
126+
ProjectExtrasKeepWeakly: int
127+
AssemblyDataKeepStrongly: int
128+
AssemblyDataKeepWeakly: int
129+
SemanticClassificationKeepStrongly: int
130+
SemanticClassificationKeepWeakly: int
131+
ItemKeyStoreKeepStrongly: int
132+
ItemKeyStoreKeepWeakly: int
133+
ScriptClosureKeepStrongly: int
134+
ScriptClosureKeepWeakly: int }
135+
136+
static member Create: sizeFactor: int -> CacheSizes
137+
101138
type internal CompilerCaches =
102139

103-
new: sizeFactor: int -> CompilerCaches
140+
new: cacheSizes: CacheSizes -> CompilerCaches
104141

105142
member AssemblyData: AsyncMemoize<FSharpProjectIdentifier, (string * string), ProjectAssemblyDataResult>
106143

@@ -134,7 +171,7 @@ type internal CompilerCaches =
134171
member SemanticClassification:
135172
AsyncMemoize<(string * FSharpProjectIdentifier), string, SemanticClassificationView option>
136173

137-
member SizeFactor: int
174+
member CacheSizes: CacheSizes
138175

139176
member TcIntermediate: AsyncMemoize<(string * FSharpProjectIdentifier), (string * int), TcIntermediate>
140177

@@ -158,7 +195,8 @@ type internal TransparentCompiler =
158195
parallelReferenceResolution: ParallelReferenceResolution *
159196
captureIdentifiersWhenParsing: bool *
160197
getSource: (string -> Async<ISourceText option>) option *
161-
useChangeNotifications: bool ->
198+
useChangeNotifications: bool *
199+
?cacheSizes: CacheSizes ->
162200
TransparentCompiler
163201

164202
member FindReferencesInFile:
@@ -177,6 +215,7 @@ type internal TransparentCompiler =
177215
fileName: string * projectSnapshot: ProjectSnapshot.ProjectSnapshot * _userOpName: 'a ->
178216
Async<FSharpParseFileResults>
179217

218+
member SetCacheSize: cacheSize: CacheSizes -> unit
180219
member SetCacheSizeFactor: sizeFactor: int -> unit
181220

182221
member Caches: CompilerCaches

0 commit comments

Comments
 (0)