Skip to content

Commit

Permalink
Split dict by ValueType/RefType
Browse files Browse the repository at this point in the history
  • Loading branch information
Paul Westcott committed Jul 21, 2015
1 parent d4b6861 commit 23cc156
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 20 deletions.
4 changes: 2 additions & 2 deletions src/fsharp/FSharp.Core/array.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
46 changes: 29 additions & 17 deletions src/fsharp/FSharp.Core/fslib-extra-pervasives.fs
Original file line number Diff line number Diff line change
Expand Up @@ -30,65 +30,77 @@ module ExtraTopLevelOperators =
[<CompiledName("CreateSet")>]
let set l = Collections.Set.ofSeq l

[<CompiledName("CreateDictionary")>]
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>,_>(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
{ new ICollection<'Key> 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(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<KeyValuePair<'Key, 'T>> 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<KeyValuePair<'Key, 'T>> 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

[<CompiledName("CreateDictionary")>]
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
Expand Down
2 changes: 1 addition & 1 deletion src/fsharp/FSharp.Core/list.fs
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ namespace Microsoft.FSharp.Collections
[<CompiledName("Where")>]
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
Expand Down

0 comments on commit 23cc156

Please sign in to comment.