Description
Comparing value types with the equality operator (=) causes unnecessary boxing to occur. Tested on Visual Studio 2015 RC. I described the issue in this StackOverflow question.
Example:
[<Struct>]
type MyVal =
val X : int
new(x) = { X = x }
for i in 0 .. 10000000 do
(MyVal(i) = MyVal(i + 1)) |> ignore;;
Real: 00:00:00.077, CPU: 00:00:00.078, GC gen0: 38, gen1: 1, gen2: 0
My totally uninformed guess here is that the type is casted to IEquatable<_> before the strongly typed Equals is then called.
But it gets worse! If custom equality is defined:
[<Struct;CustomEquality;NoComparison>]
type MyVal =
val X : int
new(x) = { X = x }
override this.Equals(yobj) =
match yobj with
| :? MyVal as y -> y.X = this.X
| _ -> false
interface System.IEquatable<MyVal> with
member this.Equals(other) =
other.X = this.X
for i in 0 .. 10000000 do
(MyVal(i) = MyVal(i + 1)) |> ignore;;
Real: 00:00:00.497, CPU: 00:00:00.500, GC gen0: 77, gen1: 1, gen2: 0
Then the equality operator calls Equals(obj)
, boxing both operands in the process! Nevermind the casts then required. As you can see this results in roughly twice the amount of GC pressure.
Even for reference types, this is suboptimal because casts are required.
The only workaround this problem is systematically avoid use of this operator, which may not be feasible when using generic code in third-party F# libraries. I see no reason why the compiler shouldn't generate a direct call to IEquatable<T>.Equals
without boxing or casts; a call that could then be properly inlined by the CLR in many cases.
Metadata
Metadata
Assignees
Type
Projects
Status