From 177399f068bafca5b70090232007fa5be4a9acab Mon Sep 17 00:00:00 2001 From: Casper Bollen Date: Mon, 23 Oct 2023 12:47:52 +0200 Subject: [PATCH] perf: improved valueunit simplify performance --- .gitignore | 1 + ...ogram.ValueUnitBenchmarks-report-github.md | 17 ++--- benchmark/Program.fs | 14 ++++ benchmark/run.sh | 1 + src/Informedica.GenUnits.Lib/ValueUnit.fs | 68 ++++++++++--------- 5 files changed, 62 insertions(+), 39 deletions(-) create mode 100644 benchmark/run.sh diff --git a/.gitignore b/.gitignore index df37036..af7c3b4 100644 --- a/.gitignore +++ b/.gitignore @@ -206,6 +206,7 @@ # Add benchmark files !/benchmark/ !/benchmark/run.bat +!/benchmark/run.sh !/benchmark/.paket/ !/benchmark/.paket/Paket.Restore.targets !/benchmark/Informedica.GenSolver.Lib_v1/ diff --git a/benchmark/BenchmarkDotNet.Artifacts/results/Program.ValueUnitBenchmarks-report-github.md b/benchmark/BenchmarkDotNet.Artifacts/results/Program.ValueUnitBenchmarks-report-github.md index fb82ee3..15d362e 100644 --- a/benchmark/BenchmarkDotNet.Artifacts/results/Program.ValueUnitBenchmarks-report-github.md +++ b/benchmark/BenchmarkDotNet.Artifacts/results/Program.ValueUnitBenchmarks-report-github.md @@ -8,11 +8,12 @@ Apple M2 Max, 1 CPU, 12 logical and 12 physical cores ``` -| Method | Mean | Error | StdDev | -|------------------------ |----------:|---------:|---------:| -| BaseValue_200 | 41.12 ms | 0.661 ms | 0.618 ms | -| AllPairs_True_100 | 25.70 ms | 0.089 ms | 0.083 ms | -| AllPairs_True_200 | 129.14 ms | 0.579 ms | 0.513 ms | -| AllPairs_True_Rand_100 | 127.78 ms | 0.404 ms | 0.378 ms | -| AllPairs_False_Rand_200 | 623.85 ms | 4.947 ms | 4.627 ms | -| AllPairs_True_Rand_200 | 624.27 ms | 2.291 ms | 2.031 ms | +| Method | Mean | Error | StdDev | +|---------------------------------- |----------:|---------:|---------:| +| BaseValue_200 | 39.32 ms | 0.502 ms | 0.419 ms | +| AllPairs_True_100 | 25.08 ms | 0.117 ms | 0.104 ms | +| AllPairs_True_200 | 128.50 ms | 0.730 ms | 0.683 ms | +| AllPairs_True_Rand_100 | 133.47 ms | 0.397 ms | 0.372 ms | +| AllPairs_False_Rand_200 | 555.15 ms | 4.057 ms | 3.596 ms | +| AllPairs_True_Rand_200 | 554.77 ms | 3.653 ms | 3.051 ms | +| AllPairs_True_Rand_100_mgPerMl_ml | 114.64 ms | 2.232 ms | 2.088 ms | diff --git a/benchmark/Program.fs b/benchmark/Program.fs index 43949b7..b5c87f1 100644 --- a/benchmark/Program.fs +++ b/benchmark/Program.fs @@ -222,6 +222,12 @@ type ValueUnitBenchmarks () = Utils.getTwoRandomLists 200 1_000 |> fun (x1, x2) -> ValueUnit.create Units.Count.times x1, ValueUnit.create Units.Count.times x2 + let rand_100_a_mg_per_ml, rand_100_b_ml = + let mgPerMl = Units.Mass.milliGram |> Units.per Units.Volume.milliLiter + let ml = Units.Volume.milliLiter + Utils.getTwoRandomLists 100 1_000 + |> fun (x1, x2) -> ValueUnit.create mgPerMl x1, ValueUnit.create ml x2 + let calc b op x1 x2 = ValueUnit.calc b op x1 x2 //x1 |> op <| x2 let add b = calc b (+) @@ -292,6 +298,14 @@ type ValueUnitBenchmarks () = div true x1 x2 |> ignore + [] + member this.AllPairs_True_Rand_100_mgPerMl_ml () = + let x1, x2 = rand_100_a_mg_per_ml, rand_100_b_ml + add true x1 x1 |> ignore // can only add with same units + sub true x1 x1 |> ignore // can only sub with same units + mul true x1 x2 |> ignore + div true x1 x2 |> ignore + type EquationBenchmarks () = diff --git a/benchmark/run.sh b/benchmark/run.sh new file mode 100644 index 0000000..ed507e7 --- /dev/null +++ b/benchmark/run.sh @@ -0,0 +1 @@ +sudo dotnet run -c Release $@ \ No newline at end of file diff --git a/src/Informedica.GenUnits.Lib/ValueUnit.fs b/src/Informedica.GenUnits.Lib/ValueUnit.fs index 003ce5f..cc979fe 100644 --- a/src/Informedica.GenUnits.Lib/ValueUnit.fs +++ b/src/Informedica.GenUnits.Lib/ValueUnit.fs @@ -2594,6 +2594,33 @@ module ValueUnit = |> fun u -> build tail ds (isCount, u) + /// + /// Simplify a unit u such that units are algebraically removed or + /// transformed to count units, where applicable. + /// + /// The unit to simplify + /// + /// The simplified unit + /// + let simplifyUnit u = + if u = NoUnit then u + else + let ns, ds = u |> numDenom true + + (false, NoUnit) + |> build ns ds + |> fun (_, newU) -> + // nothing changed so just return original + if u = newU then u + else + match newU with + | CombiUnit(u1, OpPer, CombiUnit(u2, OpTimes, u3)) -> + if u2 |> Group.eqsGroup u3 then newU + else + CombiUnit(CombiUnit(u1, OpPer, u2), OpPer, u3) + | _ -> newU + + /// /// Simplify a value unit u such that units are algebraically removed or /// transformed to count units, where applicable. @@ -2610,40 +2637,19 @@ module ValueUnit = /// ValueUnit ([|1N; 2N; 3N|], Count (Times 1N)) /// /// - let simplify vu = - let u = vu |> getUnit + let rec simplify vu = + let v, u = vu |> get if u = NoUnit then vu else - let ns, ds = u |> numDenom true - - (false, NoUnit) - |> build ns ds - |> fun (_, u1) -> - // nothing changed so just return original - if u = u1 then vu - else - vu - |> toBaseValue - |> create u1 - |> toUnitValue - |> create u1 - // rewrite case u1/u2 * u3 -> u1/u2/u3 when group u2 <> group u3 - |> fun vu -> - let v, u = - vu - |> get - let u = - match u with - | CombiUnit(u1, OpPer, CombiUnit(u2, OpTimes, u3)) -> - if u2 |> Group.eqsGroup u3 then - u - else - CombiUnit(CombiUnit(u1, OpPer, u2), OpPer, u3) - | _ -> u - - v |> withUnit u + let u = simplifyUnit u + v + |> create u + // calculate to the new combiunit + |> toUnitValue + // recreate again to final value unit + |> create u /// @@ -2696,6 +2702,7 @@ module ValueUnit = | _ -> failwith <| $"cannot add or subtract different units %A{u1} %A{u2}" + |> fun u -> if b then simplifyUnit u else u // recreate valueunit with base value and combined unit v |> create u @@ -2703,7 +2710,6 @@ module ValueUnit = |> toUnitValue // recreate again to final value unit |> create u - |> fun vu -> if b then vu |> simplify else vu ///