Skip to content

Commit

Permalink
ASet.union: optimizations in case one of the input sets is constant
Browse files Browse the repository at this point in the history
  • Loading branch information
luithefirst committed Sep 9, 2024
1 parent 2562208 commit 5b764a0
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 5 deletions.
39 changes: 35 additions & 4 deletions src/FSharp.Data.Adaptive/AdaptiveHashSet/AdaptiveHashSet.fs
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,32 @@ module AdaptiveHashSetImplementation =
state <- newState
HashSetDelta.ofHashMap delta


/// Reader for unioning an aset with a constant set of elements.
[<Sealed>]
type UnionConstantSingleReader<'T>(constant : HashSet<'T>, input : aset<'T>) =
inherit AbstractReader<HashSetDelta<'T>>(HashSetDelta.empty)

let mutable isInitial = true
let inputReader =
let r = input.GetReader()
r.Tag <- "InnerReader"
r

override x.Compute(token) =
if isInitial then
isInitial <- false
// initially we need to combined the constant and the input set.
//let initial = constant |> Seq.map (fun v -> Add v) |> HashSetDelta.ofSeq // NOTE: this allocates enumerators
let mutable initial = HashSetDelta.empty
for v in constant do
initial <- initial |> HashSetDelta.add (Add v)
let otherInitial = inputReader.GetChanges(token)
HashSetDelta.combine initial otherInitial
else
// once evaluated only the input set needs to be pulled.
inputReader.GetChanges token

/// Reader for unioning a constant set of asets.
[<Sealed>]
type UnionConstantReader<'T>(input : HashSet<aset<'T>>) =
Expand Down Expand Up @@ -1201,13 +1227,18 @@ module ASet =
let union (a : aset<'A>) (b : aset<'A>) =
if a = b then
a
elif a.IsConstant && b.IsConstant then
elif a.IsConstant then
let va = force a
if b.IsConstant then
let vb = force b
if va.IsEmpty && vb.IsEmpty then empty
else constant (fun () -> HashSet.union va vb)
else
ofReader (fun () -> UnionConstantSingleReader(va, b))
elif b.IsConstant then
let vb = force b
if va.IsEmpty && vb.IsEmpty then empty
else constant (fun () -> HashSet.union va vb)
ofReader (fun () -> UnionConstantSingleReader(vb, a))
else
// TODO: can be optimized in case one of the two sets is constant.
ofReader (fun () -> UnionConstantReader (HashSet.ofArray [| a; b |]))

/// Adaptively subtracts the given sets.
Expand Down
44 changes: 44 additions & 0 deletions src/Test/FSharp.Data.Adaptive.Tests/ASet.fs
Original file line number Diff line number Diff line change
Expand Up @@ -715,3 +715,47 @@ let ``[ASet] mapA/flattenA/chooseA async``() =

()


[<Test>]
let ``[ASet] union constant``() =
let constSet = ASet.ofList [1; 2; 3]
let changeSet = cset [4;5;6]

let union1 = ASet.union constSet changeSet
let union2 = ASet.union changeSet constSet

let refSet = [1; 2; 3; 4; 5; 6]
union1 |> ASet.force |> should setequal refSet
union2 |> ASet.force |> should setequal refSet

transact(fun () -> changeSet.Add(1) |> ignore)

union1 |> ASet.force |> should setequal refSet
union2 |> ASet.force |> should setequal refSet

transact(fun () -> changeSet.Remove(1) |> ignore)

union1 |> ASet.force |> should setequal refSet
union2 |> ASet.force |> should setequal refSet

transact(fun () -> changeSet.Remove(5) |> ignore)

let refSet = [1; 2; 3; 4; 6]
union1 |> ASet.force |> should setequal refSet
union2 |> ASet.force |> should setequal refSet

let constSet = ASet.ofList [1; 2; 3]
let changeSet = cset [3;4;5]

let union1 = ASet.union constSet changeSet
let union2 = ASet.union changeSet constSet

let refSet = [1; 2; 3; 4; 5]
union1 |> ASet.force |> should setequal refSet
union2 |> ASet.force |> should setequal refSet

transact(fun () -> changeSet.Remove(5) |> ignore)

let refSet = [1; 2; 3; 4]
union1 |> ASet.force |> should setequal refSet
union2 |> ASet.force |> should setequal refSet
3 changes: 2 additions & 1 deletion src/Test/FSharp.Data.Adaptive.Tests/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ let ``[AList] sub``() =
let main _args =

//ASet.``[ASet] mapA/flattenA/chooseA async``()
ASet.``[ASet] union constant``()

//``[AList] sub``();

Expand Down Expand Up @@ -88,6 +89,6 @@ let main _args =
//BenchmarkRunner.Run<Benchmarks.HashMapDeltaEnumeratorBenchmark>() |> ignore
//BenchmarkRunner.Run<Benchmarks.MapExtEnumeratorBenchmark>() |> ignore
//BenchmarkRunner.Run<Benchmarks.HashSetDeltaBench>() |> ignore
BenchmarkRunner.Run<Benchmarks.IndexListBenchmarks>() |> ignore
//BenchmarkRunner.Run<Benchmarks.IndexListBenchmarks>() |> ignore

0

0 comments on commit 5b764a0

Please sign in to comment.