Skip to content

Commit f3ac895

Browse files
committed
non-boxing equality and comparison
1 parent 16bca5a commit f3ac895

17 files changed

Lines changed: 1299 additions & 964 deletions

File tree

‎src/fsharp/FSharp.Core/local.fs‎

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -987,8 +987,12 @@ module internal Array =
987987

988988
open System
989989

990-
let inline fastComparerForArraySort<'t when 't : comparison> () =
991-
LanguagePrimitives.FastGenericComparerCanBeNull<'t>
990+
let inline getInternalComparer<'t when 't : comparison> () =
991+
// Previously a "comparer" was returned that could be null, which was for optimized Array.Sort
992+
// but we now mainly return Comparer.Default (and FastGenericComparerInternal more so)
993+
// which is also optimized in Array.Sort
994+
// ** this comment can be destroyed sometime in the future, it is just as a breadcrumb for review **
995+
LanguagePrimitives.FastGenericComparerInternal<'t>
992996

993997
// The input parameter should be checked by callers if necessary
994998
let inline zeroCreateUnchecked (count:int) =
@@ -1086,26 +1090,26 @@ module internal Array =
10861090
let keys = zeroCreateUnchecked array.Length
10871091
for i = 0 to array.Length - 1 do
10881092
keys.[i] <- projection array.[i]
1089-
Array.Sort<_, _>(keys, array, fastComparerForArraySort())
1093+
Array.Sort<_, _>(keys, array, getInternalComparer())
10901094

