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