From 23cc1564df7b9df4449ee419fd457c9f8f629564 Mon Sep 17 00:00:00 2001 From: Paul Westcott Date: Wed, 22 Jul 2015 05:53:33 +1000 Subject: [PATCH] Split dict by ValueType/RefType --- src/fsharp/FSharp.Core/array.fs | 4 +- .../FSharp.Core/fslib-extra-pervasives.fs | 46 ++++++++++++------- src/fsharp/FSharp.Core/list.fs | 2 +- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/fsharp/FSharp.Core/array.fs b/src/fsharp/FSharp.Core/array.fs index b8c0c42520c..d059cf574da 100644 --- a/src/fsharp/FSharp.Core/array.fs +++ b/src/fsharp/FSharp.Core/array.fs @@ -172,7 +172,7 @@ namespace Microsoft.FSharp.Collections Microsoft.FSharp.Primitives.Basics.Array.subUnchecked 0 count array - let countByImpl (comparer:IEqualityComparer<'SafeKey>) (projection:'T->'SafeKey) (getKey:'SafeKey->'Key) (array:'T[]) = + let inline countByImpl (comparer:IEqualityComparer<'SafeKey>) (projection:'T->'SafeKey) (getKey:'SafeKey->'Key) (array:'T[]) = let dict = Dictionary comparer // Build the groupings @@ -423,7 +423,7 @@ namespace Microsoft.FSharp.Collections let rec loop i = i >= len1 || (f.Invoke(array1.[i], array2.[i]) && loop (i+1)) loop 0 - let groupByImpl (comparer:IEqualityComparer<'SafeKey>) (keyf:'T->'SafeKey) (getKey:'SafeKey->'Key) (array: 'T[]) = + let inline groupByImpl (comparer:IEqualityComparer<'SafeKey>) (keyf:'T->'SafeKey) (getKey:'SafeKey->'Key) (array: 'T[]) = let dict = Dictionary<_,ResizeArray<_>> comparer // Previously this was 1, but I think this is rather stingy, considering that we are alreadying paying diff --git a/src/fsharp/FSharp.Core/fslib-extra-pervasives.fs b/src/fsharp/FSharp.Core/fslib-extra-pervasives.fs index 0fa7edf74e2..94c4b2883cb 100644 --- a/src/fsharp/FSharp.Core/fslib-extra-pervasives.fs +++ b/src/fsharp/FSharp.Core/fslib-extra-pervasives.fs @@ -30,19 +30,16 @@ module ExtraTopLevelOperators = [] let set l = Collections.Set.ofSeq l - [] - let dict l = - // Use a dictionary (this requires hashing and equality on the key type) - // Wrap keys in a StructBox in case they are null (when System.Collections.Generic.Dictionary fails). - let t = new Dictionary,_>(RuntimeHelpers.StructBox<'Key>.Comparer) + let inline dictImpl (comparer:IEqualityComparer<'SafeKey>) (makeSafeKey:'Key->'SafeKey) (getKey:'SafeKey->'Key) (l:seq<'Key*'T>) = + let t = Dictionary comparer for (k,v) in l do - t.[RuntimeHelpers.StructBox(k)] <- v + t.[makeSafeKey k] <- v let d = (t :> IDictionary<_,_>) let c = (t :> ICollection<_>) // Give a read-only view of the dictionary { new IDictionary<'Key, 'T> with member s.Item - with get x = d.[RuntimeHelpers.StructBox(x)] + with get x = d.[makeSafeKey x] and set x v = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))) member s.Keys = let keys = d.Keys @@ -50,45 +47,60 @@ module ExtraTopLevelOperators = member s.Add(x) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); member s.Clear() = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); member s.Remove(x) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); - member s.Contains(x) = keys.Contains(RuntimeHelpers.StructBox(x)) + member s.Contains(x) = keys.Contains(makeSafeKey x) member s.CopyTo(arr,i) = let mutable n = 0 for k in keys do - arr.[i+n] <- k.Value + arr.[i+n] <- getKey k n <- n + 1 member s.IsReadOnly = true member s.Count = keys.Count interface IEnumerable<'Key> with - member s.GetEnumerator() = (keys |> Seq.map (fun v -> v.Value)).GetEnumerator() + member s.GetEnumerator() = (keys |> Seq.map getKey).GetEnumerator() interface System.Collections.IEnumerable with - member s.GetEnumerator() = ((keys |> Seq.map (fun v -> v.Value)) :> System.Collections.IEnumerable).GetEnumerator() } + member s.GetEnumerator() = ((keys |> Seq.map getKey) :> System.Collections.IEnumerable).GetEnumerator() } member s.Values = d.Values member s.Add(k,v) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))) - member s.ContainsKey(k) = d.ContainsKey(RuntimeHelpers.StructBox(k)) + member s.ContainsKey(k) = d.ContainsKey(makeSafeKey k) member s.TryGetValue(k,r) = - let key = RuntimeHelpers.StructBox(k) + let key = makeSafeKey k if d.ContainsKey(key) then (r <- d.[key]; true) else false member s.Remove(k : 'Key) = (raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))) : bool) interface ICollection> with member s.Add(x) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); member s.Clear() = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); member s.Remove(x) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))); - member s.Contains(KeyValue(k,v)) = c.Contains(KeyValuePair<_,_>(RuntimeHelpers.StructBox(k),v)) + member s.Contains(KeyValue(k,v)) = c.Contains(KeyValuePair<_,_>(makeSafeKey k,v)) member s.CopyTo(arr,i) = let mutable n = 0 for (KeyValue(k,v)) in c do - arr.[i+n] <- KeyValuePair<_,_>(k.Value,v) + arr.[i+n] <- KeyValuePair<_,_>(getKey k,v) n <- n + 1 member s.IsReadOnly = true member s.Count = c.Count interface IEnumerable> with member s.GetEnumerator() = - (c |> Seq.map (fun (KeyValue(k,v)) -> KeyValuePair<_,_>(k.Value,v))).GetEnumerator() + (c |> Seq.map (fun (KeyValue(k,v)) -> KeyValuePair<_,_>(getKey k,v))).GetEnumerator() interface System.Collections.IEnumerable with member s.GetEnumerator() = - ((c |> Seq.map (fun (KeyValue(k,v)) -> KeyValuePair<_,_>(k.Value,v))) :> System.Collections.IEnumerable).GetEnumerator() } + ((c |> Seq.map (fun (KeyValue(k,v)) -> KeyValuePair<_,_>(getKey k,v))) :> System.Collections.IEnumerable).GetEnumerator() } + + // We avoid wrapping a StructBox, because under 64 JIT we get some "hard" tailcalls which affect performance + let dictValueType (l:seq<'Key*'T>) = dictImpl HashIdentity.Structural<'Key> id id l + + // Wrap a StructBox around all keys in case the key type is itself a type using null as a representation + let dictRefType (l:seq<'Key*'T>) = dictImpl RuntimeHelpers.StructBox<'Key>.Comparer (fun k -> RuntimeHelpers.StructBox k) (fun sb -> sb.Value) l + [] + let dict (l:seq<'Key*'T>) = +#if FX_ATLEAST_40 + if typeof<'Key>.IsValueType + then dictValueType l + else dictRefType l +#else + dictRefType l +#endif let getArray (vals : seq<'T>) = match vals with diff --git a/src/fsharp/FSharp.Core/list.fs b/src/fsharp/FSharp.Core/list.fs index 5d888107daa..20e6370d261 100644 --- a/src/fsharp/FSharp.Core/list.fs +++ b/src/fsharp/FSharp.Core/list.fs @@ -449,7 +449,7 @@ namespace Microsoft.FSharp.Collections [] let where f x = Microsoft.FSharp.Primitives.Basics.List.filter f x - let groupByImpl (comparer:IEqualityComparer<'SafeKey>) (keyf:'T->'SafeKey) (getKey:'SafeKey->'Key) (list: 'T list) = + let inline groupByImpl (comparer:IEqualityComparer<'SafeKey>) (keyf:'T->'SafeKey) (getKey:'SafeKey->'Key) (list: 'T list) = let dict = Dictionary<_,ResizeArray<_>> comparer // Previously this was 1, but I think this is rather stingy, considering that we are alreadying paying