From c46de2db08281ec22fec9e2e10824cce7c369a38 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Tue, 20 Aug 2024 11:46:45 +0200 Subject: [PATCH] Use concurrent dictionary in NameMultiMap for CE yield cache --- src/Compiler/Checking/CheckDeclarations.fs | 2 +- src/Compiler/Utilities/HashMultiMap.fs | 64 ++++++++++++++-------- src/Compiler/Utilities/HashMultiMap.fsi | 6 +- 3 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index 96c664d55bf..347026df9db 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -5564,7 +5564,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 3403aae4e84..9b0db4cf6ae 100644 --- a/src/Compiler/Utilities/HashMultiMap.fs +++ b/src/Compiler/Utilities/HashMultiMap.fs @@ -3,6 +3,17 @@ namespace Internal.Utilities.Collections open System.Collections.Generic +open System.Collections.Concurrent + +[] +module HashMultiMap = + type IDictionary<'K, 'V> with + // We only call it on `.Copy()` so it should be fine. + member this.GetComparer() = + match this with + | :? Dictionary<'K, 'V> as d -> d.Comparer + | :? ConcurrentDictionary<'K, 'V> as d -> d.Comparer + | _ -> failwith "Unknown dictionary type" // 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. @@ -11,40 +22,49 @@ type internal HashMultiMap<'Key, 'Value #if !NO_CHECKNULLS when 'Key:not null #endif - >(size: int, comparer: IEqualityComparer<'Key>) = + >(size: int, comparer: IEqualityComparer<'Key>, ?useConcurrentDictionary: bool) = - let firstEntries = Dictionary<_, _>(size, comparer) + let firstEntries: IDictionary<_, _> = + if defaultArg useConcurrentDictionary false then + ConcurrentDictionary<_, _>(comparer) + else + Dictionary<_, _>(size, comparer) - let rest = Dictionary<_, _>(3, comparer) + let rest: IDictionary<_, _> = + if defaultArg useConcurrentDictionary false then + ConcurrentDictionary<_, _>(comparer) + else + Dictionary<_, _>(3, comparer) - new(comparer: IEqualityComparer<'Key>) = HashMultiMap<'Key, 'Value>(11, comparer) + new(comparer: IEqualityComparer<'Key>, ?useConcurrentDictionary: bool) = + HashMultiMap<'Key, 'Value>(11, comparer, defaultArg useConcurrentDictionary false) - 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)) + 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 x.GetRest(k) = + 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, firstEntries.GetComparer()) for kvp in firstEntries do res.FirstEntries.Add(kvp.Key, kvp.Value) @@ -90,11 +110,11 @@ type internal HashMultiMap<'Key, 'Value 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 -> @@ -112,14 +132,14 @@ type internal HashMultiMap<'Key, 'Value | _ -> 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 @@ -188,6 +208,6 @@ type internal HashMultiMap<'Key, 'Value 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 60fa9dce9c4..6a4ba2ce119 100644 --- a/src/Compiler/Utilities/HashMultiMap.fsi +++ b/src/Compiler/Utilities/HashMultiMap.fsi @@ -13,14 +13,14 @@ type internal HashMultiMap<'Key, 'Value #endif > = /// 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>