From 5492130dd621cad6f79e78ce70abf9ef940a4083 Mon Sep 17 00:00:00 2001 From: Ahmed Date: Sat, 14 Feb 2026 19:17:28 +0200 Subject: [PATCH 1/2] Optimize Set.intersect symmetry and add release notes --- docs/release-notes/.FSharp.Core/10.0.300.md | 1 + src/FSharp.Core/set.fs | 41 ++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/docs/release-notes/.FSharp.Core/10.0.300.md b/docs/release-notes/.FSharp.Core/10.0.300.md index 90fd2f1e9ad..7f011f4bed8 100644 --- a/docs/release-notes/.FSharp.Core/10.0.300.md +++ b/docs/release-notes/.FSharp.Core/10.0.300.md @@ -1,5 +1,6 @@ ### Fixed +* Optimize Set.intersect performance symmetry and preserve identity from the first set argument. ([PR #19291](https://github.com/dotnet/fsharp/pull/19291)) (Fixes #19139) * Fix anonymous record field ordering in LINQ expression conversion to produce consistent expression trees regardless of field declaration order. ([Issue #11131](https://github.com/dotnet/fsharp/issues/11131), [Issue #15648](https://github.com/dotnet/fsharp/issues/15648)) * Fix array indexing in LINQ expressions to generate proper array index expressions instead of GetArray method calls, enabling LINQ providers like Azure Cosmos DB to translate array access. ([Issue #16918](https://github.com/dotnet/fsharp/issues/16918)) * Fix tuple join conditions and groupBy operations to properly compare tuple keys using structural equality. AnonymousObject types now implement Equals and GetHashCode, enabling inline tuple joins like `join b on ((a.Id1, a.Id2) = (b.Id1, b.Id2))` to work correctly. ([Issue #7885](https://github.com/dotnet/fsharp/issues/7885), [Issue #47](https://github.com/dotnet/fsharp/issues/47)) diff --git a/src/FSharp.Core/set.fs b/src/FSharp.Core/set.fs index fd8478a60a2..de8a21ffbba 100644 --- a/src/FSharp.Core/set.fs +++ b/src/FSharp.Core/set.fs @@ -267,6 +267,21 @@ module internal SetTree = elif c = 0 then true else mem comparer k tn.Right + let rec tryGet (comparer: IComparer<'T>) k (t: SetTree<'T>) = + if isEmpty t then + None + else + let c = comparer.Compare(k, t.Key) + + if t.Height = 1 then + if c = 0 then Some t.Key else None + else + let tn = asNode t + + if c < 0 then tryGet comparer k tn.Left + elif c = 0 then Some tn.Key + else tryGet comparer k tn.Right + let rec iter f (t: SetTree<'T>) = if isEmpty t then () @@ -391,6 +406,24 @@ module internal SetTree = balance comparer (union comparer t2n.Left lo) t2n.Key (union comparer t2n.Right hi) + let rec intersectionAuxFromSmall comparer a (t: SetTree<'T>) acc = + if isEmpty t then + acc + else if t.Height = 1 then + match tryGet comparer t.Key a with + | Some v -> add comparer v acc + | None -> acc + else + let tn = asNode t + let acc = intersectionAuxFromSmall comparer a tn.Right acc + + let acc = + match tryGet comparer tn.Key a with + | Some v -> add comparer v acc + | None -> acc + + intersectionAuxFromSmall comparer a tn.Left acc + let rec intersectionAux comparer b (t: SetTree<'T>) acc = if isEmpty t then acc @@ -412,7 +445,13 @@ module internal SetTree = intersectionAux comparer b tn.Left acc let intersection comparer a b = - intersectionAux comparer b a empty + let n1 = count a + let n2 = count b + + if n1 <= n2 then + intersectionAux comparer b a empty + else + intersectionAuxFromSmall comparer a b empty let partition1 comparer f k (acc1, acc2) = if f k then From 255bd156a0b4ffff370f30d3d01689ef803c9f3c Mon Sep 17 00:00:00 2001 From: Ahmed Walid Date: Sat, 14 Feb 2026 19:44:49 +0200 Subject: [PATCH 2/2] Fix release notes for FSharp.Core 10.0.300 --- docs/release-notes/.FSharp.Core/10.0.300.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/release-notes/.FSharp.Core/10.0.300.md b/docs/release-notes/.FSharp.Core/10.0.300.md index 7f011f4bed8..2c5c2834a91 100644 --- a/docs/release-notes/.FSharp.Core/10.0.300.md +++ b/docs/release-notes/.FSharp.Core/10.0.300.md @@ -1,6 +1,6 @@ ### Fixed -* Optimize Set.intersect performance symmetry and preserve identity from the first set argument. ([PR #19291](https://github.com/dotnet/fsharp/pull/19291)) (Fixes #19139) +* Optimize Set.intersect performance symmetry and preserve identity from the first set argument. ([PR #19292](https://github.com/dotnet/fsharp/pull/19291)) (Fixes #19292) * Fix anonymous record field ordering in LINQ expression conversion to produce consistent expression trees regardless of field declaration order. ([Issue #11131](https://github.com/dotnet/fsharp/issues/11131), [Issue #15648](https://github.com/dotnet/fsharp/issues/15648)) * Fix array indexing in LINQ expressions to generate proper array index expressions instead of GetArray method calls, enabling LINQ providers like Azure Cosmos DB to translate array access. ([Issue #16918](https://github.com/dotnet/fsharp/issues/16918)) * Fix tuple join conditions and groupBy operations to properly compare tuple keys using structural equality. AnonymousObject types now implement Equals and GetHashCode, enabling inline tuple joins like `join b on ((a.Id1, a.Id2) = (b.Id1, b.Id2))` to work correctly. ([Issue #7885](https://github.com/dotnet/fsharp/issues/7885), [Issue #47](https://github.com/dotnet/fsharp/issues/47))