Skip to content

Commit c47f8b5

Browse files
authored
Aligning equality docs with the new reality (dotnet#16816)
* up * Update optimizations-equality.md
1 parent 34df502 commit c47f8b5

File tree

3 files changed

+17
-20
lines changed

3 files changed

+17
-20
lines changed

FSharp.sln

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{4E4F41D9-8
6060
docs\large-inputs-and-stack-overflows.md = docs\large-inputs-and-stack-overflows.md
6161
docs\memory-usage.md = docs\memory-usage.md
6262
docs\optimizations.md = docs\optimizations.md
63+
docs\optimizations-equality.md = docs\optimizations-equality.md
6364
docs\overview.md = docs\overview.md
6465
EndProjectSection
6566
EndProject

docs/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Welcome to [the F# compiler and tools repository](https://github.com/dotnet/fsha
1414
* [Large inputs and stack overflows](large-inputs-and-stack-overflows.md)
1515
* [Memory usage](memory-usage.md)
1616
* [Optimizations](optimizations.md)
17+
* [Equality optimizations](optimizations-equality.md)
1718
* [Project builds](project-builds.md)
1819
* [Tooling features](tooling-features.md)
1920

docs/optimizations-equality.md

+15-20
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ This spec is about the semantics and performance of the following coding constru
1111

1212
It is also about the semantics and performance of uses of the following `FSharp.Core` constructs which, after inlining, generate code that contains an equality check at the specific `EQTYPE`
1313
* `HashIdentity.Structural<'T>`
14-
* `{Array,Seq,List}.contains`
15-
* `{Array,Seq,List}.countBy`
14+
* `{Array,Seq,List}.contains`
15+
* `{Array,Seq,List}.countBy`
1616
* `{Array,Seq,List}.groupBy`
17-
* `{Array,Seq,List}.distinct`
18-
* `{Array,Seq,List}.distinctBy`
19-
* `{Array,Seq,List}.except`
17+
* `{Array,Seq,List}.distinct`
18+
* `{Array,Seq,List}.distinctBy`
19+
* `{Array,Seq,List}.except`
2020

2121
All of which have implied equality checks. Some of these operations are inlined, see below, which in turn affects the semantics and performance of the overall operation.
2222

@@ -203,10 +203,9 @@ let f (x: float32) (y: float32) = (x = y)
203203

204204
* Semantics: User expects call to `IEquatable<T>` if present, but F# spec says call `this.Equals(box that)`, in practice these are the same
205205
* Perf expected: no boxing
206-
* Compilation today: `GenericEqualityIntrinsic<SomeStructType>`
207-
* Perf today: always boxes (Problem3 ❌)
206+
* Compilation today: EqualityComparer<T>.Default
207+
* Perf today: good ✅
208208
* [sharplab](https://sharplab.io/#v2:DYLgZgzgNALiCWwA+BYAUMApjABGHAFAB4g4DKAnhDJgLYB0AIgIY0Aq8tmA8mJNgEocFHAF5CRMcIHogA==)
209-
* Note: [#16615](https://github.com/dotnet/fsharp/pull/16615) will improve things here since we'll start avoiding boxing
210209

211210
### F# struct type (records, tuples - with compiler-generated structural equality)
212211

@@ -262,10 +261,9 @@ If we did, the devirtualizing optimization should reduce to this directly, which
262261

263262
* Semantics: User expects structural
264263
* Perf expected: User expects perf is sum of constituent parts
265-
* Compilation today: `GenericEqualityIntrinsic<uint8[]>`
266-
* Perf today: hand-optimized ([here](https://github.com/dotnet/fsharp/blob/611e4f350e119a4173a2b235eac65539ac2b61b6/src/FSharp.Core/prim-types.fs#L1562)) for some primitive element types ✅ but boxes each element if "other" is struct or generic, see Problem3 ❌, Problem4 ❌
264+
* Compilation today: either ``FSharpEqualityComparer_PER`1<uint8[]>::get_EqualityComparer().Equals(...)`` or ``FSharpEqualityComparer_PER`1<T[]>::get_EqualityComparer().Equals(...)``
265+
* Perf today: good ✅
267266
* [sharplab for `byte[]`](https://sharplab.io/#v2:DYLgZgzgPgsAUMApgFwARlQCgB4lQIwE9lEBtAXQEpVDUBeLbemy+IA=)
268-
* Note: ([#16615](https://github.com/dotnet/fsharp/pull/16615)) will improve this compiling to either ``FSharpEqualityComparer_PER`1<uint8[]>::get_EqualityComparer().Equals(...)`` or ``FSharpEqualityComparer_PER`1<T[]>::get_EqualityComparer().Equals(...)``
269267

270268
### F# large reference record/union type
271269

@@ -282,28 +280,25 @@ Here "tiny" means the compiler-generated structural equality IS inlined.
282280

283281
* Semantics: User expects structural by default
284282
* Perf expected: User expects perf is sum of constituent parts, type-specialized if generic
285-
* Compilation today: flattened, calling `GenericEqualityERIntrinsic` on struct and generic fields
286-
* Perf today: boxes on struct and generic fields, see Problem3 ❌, Problem4 ❌
287-
* Note: [#16615](https://github.com/dotnet/fsharp/pull/16615) will help, compiling to ``FSharpEqualityComparer_ER`1<!a>::get_EqualityComparer().Equals(...)`` on struct and generic fields
283+
* Compilation today: ``FSharpEqualityComparer_ER`1<!a>::get_EqualityComparer().Equals(...)``
284+
* Perf today: good ✅
288285

289286
### Generic `'T` in non-inlined generic code
290287

291288
* Semantics: User expects the PER equality semantics of whatever `'T` actually is
292289
* Perf expected: User expects no boxing
293-
* Compilation today: `GenericEqualityERIntrinsic`
294-
* Perf today: boxes if `'T` is any non-reference type (Problem4 ❌)
295-
* Note: [#16615](https://github.com/dotnet/fsharp/pull/16615) will improve this compiling to ``FSharpEqualityComparer_ER`1<!a>::get_EqualityComparer().Equals(...)``
290+
* Compilation today: ``FSharpEqualityComparer_ER`1<!a>::get_EqualityComparer().Equals(...)``
291+
* Perf today: good ✅
296292

297293
### Generic `'T` in recursive position in structural comparison
298294

299295
This case happens in structural equality for tuple types and other structural types
300296

301297
* Semantics: User expects the PER equality semantics of whatever `'T` actually is
302298
* Perf: User expects no boxing
303-
* Compilation today: `GenericEqualityWithComparerIntrinsic LanguagePrimitives.GenericComparer`
304-
* Perf today: boxes for if `'T` is any non-reference type (Problem4 ❌)
299+
* Compilation today: ``FSharpEqualityComparer_ER`1<!a>::get_EqualityComparer().Equals(...)``
300+
* Perf today: good ✅
305301
* [Sharplab](https://sharplab.io/#v2:DYLgZgzgPgsAUMApgFwARlQCgB4iwSwDs0AqVAEwHsBXAIyVTIHIAVASjdQE9UBeLbH25t48TCVFxB/LpIC0cosCJEA5goB8kgOKJCiAE74AxgFEAjtQCGy5D0Gy48BUpWF1crU7gAJKxAALAGFKAFsABysDRAA6XX0jM0sbfDsAMX80B1R5RUJlQjVNHT1DEwtrWy4ASWIjQggTAB4WAEZGVBYAJg6WAGYNVAdcgHlw5HxQ/AAvQ00sckQAN3wDNHiypMrUmrqiRuMRbwyIZAqbCBZqcKQ+1AAZK3drVUQABSMpiaXECDjSxIhCJRQwCVoAGmwXUhfU4mC4EK40K4sNyrkK7mK3iQaGMYUi0QMQkezysrw+k1S+B+fw2gPxIIM8Dp5WSVQA6qlggzCSdcTzQdh2gjUAAyUXMgGs7Z2TnIbnA3mZVB4xWCnpIsUSuAsrYpWVcoEEwx8lUConYO4o3KDSQ4s1qon8EmqF7vT5Umn/BImI2M+DGRDmIbC9rigNBoYanrhnVSvUcw3m2rIeoHB3Gi1WvqSEhHeBAA==)
306-
* Note: [#16615](https://github.com/dotnet/fsharp/pull/16615) will compile to ``FSharpEqualityComparer_ER`1<!a>::get_EqualityComparer().Equals(...)`` and avoid boxing in many cases
307302

308303
## Techniques available to us
309304

0 commit comments

Comments
 (0)