10911095
let unstableSortInPlace (array : array<'T>) =
10921096
let len = array.Length
10931097
if len < 2 then ()
1094-
else Array.Sort<_>(array, fastComparerForArraySort())
1098+
else Array.Sort<_>(array, getInternalComparer())
10951099

1096-
let stableSortWithKeysAndComparer (cFast:IComparer<'Key>) (c:IComparer<'Key>) (array:array<'T>) (keys:array<'Key>) =
1100+
let stableSortWithKeysAndComparer (c:IComparer<'Key>) (array:array<'T>) (keys:array<'Key>) =
10971101
// 'places' is an array or integers storing the permutation performed by the sort
10981102
let places = zeroCreateUnchecked array.Length
10991103
for i = 0 to array.Length - 1 do
11001104
places.[i] <- i
1101-
System.Array.Sort<_, _>(keys, places, cFast)
1105+
System.Array.Sort<_, _>(keys, places, c)
11021106
// 'array2' is a copy of the original values
11031107
let array2 = (array.Clone() :?> array<'T>)
11041108

11051109
// Walk through any chunks where the keys are equal
11061110
let mutable i = 0
11071111
let len = array.Length
1108-
let intCompare = fastComparerForArraySort<int>()
1112+
let intCompare = getInternalComparer<int>()
11091113

11101114
while i < len do
11111115
let mutable j = i
@@ -1120,9 +1124,8 @@ module internal Array =
11201124
i <- j
11211125

11221126
let stableSortWithKeys (array:array<'T>) (keys:array<'Key>) =
1123-
let cFast = fastComparerForArraySort()
1124-
let c = LanguagePrimitives.FastGenericComparer<'Key>
1125-
stableSortWithKeysAndComparer cFast c array keys
1127+
let c = getInternalComparer()
1128+
stableSortWithKeysAndComparer c array keys
11261129

11271130
let stableSortInPlaceBy (projection: 'T -> 'U) (array : array<'T>) =
11281131
let len = array.Length
@@ -1138,13 +1141,11 @@ module internal Array =
11381141
let len = array.Length
11391142
if len < 2 then ()
11401143
else
1141-
let cFast = LanguagePrimitives.FastGenericComparerCanBeNull<'T>
1142-
match cFast with
1143-
| null ->
1144+
if LanguagePrimitives.EquivalentForStableAndUnstableSort<'T> then
11441145
// An optimization for the cases where the keys and values coincide and do not have identity, e.g. are integers
11451146
// In this case an unstable sort is just as good as a stable sort (and faster)
11461147
Array.Sort<_, _>(array, null)
1147-
| _ ->
1148+
else
11481149
// 'keys' is an array storing the projected keys
11491150
let keys = (array.Clone() :?> array<'T>)
11501151
stableSortWithKeys array keys
@@ -1155,7 +1156,7 @@ module internal Array =
11551156
let keys = (array.Clone() :?> array<'T>)
11561157
let comparer = OptimizedClosures.FSharpFunc<_, _, _>.Adapt(comparer)
11571158
let c = { new IComparer<'T> with member __.Compare(x, y) = comparer.Invoke(x, y) }
1158-
stableSortWithKeysAndComparer c c array keys
1159+
stableSortWithKeysAndComparer c array keys
11591160

11601161
let inline subUnchecked startIndex count (array : 'T[]) =
11611162
let res = zeroCreateUnchecked count : 'T[]
@@ -1204,4 +1205,4 @@ module internal Seq =
12041205
while (e.MoveNext()) do res <- e.Current
12051206
ValueSome(res)
12061207
else
1207-
ValueNone
1208+
ValueNone

‎src/fsharp/FSharp.Core/prim-types.fs‎

Lines changed: 855 additions & 449 deletions
Large diffs are not rendered by default.

‎src/fsharp/FSharp.Core/prim-types.fsi‎

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -959,7 +959,10 @@ namespace Microsoft.FSharp.Core
959959
val inline FastGenericComparer<'T> : System.Collections.Generic.IComparer<'T> when 'T : comparison
960960

961961
/// <summary>Make an F# comparer object for the given type, where it can be null if System.Collections.Generic.Comparer&lt;'T&gt;.Default</summary>
962-
val internal FastGenericComparerCanBeNull<'T> : System.Collections.Generic.IComparer<'T> when 'T : comparison
962+
val internal FastGenericComparerInternal<'T> : System.Collections.Generic.Comparer<'T> when 'T : comparison
963+
964+
/// As an optimization, determine if a fast unstable sort can be used with equivalent results
965+
val internal EquivalentForStableAndUnstableSort<'T> : bool
963966

964967
/// <summary>Make an F# hash/equality object for the given type</summary>
965968
val inline FastGenericEqualityComparer<'T> : System.Collections.Generic.IEqualityComparer<'T> when 'T : equality
@@ -1336,8 +1339,38 @@ namespace Microsoft.FSharp.Core
13361339
//[<CompilerMessage("This function is for use by compiled F# code and should not be used directly", 1204, IsHidden=true)>]
13371340
val inline SetArray4D : target:'T[,,,] -> index1:int -> index2:int -> index3:int -> index4:int -> value:'T -> unit
13381341

1342+
module internal Reflection =
1343+
val internal tupleNames : string []
1344+
val internal isTupleType : Type -> bool
1345+
val internal tryFindSourceConstructFlagsOfType : Type * byref<SourceConstructFlags> -> bool
1346+
val internal fieldPropsOfRecordType : Type * System.Reflection.BindingFlags -> System.Reflection.PropertyInfo[]
1347+
val internal isRecordType : Type * System.Reflection.BindingFlags -> bool
1348+
val internal getUnionTypeTagNameMap : Type * System.Reflection.BindingFlags -> (int*string)[]
1349+
val internal fieldsPropsOfUnionCase : Type * int* System.Reflection.BindingFlags -> System.Reflection.PropertyInfo[]
1350+
val internal isUnionType : Type * System.Reflection.BindingFlags -> bool
1351+
13391352
/// <summary>The F# compiler emits calls to some of the functions in this module as part of the compiled form of some language constructs</summary>
13401353
module HashCompare =
1354+
[<AbstractClass; Sealed>]
1355+
type FSharpEqualityComparer_ER<'T> =
1356+
static member EqualityComparer : System.Collections.Generic.EqualityComparer<'T>
1357+
1358+
[<AbstractClass; Sealed>]
1359+
type FSharpEqualityComparer_PER<'T> =
1360+
static member EqualityComparer : System.Collections.Generic.EqualityComparer<'T>
1361+
1362+
/// <summary>A primitive entry point used by the F# compiler for optimization purposes.</summary>
1363+
[<CompilerMessage("This function is a primitive library routine used by optimized F# code and should not be used directly", 1204, IsHidden=true)>]
1364+
val inline FSharpEqualityComparer_ER_Equals : x:'T -> y:'T -> bool
1365+
1366+
/// <summary>A primitive entry point used by the F# compiler for optimization purposes.</summary>
1367+
[<CompilerMessage("This function is a primitive library routine used by optimized F# code and should not be used directly", 1204, IsHidden=true)>]
1368+
val inline FSharpEqualityComparer_PER_Equals : x:'T -> y:'T -> bool
1369+
1370+
/// <summary>A primitive entry point used by the F# compiler for optimization purposes.</summary>
1371+
[<CompilerMessage("This function is a primitive library routine used by optimized F# code and should not be used directly", 1204, IsHidden=true)>]
1372+
val inline FSharpEqualityComparer_GetHashCode : x:'T -> int
1373+
13411374
/// <summary>A primitive entry point used by the F# compiler for optimization purposes.</summary>
13421375
[<CompilerMessage("This function is a primitive library routine used by optimized F# code and should not be used directly", 1204, IsHidden=true)>]
13431376
val PhysicalHashIntrinsic : input:'T -> int when 'T : not struct

0 commit comments

Comments
 (0)