Skip to content

Commit 23cc156

Browse files
author
Paul Westcott
committed
Split dict by ValueType/RefType
1 parent d4b6861 commit 23cc156

File tree

3 files changed

+32
-20
lines changed

3 files changed

+32
-20
lines changed

src/fsharp/FSharp.Core/array.fs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ namespace Microsoft.FSharp.Collections
172172

173173
Microsoft.FSharp.Primitives.Basics.Array.subUnchecked 0 count array
174174

175-
let countByImpl (comparer:IEqualityComparer<'SafeKey>) (projection:'T->'SafeKey) (getKey:'SafeKey->'Key) (array:'T[]) =
175+
let inline countByImpl (comparer:IEqualityComparer<'SafeKey>) (projection:'T->'SafeKey) (getKey:'SafeKey->'Key) (array:'T[]) =
176176
let dict = Dictionary comparer
177177

178178
// Build the groupings
@@ -423,7 +423,7 @@ namespace Microsoft.FSharp.Collections
423423
let rec loop i = i >= len1 || (f.Invoke(array1.[i], array2.[i]) && loop (i+1))
424424
loop 0
425425

426-
let groupByImpl (comparer:IEqualityComparer<'SafeKey>) (keyf:'T->'SafeKey) (getKey:'SafeKey->'Key) (array: 'T[]) =
426+
let inline groupByImpl (comparer:IEqualityComparer<'SafeKey>) (keyf:'T->'SafeKey) (getKey:'SafeKey->'Key) (array: 'T[]) =
427427
let dict = Dictionary<_,ResizeArray<_>> comparer
428428

429429
// Previously this was 1, but I think this is rather stingy, considering that we are alreadying paying

src/fsharp/FSharp.Core/fslib-extra-pervasives.fs

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,65 +30,77 @@ module ExtraTopLevelOperators =
3030
[<CompiledName("CreateSet")>]
3131
let set l = Collections.Set.ofSeq l
3232

33-
[<CompiledName("CreateDictionary")>]
34-
let dict l =
35-
// Use a dictionary (this requires hashing and equality on the key type)
36-
// Wrap keys in a StructBox in case they are null (when System.Collections.Generic.Dictionary fails).
37-
let t = new Dictionary<RuntimeHelpers.StructBox<'Key>,_>(RuntimeHelpers.StructBox<'Key>.Comparer)
33+
let inline dictImpl (comparer:IEqualityComparer<'SafeKey>) (makeSafeKey:'Key->'SafeKey) (getKey:'SafeKey->'Key) (l:seq<'Key*'T>) =
34+
let t = Dictionary comparer
3835
for (k,v) in l do
39-
t.[RuntimeHelpers.StructBox(k)] <- v
36+
t.[makeSafeKey k] <- v
4037
let d = (t :> IDictionary<_,_>)
4138
let c = (t :> ICollection<_>)
4239
// Give a read-only view of the dictionary
4340
{ new IDictionary<'Key, 'T> with
4441
member s.Item
45-
with get x = d.[RuntimeHelpers.StructBox(x)]
42+
with get x = d.[makeSafeKey x]
4643
and set x v = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated)))
4744
member s.Keys =
4845
let keys = d.Keys
4946
{ new ICollection<'Key> with
5047
member s.Add(x) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated)));
5148
member s.Clear() = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated)));
5249
member s.Remove(x) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated)));
53-
member s.Contains(x) = keys.Contains(RuntimeHelpers.StructBox(x))
50+
member s.Contains(x) = keys.Contains(makeSafeKey x)
5451
member s.CopyTo(arr,i) =
5552
let mutable n = 0
5653
for k in keys do
57-
arr.[i+n] <- k.Value
54+
arr.[i+n] <- getKey k
5855
n <- n + 1
5956
member s.IsReadOnly = true
6057
member s.Count = keys.Count
6158
interface IEnumerable<'Key> with
62-
member s.GetEnumerator() = (keys |> Seq.map (fun v -> v.Value)).GetEnumerator()
59+
member s.GetEnumerator() = (keys |> Seq.map getKey).GetEnumerator()
6360
interface System.Collections.IEnumerable with
64-
member s.GetEnumerator() = ((keys |> Seq.map (fun v -> v.Value)) :> System.Collections.IEnumerable).GetEnumerator() }
61+
member s.GetEnumerator() = ((keys |> Seq.map getKey) :> System.Collections.IEnumerable).GetEnumerator() }
6562

