demetrixbio / FSharp.Data.Npgsql

F# type providers to support statically typed access to input parameters and result set of sql statement in idiomatic F# way. Data modifications via statically typed tables.
Apache License 2.0
128 stars 15 forks source link

Cache option case constructors #95

Closed kerams closed 3 years ago

kerams commented 4 years ago

These were constructed from scratch for every single nullable value, resulting in unnecessary garbage and slowdown. So when executing a query like SELECT * FROM generate_series(0, 10000), these 3 reflection-heavy calls would execute 10000 times:

let optionType = typeof<unit option>.GetGenericTypeDefinition().MakeGenericType([| typeParam |])
let cases = FSharpType.GetUnionCases(optionType)
FSharpValue.MakeUnion(relevantCase, args)
kerams commented 4 years ago

Still fairly slow but a 12x improvement over the original.

[<MemoryDiagnoser>]
type Test () =
    let someCtor =
        let t = typeof<unit option>.GetGenericTypeDefinition().MakeGenericType([| typeof<int64> |])
        let cases = FSharp.Reflection.FSharpType.GetUnionCases t
        FSharp.Reflection.FSharpValue.PreComputeUnionConstructor (cases.[1])

    [<Benchmark(Baseline = true)>]
    member _.Reflection () =
        for i in 1 .. 10000 do
            let t = typedefof<unit option>.GetGenericTypeDefinition().MakeGenericType typeof<int64>
            let cs = FSharp.Reflection.FSharpType.GetUnionCases t
            FSharp.Reflection.FSharpValue.MakeUnion(cs.[1], [| box 1L |]) |> ignore

    [<Benchmark>]
    member _.Cached () =
        for i in 1 .. 10000 do
            someCtor [| box 1L |] |> ignore
Method Mean Error StdDev Ratio Gen 0 Gen 1 Gen 2 Allocated
Reflection 54.405 ms 0.7385 ms 0.6908 ms 1.00 1500.0000 - - 12.44 MB
Cached 4.262 ms 0.0623 ms 0.0583 ms 0.08 132.8125 - - 1.07 MB
kerams commented 3 years ago

So moving to .NET 5 and most importantly FSharp.Core 5.0 provides a further almost 40x improvement over the Cached result above, so caching the constructor can provide north of 400x speedup.

Method Runtime Mean Error StdDev Ratio Gen 0 Gen 1 Gen 2 Allocated
Reflection .NET Core 5.0 47,042.3 us 178.31 us 158.07 us 1.000 1545.4545 - - 12735.21 KB
Cached .NET Core 5.0 114.6 us 1.97 us 3.94 us 0.003 95.4590 - - 781.25 KB

@melanore, @daz10000, can you merge this when you have a spare moment?