question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Equality operator causes boxing on value types

See original GitHub issue

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.

Issue Analytics

  • State:open
  • Created 8 years ago
  • Reactions:10
  • Comments:40 (34 by maintainers)

github_iconTop GitHub Comments

7reactions
asikcommented, Jan 9, 2016

I disagree that this should resolved as by design. This is surprising behavior and a performance trap. It affects all library code that uses this operator or structural equality, like Array.contains, Array.except, Array.distinct (and the same in Seq, List); causing N allocations for an input of size N is not a small performance problem, especially for larger value types (for ex. a 4x4 matrix of floats). There is no way to know which functions to avoid except through benchmarking. This reduces F#'s appeal in low-latency or high performance applications.

5reactions
manofstickcommented, Feb 19, 2022

@christian-clausen

It is a shame. It’s not just = though, it’s usage through containers, to members in records - especially then you have to consider generics. It’s quite nasty. So making a warning would be difficult (impossible?) to cover all usages; so would it really be a good warning? (Maybe some code analyser could at least offer some hints, albeit in a limited form)

I’ve tried, and tried to get something suitable to fix this but I just didn’t get into the fsharp early enough in it’s lifecycle, and now any change is going to be a breaking one - it appears that it’s unacceptable to prefer IEquatable<>.Equals(...) over object.Equals(...) and technically this is a breaking change. But sad.

Anyway, it has lost me as an fsharp developer, which is a shame as I really love writing code in fsharp, but with advancements in csharp over the years, it’s, for the type of code I write, a lesser worse option. (I mean most people probably just don’t care about performance at this level, so it’s mostly a non-issue, which is why it’s never found enough traction to solve, and I understand that. But does make me sad.)

Read more comments on GitHub >

github_iconTop Results From Across the Web

c# - Checking equality for boxed value types of different ...
Checking equality for boxed value types of different types when boxed type is unknown. This isn't the preferred method, though, because Convert ...
Read more >
Beware of implicit boxing of value types - theburningmonk.com
Aside from benefiting from CLR's short-circuit behaviour above, another good reason to override Equals in your custom value type is because the ...
Read more >
CA2013: Do not use ReferenceEquals with value types
When comparing values using ReferenceEquals, if objA and objB are value types, they are boxed before they are passed to the ReferenceEquals ......
Read more >
How to avoid boxing value types in F# equality comparisons
First, we are redefining the (=) operator in terms of IEquatable(T).Equals. This does the correct thing, not just for value types, but for ......
Read more >
Equality Operator And Value Types In C#
IEquatable<T > interface can be implemented on a type to provide a strongly typed Equals method which also avoids boxing for value types....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found