6663
member s.Values = d.Values
6764
member s.Add(k,v) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated)))
68-
member s.ContainsKey(k) = d.ContainsKey(RuntimeHelpers.StructBox(k))
65+
member s.ContainsKey(k) = d.ContainsKey(makeSafeKey k)
6966
member s.TryGetValue(k,r) =
70-
let key = RuntimeHelpers.StructBox(k)
67+
let key = makeSafeKey k
7168
if d.ContainsKey(key) then (r <- d.[key]; true) else false
7269
member s.Remove(k : 'Key) = (raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated))) : bool)
7370
interface ICollection<KeyValuePair<'Key, 'T>> with
7471
member s.Add(x) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated)));
7572
member s.Clear() = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated)));
7673
member s.Remove(x) = raise (NotSupportedException(SR.GetString(SR.thisValueCannotBeMutated)));
77-
member s.Contains(KeyValue(k,v)) = c.Contains(KeyValuePair<_,_>(RuntimeHelpers.StructBox(k),v))
74+
member s.Contains(KeyValue(k,v)) = c.Contains(KeyValuePair<_,_>(makeSafeKey k,v))
7875
member s.CopyTo(arr,i) =
7976
let mutable n = 0
8077
for (KeyValue(k,v)) in c do
81-
arr.[i+n] <- KeyValuePair<_,_>(k.Value,v)
78+
arr.[i+n] <- KeyValuePair<_,_>(getKey k,v)
8279
n <- n + 1
8380
member s.IsReadOnly = true
8481
member s.Count = c.Count
8582
interface IEnumerable<KeyValuePair<'Key, 'T>> with
8683
member s.GetEnumerator() =
87-
(c |> Seq.map (fun (KeyValue(k,v)) -> KeyValuePair<_,_>(k.Value,v))).GetEnumerator()
84+
(c |> Seq.map (fun (KeyValue(k,v)) -> KeyValuePair<_,_>(getKey k,v))).GetEnumerator()
8885
interface System.Collections.IEnumerable with
8986
member s.GetEnumerator() =
90-
((c |> Seq.map (fun (KeyValue(k,v)) -> KeyValuePair<_,_>(k.Value,v))) :> System.Collections.IEnumerable).GetEnumerator() }
87+
((c |> Seq.map (fun (KeyValue(k,v)) -> KeyValuePair<_,_>(getKey k,v))) :> System.Collections.IEnumerable).GetEnumerator() }
88+
89+
// We avoid wrapping a StructBox, because under 64 JIT we get some "hard" tailcalls which affect performance
90+
let dictValueType (l:seq<'Key*'T>) = dictImpl HashIdentity.Structural<'Key> id id l
91+
92+
// Wrap a StructBox around all keys in case the key type is itself a type using null as a representation
93+
let dictRefType (l:seq<'Key*'T>) = dictImpl RuntimeHelpers.StructBox<'Key>.Comparer (fun k -> RuntimeHelpers.StructBox k) (fun sb -> sb.Value) l
9194

95+
[<CompiledName("CreateDictionary")>]
96+
let dict (l:seq<'Key*'T>) =
97+
#if FX_ATLEAST_40
98+
if typeof<'Key>.IsValueType
99+
then dictValueType l
100+
else dictRefType l
101+
#else
102+
dictRefType l
103+
#endif
92104

93105
let getArray (vals : seq<'T>) =
94106
match vals with

src/fsharp/FSharp.Core/list.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ namespace Microsoft.FSharp.Collections
449449
[<CompiledName("Where")>]
450450
let where f x = Microsoft.FSharp.Primitives.Basics.List.filter f x
451451

452-
let groupByImpl (comparer:IEqualityComparer<'SafeKey>) (keyf:'T->'SafeKey) (getKey:'SafeKey->'Key) (list: 'T list) =
452+
let inline groupByImpl (comparer:IEqualityComparer<'SafeKey>) (keyf:'T->'SafeKey) (getKey:'SafeKey->'Key) (list: 'T list) =
453453
let dict = Dictionary<_,ResizeArray<_>> comparer
454454

455455
// Previously this was 1, but I think this is rather stingy, considering that we are alreadying paying

0 commit comments

Comments
 (0)