Closed kerams closed 3 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 |
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?
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: