Skip to content

Perf: reduce allocations and redundant work in hot paths#1514

Open
Thorium wants to merge 1 commit intoionide:mainfrom
Thorium:perf-improvement
Open

Perf: reduce allocations and redundant work in hot paths#1514
Thorium wants to merge 1 commit intoionide:mainfrom
Thorium:perf-improvement

Conversation

@Thorium
Copy link
Copy Markdown
Contributor

@Thorium Thorium commented Mar 26, 2026

Performance improvements:

  • FileSystem.fs: Cache Lines property on RoslynSourceTextFile via lazy, avoiding repeated Seq.toArray + Array.map on every access. Replace ToString().GetBytes() in GetFileContent with chunked ISourceText.CopyTo to avoid materializing the entire source as an intermediate string. Uses UTF8Encoding(false) to avoid BOM, matching original behavior.

  • Lexer.fs: Replace Array.fold with a simple for-loop for parsing defines and lang version from args. Each call creates its own FSharpSourceTokenizer (they are lightweight); no shared mutable state across threads.

  • CompilerServiceInterface.fs: Move normalizePath into each match branch of SourceFilesTagged to avoid an extra List.map pass. Replace quadratic Array.append fold in processFSIArgs with ResizeArray for O(n) behavior.

  • AdaptiveServerState.fs: Cache SourceFilesTagged on LoadedProject via a Lazy field and factory method, avoiding repeated List.map + List.toArray on every access. Replace OTel tag 'source.text' (which boxed entire file contents as a string) with 'source.length' (just the int length).

  • AdaptiveFSharpLspServer.fs: Add rereadFile parameter to completion retry logic so the file is only re-read when the error indicates stale content (e.g. line lookup failure, trigger char mismatch), not on every retry. The 'empty completions' case skips re-read since only typecheck matters.

Performance improvements:

- FileSystem.fs: Cache Lines property on RoslynSourceTextFile via lazy,
  avoiding repeated Seq.toArray + Array.map on every access. Replace
  ToString().GetBytes() in GetFileContent with chunked ISourceText.CopyTo
  to avoid materializing the entire source as an intermediate string.
  Uses UTF8Encoding(false) to avoid BOM, matching original behavior.

- Lexer.fs: Replace Array.fold with a simple for-loop for parsing defines
  and lang version from args. Each call creates its own FSharpSourceTokenizer
  (they are lightweight); no shared mutable state across threads.

- CompilerServiceInterface.fs: Move normalizePath into each match branch of
  SourceFilesTagged to avoid an extra List.map pass. Replace quadratic
  Array.append fold in processFSIArgs with ResizeArray for O(n) behavior.

- AdaptiveServerState.fs: Cache SourceFilesTagged on LoadedProject via a
  Lazy field and factory method, avoiding repeated List.map + List.toArray
  on every access. Replace OTel tag 'source.text' (which boxed entire file
  contents as a string) with 'source.length' (just the int length).

- AdaptiveFSharpLspServer.fs: Add rereadFile parameter to completion retry
  logic so the file is only re-read when the error indicates stale content
  (e.g. line lookup failure, trigger char mismatch), not on every retry.
  The 'empty completions' case skips re-read since only typecheck matters.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant