Open cartermp opened 4 years ago
At the moment, Tokenizer
seems to be the main problem as its at the top.
The FSharpTokenInfo
allocations are from FCS's service lexing API. This object is rather large:
type FSharpTokenInfo = {
LeftColumn: int
RightColumn: int
ColorClass: FSharpTokenColorKind
CharClass: FSharpTokenCharKind
FSharpTokenTriggerClass: FSharpTokenTriggerClass
Tag: int
TokenName: string
FullMatchedLength: int }
This is a heavy weight object for something to represent a token
.
This https://github.com/dotnet/fsharp/blob/master/src/fsharp/service/ServiceLexing.fs#L1016 tried to address some of these issues, but the Lexer API that uses it doesn't keep track of previous state. The previous state is very important for the open document.
Before FSharpTokenInfo
is even used, I took a snapshot before coloring was completed (plenty of time), from which we can get some pretty nice modern art (with dotMemory):
Here:
MruCache
(i.e., retained by parseFileCase
).FSharpParseFileResults
, a little deeper, it is FSharpList<Ast+SynModuleDecl>
, which is hugeSynExpr+App
, then mostly a deeply nested Ast+SynExpr+Sequential
.I know this is all AST, and likely very hard to optimize, but these three trees together take some 60% of the memory during (I think) the lexing part.
Do note that this colorful chart is before colorization of the ComparersRegression.fs
even took place.
I thought "what if I take that file and create an application with just that single file". This would allow a more targeted analysis, but wasn't easy using Visual Studio, as it kept disappearing on me (meaning: literally disappearing, no crash window or auto-restart). The eventlog showed a hint:
Application: devenv.exe
Framework Version: v4.0.30319
Description: The application requested process termination through System.Environment.FailFast(string message).
Message: System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.CodeAnalysis.Editor.Implementation.Structure.AbstractStructureTaggerProvider`1.<ProduceTagsAsync>d__6.MoveNext()
Stack:
at System.Environment.FailFast(System.String, System.Exception)
at Microsoft.CodeAnalysis.FailFast.OnFatalException(System.Exception)
at Microsoft.CodeAnalysis.ErrorReporting.FatalError.Report(System.Exception, System.Action`1<System.Exception>)
at Microsoft.CodeAnalysis.ErrorReporting.FatalError.ReportUnlessCanceled(System.Exception)
at Microsoft.CodeAnalysis.Editor.Implementation.Structure.AbstractStructureTaggerProvider`1+<ProduceTagsAsync>d__6[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].MoveNext()
at Microsoft.CodeAnalysis.Editor.Implementation.Structure.AbstractStructureTaggerProvider`1+<ProduceTagsAsync>d__6[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].MoveNext()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.InvokeMoveNext(System.Object)
at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.Run()
at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(System.Action, Boolean, System.Threading.Tasks.Task ByRef)
at System.Threading.Tasks.Task.FinishContinuations()
at System.Threading.Tasks.Task.FinishStageThree()
at System.Threading.Tasks.Task`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].TrySetResult(System.__Canon)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].SetResult(System.__Canon)
at Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.Structure.FSharpBlockStructureService+<GetBlockStructureAsync>d__4.MoveNext()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.InvokeMoveNext(System.Object)
at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.Run()
at System.Threading.Tasks.AwaitTaskContinuation.RunOrScheduleAction(System.Action, Boolean, System.Threading.Tasks.Task ByRef)
at System.Threading.Tasks.Task.FinishContinuations()
at System.Threading.Tasks.Task.FinishStageThree()
at System.Threading.Tasks.Task`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].TrySetResult(System.__Canon)
at System.Threading.Tasks.TaskCompletionSource`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].TrySetResult(System.__Canon)
at Microsoft.VisualStudio.FSharp.Editor.RoslynHelpers+StartAsyncAsTask@113-4[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].Invoke(System.Exception)
at Microsoft.FSharp.Control.AsyncPrimitives+StartWithContinuations@916-2.Invoke(System.Runtime.ExceptionServices.ExceptionDispatchInfo)
at Microsoft.FSharp.Control.Trampoline.Execute(Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Control.AsyncReturn>)
at Microsoft.FSharp.Control.TrampolineHolder.ExecuteWithTrampoline(Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Control.AsyncReturn>)
at Microsoft.FSharp.Control.AsyncPrimitives.continuation@942[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]](Microsoft.FSharp.Control.AsyncActivation`1<System.__Canon>, Boolean, System.Threading.Tasks.Task`1<System.__Canon>)
at Microsoft.FSharp.Control.AsyncPrimitives+taskContinueWith@956[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].Invoke(System.Threading.Tasks.Task`1<System.__Canon>)
at System.Threading.Tasks.ContinuationTaskFromResultTask`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].InnerInvoke()
at System.Threading.Tasks.Task.Execute()
at System.Threading.Tasks.Task.ExecutionContextCallback(System.Object)
at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef)
at System.Threading.Tasks.Task.ExecuteEntry(Boolean)
at System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()
Similar to https://github.com/dotnet/fsharp/issues/9575 but a different service, here: http://sourceroslyn.io/#Microsoft.CodeAnalysis.EditorFeatures.Wpf/Structure/AbstractStructureTaggerProvider.cs,a40a8aa57739df85
@abelbraaksma I can't reproduce this with an internal preview build of VS with a new console app. The stack trace is coming from structured guidelines, where we pass back an empty array if there are no structured guidelines to yield back. Somehow something else is null
unexpectedly
I'll try to reliably repro it, it may have come from interaction with the IDE in that file (I scrolled through while there were still errors, in part because it later appeared that indentation after copy paste was wrong).
Another example file in the FSharp.sln that shows this behavior is pars.fs
. It is considerably smaller, but also makes VS sluggish and has memory rising through the roof (though in this case it appears that the sluggishness is not only caused by memory, it's certainly not as much as for this case, but a good example of "another F# thing that slows down VS").
ComparersRegression.fs
(nearly 60k LoC)From a 50ish second sample:
These LOH allocs are all coming from tokenizing the source, the
SourceTextData.extendTo
function: https://github.com/dotnet/fsharp/blob/be621d8725e3ea88cb832f4d457bc0bf377172ed/vsintegration/src/FSharp.Editor/LanguageService/Tokenizer.fs#L419And tokenizing source itself is the lion's share of all allocs. Note the LOH part of it: