|
module Program |
|
|
|
open System |
|
open System.Collections.Generic |
|
open System.Threading.Tasks |
|
|
|
/// https://www.nuget.org/packages/BenchmarkDotNet/ |
|
/// https://www.nuget.org/packages/BenchmarkDotNet.Diagnostics.Windows/ |
|
|
|
open BenchmarkDotNet.Attributes |
|
open BenchmarkDotNet.Running |
|
open BenchmarkDotNet.Configs |
|
open BenchmarkDotNet.Jobs |
|
|
|
#if MONO |
|
#else |
|
open BenchmarkDotNet.Diagnostics.Windows |
|
#endif |
|
|
|
module SetForBenchmark = |
|
type SetTree<'T> when 'T : comparison = |
|
| SetEmpty |
|
| SetNode of 'T * SetTree<'T> * SetTree<'T> * int |
|
| SetOne of 'T |
|
|
|
let tolerance = 2 |
|
|
|
let height t = |
|
match t with |
|
| SetEmpty -> 0 |
|
| SetOne _ -> 1 |
|
| SetNode (_,_,_,h) -> h |
|
|
|
let mk l k r = |
|
match l,r with |
|
| SetEmpty,SetEmpty -> SetOne (k) |
|
| _ -> |
|
let hl = height l |
|
let hr = height r |
|
let m = if hl < hr then hr else hl |
|
SetNode(k,l,r,m+1) |
|
|
|
let rebalance t1 k t2 = |
|
let t1h = height t1 |
|
let t2h = height t2 |
|
if t2h > t1h + tolerance then // right is heavier than left |
|
match t2 with |
|
| SetNode(t2k,t2l,t2r,_) -> |
|
// one of the nodes must have height > height t1 + 1 |
|
if height t2l > t1h + 1 then // balance left: combination |
|
match t2l with |
|
| SetNode(t2lk,t2ll,t2lr,_) -> |
|
mk (mk t1 k t2ll) t2lk (mk t2lr t2k t2r) |
|
| _ -> failwith "rebalance" |
|
else // rotate left |
|
mk (mk t1 k t2l) t2k t2r |
|
| _ -> failwith "rebalance" |
|
else |
|
if t1h > t2h + tolerance then // left is heavier than right |
|
match t1 with |
|
| SetNode(t1k,t1l,t1r,_) -> |
|
// one of the nodes must have height > height t2 + 1 |
|
if height t1r > t2h + 1 then |
|
// balance right: combination |
|
match t1r with |
|
| SetNode(t1rk,t1rl,t1rr,_) -> |
|
mk (mk t1l t1k t1rl) t1rk (mk t1rr k t2) |
|
| _ -> failwith "rebalance" |
|
else |
|
mk t1l t1k (mk t1r k t2) |
|
| _ -> failwith "rebalance" |
|
else mk t1 k t2 |
|
|
|
let rec spliceOutSuccessor t = |
|
match t with |
|
| SetEmpty -> failwith "internal error: Set.spliceOutSuccessor" |
|
| SetOne (k2) -> k2,SetEmpty |
|
| SetNode (k2,l,r,_) -> |
|
match l with |
|
| SetEmpty -> k2,r |
|
| _ -> let k3,l' = spliceOutSuccessor l in k3,mk l' k2 r |
|
|
|
let rec add (comparer: IComparer<'T>) k t = |
|
match t with |
|
| SetNode (k2,l,r,_) -> |
|
let c = comparer.Compare(k,k2) |
|
if c < 0 then rebalance (add comparer k l) k2 r |
|
elif c = 0 then t |
|
else rebalance l k2 (add comparer k r) |
|
| SetOne(k2) -> |
|
// nb. no check for rebalance needed for small trees, also be sure to reuse node already allocated |
|
let c = comparer.Compare(k,k2) |
|
if c < 0 then SetNode (k,SetEmpty,t,2) |
|
elif c = 0 then t |
|
else SetNode (k,t,SetEmpty,2) |
|
| SetEmpty -> SetOne(k) |
|
|
|
let rec remove (comparer: IComparer<'T>) k t = |
|
match t with |
|
| SetEmpty -> t |
|
| SetOne (k2) -> |
|
let c = comparer.Compare(k,k2) |
|
if c = 0 then SetEmpty |
|
else t |
|
| SetNode (k2,l,r,_) -> |
|
let c = comparer.Compare(k,k2) |
|
if c < 0 then rebalance (remove comparer k l) k2 r |
|
elif c = 0 then |
|
match l,r with |
|
| SetEmpty,_ -> r |
|
| _,SetEmpty -> l |
|
| _ -> |
|
let sk,r' = spliceOutSuccessor r |
|
mk l sk r' |
|
else rebalance l k2 (remove comparer k r) |
|
|
|
let ofArray comparer l = Array.fold (fun acc k -> add comparer k acc) SetEmpty l |
|
|
|
// THE ORIGINAL METHOD, THAT WAS |
|
let rec diffAux comparer m acc = |
|
match m with |
|
| SetNode(k,l,r,_) -> diffAux comparer l (diffAux comparer r (remove comparer k acc)) |
|
| SetOne(k) -> remove comparer k acc |
|
| SetEmpty -> acc |
|
|
|
// THE METHOD AFTER MODIFICATION |
|
let rec diffAuxModified comparer m acc = |
|
match acc with |
|
| SetEmpty -> acc |
|
| _ -> |
|
match m with |
|
| SetNode(k,l,r,_) -> diffAux comparer l (diffAux comparer r (remove comparer k acc)) |
|
| SetOne(k) -> remove comparer k acc |
|
| SetEmpty -> acc |
|
|
|
let diff comparer a b = diffAux comparer b a |
|
|
|
let diffModified comparer a b = diffAuxModified comparer b a |
|
|
|
|
|
type SetDiffPerfConfig () = |
|
inherit ManualConfig() |
|
do |
|
base.Add Job.RyuJitX64 |
|
|
|
#if MONO |
|
#else |
|
base.Add(new MemoryDiagnoser()) |
|
#endif |
|
|
|
|
|
[<Config(typeof<SetDiffPerfConfig>)>] |
|
type SetDifferenceBenchmark () = |
|
|
|
let random = Random() |
|
let comparer = LanguagePrimitives.FastGenericComparer<int> |
|
|
|
let mutable firstSet = SetForBenchmark.SetEmpty |
|
let mutable secondSet = SetForBenchmark.SetEmpty |
|
|
|
let arrayOfMaxLen len = |
|
Array.init len (fun i -> int(random.NextDouble()*2.0*float(len))) |
|
|
|
[<Params(0, 10, 100)>] |
|
member val public FirstSetItemCount = 0 with get, set |
|
|
|
[<Params(0, 10, 100)>] |
|
member val public SecondSetItemCount = 0 with get, set |
|
|
|
[<Setup>] |
|
member this.Setup () = |
|
firstSet <- this.FirstSetItemCount |> arrayOfMaxLen |> SetForBenchmark.ofArray comparer |
|
secondSet <- this.SecondSetItemCount |> arrayOfMaxLen |> SetForBenchmark.ofArray comparer |
|
|
|
[<Benchmark(Baseline=true)>] |
|
member this.OriginalMethod () = |
|
SetForBenchmark.diff comparer firstSet secondSet |> ignore |
|
|
|
[<Benchmark>] |
|
member this.ModifiedMethod () = |
|
SetForBenchmark.diffModified comparer firstSet secondSet |> ignore |
|
|
|
[<EntryPoint>] |
|
let main argv = |
|
|
|
let switch = |
|
BenchmarkSwitcher [| |
|
typeof<SetDifferenceBenchmark> |
|
|] |
|
|
|
switch.Run argv |> ignore |
|
0 |