Skip to content

Enable TypeSubsumptionCache for IDE use #18499

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 35 commits into from
May 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
5233567
add cache
majocha Apr 24, 2025
df70074
enable typeSubsumptionCache in IDE
majocha Apr 24, 2025
460f592
add some monitoring
majocha Apr 24, 2025
e94a9e5
Merge branch 'main' into compiler-cache
majocha Apr 24, 2025
b2a130a
yeet LFU
majocha Apr 24, 2025
4c7e044
flesh out the comparer
majocha Apr 24, 2025
a269b6a
fix sources
majocha Apr 24, 2025
874d9c1
try to deal with CI memory overload
majocha Apr 24, 2025
c266706
Merge branch 'main' into compiler-cache
majocha Apr 24, 2025
4bca2df
replace singleton with CWT again
majocha Apr 25, 2025
9ba405d
remove sketchy logic
majocha Apr 25, 2025
5c6a500
cache of caches
majocha Apr 25, 2025
605c967
Merge branch 'main' into compiler-cache
majocha Apr 25, 2025
3046f88
ilver
majocha Apr 25, 2025
80dc554
events -> counters
majocha Apr 25, 2025
6926f97
precompute hash
majocha Apr 25, 2025
ec8fab4
no metrics in ci
majocha Apr 26, 2025
59cb94e
simplify
majocha Apr 26, 2025
b8ba293
simplify
majocha Apr 26, 2025
13cded8
clean up
majocha Apr 26, 2025
365ae5b
cut unnecessary stuff
majocha Apr 26, 2025
96fa966
add name
majocha Apr 27, 2025
2743a4d
naming
majocha Apr 27, 2025
8b2b986
basic tests
majocha Apr 28, 2025
8e4f662
Merge branch 'main' into compiler-cache
majocha Apr 28, 2025
bf17afa
rn
majocha Apr 28, 2025
d49e276
fix tests
majocha Apr 28, 2025
f7f766e
Merge branch 'main' into compiler-cache
majocha Apr 28, 2025
aa76a84
ilver
majocha Apr 28, 2025
a182b36
use MailboxProcessor for lock-free eviction handling
majocha Apr 29, 2025
d6b3ac9
ilverify
majocha Apr 29, 2025
523fea3
return
majocha Apr 29, 2025
9c70966
restore cts
majocha Apr 29, 2025
4ae54c0
Merge branch 'main' into compiler-cache
majocha Apr 30, 2025
95b8115
Merge branch 'main' into compiler-cache
majocha Apr 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/9.0.300.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
* Type parameter constraint `null` in generic code will now automatically imply `not struct` ([Issue #18320](https://github.com/dotnet/fsharp/issues/18320), [PR #18323](https://github.com/dotnet/fsharp/pull/18323))
* Add a switch to determine whether to generate a default implementation body for overridden method when completing. [PR #18341](https://github.com/dotnet/fsharp/pull/18341)
* Use a more accurate range for CE Combine methods. [PR #18394](https://github.com/dotnet/fsharp/pull/18394)
* Enable TypeSubsumptionCache for IDE use. [PR #18499](https://github.com/dotnet/fsharp/pull/18499)


### Changed
Expand Down
8 changes: 4 additions & 4 deletions src/Compiler/Checking/TypeRelations.fs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ let TypesFeasiblyEquivStripMeasures g amap m ty1 ty2 =
TypesFeasiblyEquivalent true 0 g amap m ty1 ty2

let inline TryGetCachedTypeSubsumption (g: TcGlobals) (amap: ImportMap) key =
if g.compilationMode = CompilationMode.OneOff && g.langVersion.SupportsFeature LanguageFeature.UseTypeSubsumptionCache then
if g.langVersion.SupportsFeature LanguageFeature.UseTypeSubsumptionCache then
match amap.TypeSubsumptionCache.TryGetValue(key) with
| true, subsumes ->
ValueSome subsumes
Expand All @@ -112,8 +112,8 @@ let inline TryGetCachedTypeSubsumption (g: TcGlobals) (amap: ImportMap) key =
ValueNone

let inline UpdateCachedTypeSubsumption (g: TcGlobals) (amap: ImportMap) key subsumes : unit =
if g.compilationMode = CompilationMode.OneOff && g.langVersion.SupportsFeature LanguageFeature.UseTypeSubsumptionCache then
amap.TypeSubsumptionCache[key] <- subsumes
if g.langVersion.SupportsFeature LanguageFeature.UseTypeSubsumptionCache then
amap.TypeSubsumptionCache.TryAdd(key, subsumes) |> ignore

/// The feasible coercion relation. Part of the language spec.
let rec TypeFeasiblySubsumesType ndeep (g: TcGlobals) (amap: ImportMap) m (ty1: TType) (canCoerce: CanCoerce) (ty2: TType) =
Expand All @@ -125,7 +125,7 @@ let rec TypeFeasiblySubsumesType ndeep (g: TcGlobals) (amap: ImportMap) m (ty1:
let ty2 = stripTyEqns g ty2

// Check if language feature supported
let key = TTypeCacheKey.FromStrippedTypes (ty1, ty2, canCoerce, g)
let key = TTypeCacheKey.FromStrippedTypes (ty1, ty2, canCoerce)

match TryGetCachedTypeSubsumption g amap key with
| ValueSome subsumes ->
Expand Down
42 changes: 22 additions & 20 deletions src/Compiler/Checking/import.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,25 @@ module internal FSharp.Compiler.Import
open System.Collections.Concurrent
open System.Collections.Generic
open System.Collections.Immutable
open FSharp.Compiler.Text.Range
open System.Diagnostics

open Internal.Utilities.Library
open Internal.Utilities.Library.Extras
open Internal.Utilities.TypeHashing
open Internal.Utilities.TypeHashing.HashTypes

open FSharp.Compiler
open FSharp.Compiler.AbstractIL.IL
open FSharp.Compiler.CompilerGlobalState
open FSharp.Compiler.DiagnosticsLogger
open FSharp.Compiler.SyntaxTreeOps
open FSharp.Compiler.Text
open FSharp.Compiler.Text.Range
open FSharp.Compiler.Xml
open FSharp.Compiler.TypedTree
open FSharp.Compiler.TypedTreeBasics
open FSharp.Compiler.TypedTreeOps
open FSharp.Compiler.TcGlobals
open FSharp.Compiler.Caches

#if !NO_TYPEPROVIDERS
open FSharp.Compiler.TypeProviders
Expand Down Expand Up @@ -52,18 +55,18 @@ type CanCoerce =
| CanCoerce
| NoCoerce

type [<Struct; NoComparison; CustomEquality>] TTypeCacheKey =
[<Struct; NoComparison; CustomEquality; DebuggerDisplay("{ToString()}")>]
type TTypeCacheKey =

val ty1: TType
val ty2: TType
val canCoerce: CanCoerce
val tcGlobals: TcGlobals

private new (ty1, ty2, canCoerce, tcGlobals) =
{ ty1 = ty1; ty2 = ty2; canCoerce = canCoerce; tcGlobals = tcGlobals }
private new (ty1, ty2, canCoerce) =
{ ty1 = ty1; ty2 = ty2; canCoerce = canCoerce }

static member FromStrippedTypes (ty1, ty2, canCoerce, tcGlobals) =
TTypeCacheKey(ty1, ty2, canCoerce, tcGlobals)
static member FromStrippedTypes (ty1, ty2, canCoerce) =
TTypeCacheKey(ty1, ty2, canCoerce)

interface System.IEquatable<TTypeCacheKey> with
member this.Equals other =
Expand All @@ -72,23 +75,24 @@ type [<Struct; NoComparison; CustomEquality>] TTypeCacheKey =
elif this.ty1 === other.ty1 && this.ty2 === other.ty2 then
true
else
stampEquals this.tcGlobals this.ty1 other.ty1
&& stampEquals this.tcGlobals this.ty2 other.ty2
HashStamps.stampEquals this.ty1 other.ty1
&& HashStamps.stampEquals this.ty2 other.ty2

override this.Equals(other:objnull) =
match other with
| :? TTypeCacheKey as p -> (this :> System.IEquatable<TTypeCacheKey>).Equals p
| _ -> false

override this.GetHashCode() : int =
let g = this.tcGlobals

let ty1Hash = combineHash (hashStamp g this.ty1) (hashTType g this.ty1)
let ty2Hash = combineHash (hashStamp g this.ty2) (hashTType g this.ty2)
override this.GetHashCode () : int =
HashStamps.hashTType this.ty1
|> pipeToHash (HashStamps.hashTType this.ty2)
|> pipeToHash (hash this.canCoerce)

let combined = combineHash (combineHash ty1Hash ty2Hash) (hash this.canCoerce)
override this.ToString () = $"{this.ty1.DebugText}-{this.ty2.DebugText}"

combined
let typeSubsumptionCache =
// Leave most of the capacity in reserve for bursts.
lazy Cache.Create<TTypeCacheKey, bool>({ TotalCapacity = 131072; HeadroomPercentage = 75 }, name = "TypeSubsumptionCache")

//-------------------------------------------------------------------------
// Import an IL types as F# types.
Expand All @@ -106,15 +110,13 @@ type [<Struct; NoComparison; CustomEquality>] TTypeCacheKey =
type ImportMap(g: TcGlobals, assemblyLoader: AssemblyLoader) =
let typeRefToTyconRefCache = ConcurrentDictionary<ILTypeRef, TyconRef>()

let typeSubsumptionCache = ConcurrentDictionary<TTypeCacheKey, bool>(System.Environment.ProcessorCount, 1024)

member _.g = g

member _.assemblyLoader = assemblyLoader

member _.ILTypeRefToTyconRefCache = typeRefToTyconRefCache

member _.TypeSubsumptionCache = typeSubsumptionCache
member val TypeSubsumptionCache: Cache<TTypeCacheKey, bool> = typeSubsumptionCache.Value

let CanImportILScopeRef (env: ImportMap) m scoref =

Expand Down
12 changes: 5 additions & 7 deletions src/Compiler/Checking/import.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ module internal FSharp.Compiler.Import

open Internal.Utilities.Library
open FSharp.Compiler.AbstractIL.IL
open FSharp.Compiler.Caches
open FSharp.Compiler.TcGlobals
open FSharp.Compiler.Text
open FSharp.Compiler.Xml
open FSharp.Compiler.TypedTree

open System.Collections.Concurrent

#if !NO_TYPEPROVIDERS
open FSharp.Compiler.TypeProviders
#endif
Expand Down Expand Up @@ -45,15 +44,14 @@ type CanCoerce =
[<Struct; NoComparison; CustomEquality>]
type TTypeCacheKey =
interface System.IEquatable<TTypeCacheKey>
private new: ty1: TType * ty2: TType * canCoerce: CanCoerce * tcGlobals: TcGlobals -> TTypeCacheKey
private new: ty1: TType * ty2: TType * canCoerce: CanCoerce -> TTypeCacheKey

static member FromStrippedTypes:
ty1: TType * ty2: TType * canCoerce: CanCoerce * tcGlobals: TcGlobals -> TTypeCacheKey
static member FromStrippedTypes: ty1: TType * ty2: TType * canCoerce: CanCoerce -> TTypeCacheKey

val ty1: TType
val ty2: TType
val canCoerce: CanCoerce
val tcGlobals: TcGlobals

override GetHashCode: unit -> int

/// Represents a context used for converting AbstractIL .NET and provided types to F# internal compiler data structures.
Expand All @@ -73,7 +71,7 @@ type ImportMap =
member g: TcGlobals

/// Type subsumption cache
member TypeSubsumptionCache: ConcurrentDictionary<TTypeCacheKey, bool>
member TypeSubsumptionCache: Cache<TTypeCacheKey, bool>

module Nullness =

Expand Down
2 changes: 2 additions & 0 deletions src/Compiler/FSharp.Compiler.Service.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@
<Compile Include="Utilities\lib.fsi" />
<Compile Include="Utilities\lib.fs" />
<Compile Include="Utilities\DependencyGraph.fs" />
<Compile Include="Utilities\Caches.fsi" />
<Compile Include="Utilities\Caches.fs" />
<Compile Include="Utilities\LruCache.fsi" />
<Compile Include="Utilities\LruCache.fs" />
<Compile Include="Utilities\ImmutableArray.fsi" />
Expand Down
Loading