Benchmarks 1
Let's benchmark...
DependentType option
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: |
|
- Create a percent DependentType option compared to creating a simple float option using the same validation logic.
- Each benchmark instance creates 1 Some option and 1 None option in both cases.
- Benchmark 1,000,000 instances, hence 2M option instances.
1: 2: |
|
Not surprisingly validation and creation of a simple option is faster, 28X faster.
And it scales nearly linearly, as we see when executing the benchmark 10X instead of 1MX.
1: 2: |
|
Considering in our first benchmark DependentType created 2M option instances in less than 700 ms, and creates 20 in 9 micro-seconds, this is probably acceptable performance for all but the most demanding network applications.
The validation logic adds little overhead. Even comparing creating DependentType to "naked" options (not run through the validation logic) makes little difference in the performance ratio.
1: 2: |
|
Can we squeeze even more performance from DependentType creation? Let's use Create
instead of TryCreate
so we eliminate the overhead of "lifting" the 'T2
base type element option result
to the DependentType element.
1: 2: |
|
Validate option is now only 15X faster, so the "lift" overhead of DependentType is noticeable at large scales (2M creations).
We expect from these results Create DepeendentType option is twice as fast as TryCreate, because it does not lift the option from the value to the DependentType. And we see if we do a Create to TryCreate direct comparison, that is roughly true.
1: 2: |
|
Consuming (reading) DependentType.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: |
|
1: 2: |
|
- Reading 2M float options is only 33% faster than read/extract value operation on DependentTypes.
Substituting the verbose Some pct.Value.Value
for the helper function someValue
almost erases any advantage of float option.
1: 2: |
|
UtcDateTime DependentType
Benchmarking a type that is not an option
, we compare UtcDateTime in the DomainLib to an implementation validating a DateTime.
This time 1M benchmark runs is also 1M instances.
1: 2: |
|
1: 2: |
|
In this case the create and read performance differences are barely meaningful.
DependentPair
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: |
|
Benchmarks comparing a validated pair to DependentPair yields similar performance ratios to option DependentType.
1M runs again creates 2M instances.
1: 2: |
|
1: 2: |
|
Creation of a simple validated pair is 7X faster than creating a DependentPair, but read/consume performance is so similar we sometimes see the benchmark test failing because DependentPair performs faster.
Notes
1 The usual caveats about benchmarking apply. You should benchmark your own situation on your own system, etc. There is variance in running the benchmarks multiple times. The variance I saw was typically in absolute run time for each scenario, and not so much in the DependentType / control run time ratios. By and large these results are representative of typical benchmark runs on my system, FSharp.Core 4.5.3, net45 DependentTypes.dllfrom DependentTypes
type PercentValidator =
inherit Pi<unit,float,float option>
new : unit -> PercentValidator
--------------------
new : unit -> PercentValidator
type Pi<'Config,'T,'T2> =
new : config:'Config * pi:('Config -> 'T -> 'T2) -> Pi<'Config,'T,'T2>
member Create : x:'T -> 'T2
--------------------
new : config:'Config * pi:('Config -> 'T -> 'T2) -> Pi<'Config,'T,'T2>
val float : value:'T -> float (requires member op_Explicit)
--------------------
type float = System.Double
--------------------
type float<'Measure> = float
type PairPercentValidator =
inherit Sigma<unit,float,float option>
new : unit -> PairPercentValidator
--------------------
new : unit -> PairPercentValidator
type Sigma<'Config,'T,'T2> =
new : config:'Config * pi:('Config -> 'T -> 'T2) -> Sigma<'Config,'T,'T2>
member Create : x:'T -> 'T * 'T2
--------------------
new : config:'Config * pi:('Config -> 'T -> 'T2) -> Sigma<'Config,'T,'T2>
union case SomeDependentType.SomeDependentType: 'T2 -> SomeDependentType<'Pi,'Config,'T,'T2>
--------------------
type SomeDependentType<'Pi,'Config,'T,'T2 (requires 'Pi :> Pi<'Config,'T,'T2 option> and default constructor)> =
| SomeDependentType of 'T2
override ToString : unit -> string
member Value : 'T2
static member ConvertTo : x:SomeDependentType<'x,'y,'q,'r> -> SomeDependentType<'a,'b,'r,'s> (requires 'x :> Pi<'y,'q,'r option> and default constructor and 'a :> Pi<'b,'r,'s option> and default constructor)
static member Create : x:'T -> SomeDependentType<'Pi,'Config,'T,'T2>
static member Extract : x:SomeDependentType<'Pi,'Config,'T,'T2> -> 'T2
static member TryCreate : x:'T -> SomeDependentType<'Pi,'Config,'T,'T2> option
from Benchmarks
type PercentValidator =
inherit Pi<unit,float,float option>
new : unit -> PercentValidator
--------------------
new : unit -> PercentType.PercentValidator
from Microsoft.FSharp.Collections
from Microsoft.FSharp.Core
union case DependentPair.DependentPair: 'T * 'T2 -> DependentPair<'Sigma,'Config,'T,'T2>
--------------------
type DependentPair<'Sigma,'Config,'T,'T2 (requires 'Sigma :> Sigma<'Config,'T,'T2> and default constructor)> =
| DependentPair of 'T * 'T2
member Value : 'T * 'T2
static member Create : x:'T -> DependentPair<'Sigma,'Config,'T,'T2>
type PairPercentValidator =
inherit Sigma<unit,float,float option>
new : unit -> PairPercentValidator
--------------------
new : unit -> PercentType.PairPercentValidator