From 84c5478434a6a0cb4f1a1abc09b205fb44568d25 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Tue, 20 Aug 2024 15:12:55 +0200 Subject: [PATCH 1/2] Use concurrent dictionary in NameMultiMap for CE yield cache (#17565) --- src/Compiler/Checking/CheckDeclarations.fs | 2 +- src/Compiler/Utilities/HashMultiMap.fs | 70 +++++++++++++--------- src/Compiler/Utilities/HashMultiMap.fsi | 6 +- 3 files changed, 47 insertions(+), 31 deletions(-) diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index be083e74871..2e49738e7e5 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -5540,7 +5540,7 @@ let emptyTcEnv g = eCallerMemberName = None eLambdaArgInfos = [] eIsControlFlow = false - eCachedImplicitYieldExpressions = HashMultiMap(HashIdentity.Structural) } + eCachedImplicitYieldExpressions = HashMultiMap(HashIdentity.Structural, useConcurrentDictionary = true) } let CreateInitialTcEnv(g, amap, scopem, assemblyName, ccus) = (emptyTcEnv g, ccus) ||> List.collectFold (fun env (ccu, autoOpens, internalsVisible) -> diff --git a/src/Compiler/Utilities/HashMultiMap.fs b/src/Compiler/Utilities/HashMultiMap.fs index b88af5d77eb..c0fc897121d 100644 --- a/src/Compiler/Utilities/HashMultiMap.fs +++ b/src/Compiler/Utilities/HashMultiMap.fs @@ -3,44 +3,60 @@ namespace Internal.Utilities.Collections open System.Collections.Generic +open System.Collections.Concurrent // Each entry in the HashMultiMap dictionary has at least one entry. Under normal usage each entry has _only_ // one entry. So use two hash tables: one for the main entries and one for the overflow. [] -type internal HashMultiMap<'Key, 'Value>(size: int, comparer: IEqualityComparer<'Key>) = - - let firstEntries = Dictionary<_, _>(size, comparer) - - let rest = Dictionary<_, _>(3, comparer) - - new(comparer: IEqualityComparer<'Key>) = HashMultiMap<'Key, 'Value>(11, comparer) - - new(entries: seq<'Key * 'Value>, comparer: IEqualityComparer<'Key>) as x = - new HashMultiMap<'Key, 'Value>(11, comparer) - then entries |> Seq.iter (fun (k, v) -> x.Add(k, v)) - - member x.GetRest(k) = +type internal HashMultiMap<'Key, 'Value +#if !NO_CHECKNULLS + when 'Key:not null +#endif + >(size: int, comparer: IEqualityComparer<'Key>, ?useConcurrentDictionary: bool) = + + let comparer = comparer + + let firstEntries: IDictionary<_, _> = + if defaultArg useConcurrentDictionary false then + ConcurrentDictionary<_, _>(comparer) + else + Dictionary<_, _>(size, comparer) + + let rest: IDictionary<_, _> = + if defaultArg useConcurrentDictionary false then + ConcurrentDictionary<_, _>(comparer) + else + Dictionary<_, _>(3, comparer) + + new(comparer: IEqualityComparer<'Key>, ?useConcurrentDictionary: bool) = + HashMultiMap<'Key, 'Value>(11, comparer, defaultArg useConcurrentDictionary false) + + new(entries: seq<'Key * 'Value>, comparer: IEqualityComparer<'Key>, ?useConcurrentDictionary: bool) as this = + HashMultiMap<'Key, 'Value>(11, comparer, defaultArg useConcurrentDictionary false) + then entries |> Seq.iter (fun (k, v) -> this.Add(k, v)) + + member _.GetRest(k) = match rest.TryGetValue k with | true, res -> res | _ -> [] - member x.Add(y, z) = + member this.Add(y, z) = match firstEntries.TryGetValue y with - | true, res -> rest[y] <- res :: x.GetRest(y) + | true, res -> rest[y] <- res :: this.GetRest(y) | _ -> () firstEntries[y] <- z - member x.Clear() = + member _.Clear() = firstEntries.Clear() rest.Clear() - member x.FirstEntries = firstEntries + member _.FirstEntries = firstEntries - member x.Rest = rest + member _.Rest = rest - member x.Copy() = - let res = HashMultiMap<'Key, 'Value>(firstEntries.Count, firstEntries.Comparer) + member _.Copy() = + let res = HashMultiMap<'Key, 'Value>(firstEntries.Count, comparer) for kvp in firstEntries do res.FirstEntries.Add(kvp.Key, kvp.Value) @@ -86,11 +102,11 @@ type internal HashMultiMap<'Key, 'Value>(size: int, comparer: IEqualityComparer< for z in rest do f kvp.Key z - member x.Contains(y) = firstEntries.ContainsKey(y) + member _.Contains(y) = firstEntries.ContainsKey(y) - member x.ContainsKey(y) = firstEntries.ContainsKey(y) + member _.ContainsKey(y) = firstEntries.ContainsKey(y) - member x.Remove(y) = + member _.Remove(y) = match firstEntries.TryGetValue y with // NOTE: If not ok then nothing to remove - nop | true, _res -> @@ -108,14 +124,14 @@ type internal HashMultiMap<'Key, 'Value>(size: int, comparer: IEqualityComparer< | _ -> firstEntries.Remove(y) |> ignore | _ -> () - member x.Replace(y, z) = firstEntries[y] <- z + member _.Replace(y, z) = firstEntries[y] <- z - member x.TryFind(y) = + member _.TryFind(y) = match firstEntries.TryGetValue y with | true, res -> Some res | _ -> None - member x.Count = firstEntries.Count + member _.Count = firstEntries.Count interface IEnumerable> with @@ -184,6 +200,6 @@ type internal HashMultiMap<'Key, 'Value>(size: int, comparer: IEqualityComparer< member s.CopyTo(arr, arrIndex) = s |> Seq.iteri (fun j x -> arr[arrIndex + j] <- x) - member s.IsReadOnly = false + member _.IsReadOnly = false member s.Count = s.Count diff --git a/src/Compiler/Utilities/HashMultiMap.fsi b/src/Compiler/Utilities/HashMultiMap.fsi index a4dd51a5bba..afca303d2c1 100644 --- a/src/Compiler/Utilities/HashMultiMap.fsi +++ b/src/Compiler/Utilities/HashMultiMap.fsi @@ -9,14 +9,14 @@ open System.Collections.Generic [] type internal HashMultiMap<'Key, 'Value> = /// Create a new empty mutable HashMultiMap with the given key hash/equality functions. - new: comparer: IEqualityComparer<'Key> -> HashMultiMap<'Key, 'Value> + new: comparer: IEqualityComparer<'Key> * ?useConcurrentDictionary: bool -> HashMultiMap<'Key, 'Value> /// Create a new empty mutable HashMultiMap with an internal bucket array of the given approximate size /// and with the given key hash/equality functions. - new: size: int * comparer: IEqualityComparer<'Key> -> HashMultiMap<'Key, 'Value> + new: size: int * comparer: IEqualityComparer<'Key> * ?useConcurrentDictionary: bool -> HashMultiMap<'Key, 'Value> /// Build a map that contains the bindings of the given IEnumerable. - new: entries: seq<'Key * 'Value> * comparer: IEqualityComparer<'Key> -> HashMultiMap<'Key, 'Value> + new: entries: seq<'Key * 'Value> * comparer: IEqualityComparer<'Key> * ?useConcurrentDictionary: bool -> HashMultiMap<'Key, 'Value> /// Make a shallow copy of the collection. member Copy: unit -> HashMultiMap<'Key, 'Value> From 22fcebc3e2cbd970adaa8c083c9ce1a6d529dc9d Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Tue, 20 Aug 2024 15:32:24 +0200 Subject: [PATCH 2/2] Fixes --- src/Compiler/Utilities/HashMultiMap.fs | 6 +----- src/Compiler/Utilities/HashMultiMap.fsi | 4 +++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Compiler/Utilities/HashMultiMap.fs b/src/Compiler/Utilities/HashMultiMap.fs index c0fc897121d..c502ee91240 100644 --- a/src/Compiler/Utilities/HashMultiMap.fs +++ b/src/Compiler/Utilities/HashMultiMap.fs @@ -8,11 +8,7 @@ open System.Collections.Concurrent // Each entry in the HashMultiMap dictionary has at least one entry. Under normal usage each entry has _only_ // one entry. So use two hash tables: one for the main entries and one for the overflow. [] -type internal HashMultiMap<'Key, 'Value -#if !NO_CHECKNULLS - when 'Key:not null -#endif - >(size: int, comparer: IEqualityComparer<'Key>, ?useConcurrentDictionary: bool) = +type internal HashMultiMap<'Key, 'Value>(size: int, comparer: IEqualityComparer<'Key>, ?useConcurrentDictionary: bool) = let comparer = comparer diff --git a/src/Compiler/Utilities/HashMultiMap.fsi b/src/Compiler/Utilities/HashMultiMap.fsi index afca303d2c1..a09a6dbb68e 100644 --- a/src/Compiler/Utilities/HashMultiMap.fsi +++ b/src/Compiler/Utilities/HashMultiMap.fsi @@ -16,7 +16,9 @@ type internal HashMultiMap<'Key, 'Value> = new: size: int * comparer: IEqualityComparer<'Key> * ?useConcurrentDictionary: bool -> HashMultiMap<'Key, 'Value> /// Build a map that contains the bindings of the given IEnumerable. - new: entries: seq<'Key * 'Value> * comparer: IEqualityComparer<'Key> * ?useConcurrentDictionary: bool -> HashMultiMap<'Key, 'Value> + new: + entries: seq<'Key * 'Value> * comparer: IEqualityComparer<'Key> * ?useConcurrentDictionary: bool -> + HashMultiMap<'Key, 'Value> /// Make a shallow copy of the collection. member Copy: unit -> HashMultiMap<'Key, 'Value>