FSharp.SystemTextJson icon indicating copy to clipboard operation
FSharp.SystemTextJson copied to clipboard

Use System.Reflection.Emit

Open Tarmil opened this issue 6 years ago • 9 comments

Very WIP.

Currently only implemented for record serialization (struct records are failing). It's definitely an improvement over the previous results, especially in terms of runtime but also in terms of allocations (no more obj[]):

BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i7-6700 CPU 3.40GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.0.100-preview7-012821
  [Host]   : .NET Core 3.0.0-preview7-27912-14 (CoreCLR 4.700.19.32702, CoreFX 4.700.19.36209), 64bit RyuJIT DEBUG
  ShortRun : .NET Core 3.0.0-preview7-27912-14 (CoreCLR 4.700.19.32702, CoreFX 4.700.19.36209), 64bit RyuJIT

Job=ShortRun  Runtime=Core  IterationCount=3
LaunchCount=1  WarmupCount=3

|                   Method | ArrayLength |        Mean |      Error |     StdDev |    Gen 0 |   Gen 1 |   Gen 2 | Allocated |
|------------------------- |------------ |------------:|-----------:|-----------:|---------:|--------:|--------:|----------:|
|     Serialize_Newtonsoft |          10 |    18.09 us |  11.246 us |  0.6164 us |   1.9836 |       - |       - |   8.13 KB |
| Serialize_SystemTextJson |          10 |    19.07 us |   4.051 us |  0.2221 us |   1.0071 |       - |       - |   4.13 KB |
|     Serialize_Newtonsoft |         100 |   170.73 us |  47.437 us |  2.6002 us |  19.7754 |  0.2441 |       - |   81.1 KB |
| Serialize_SystemTextJson |         100 |   188.28 us |  44.945 us |  2.4636 us |   9.5215 |  0.4883 |       - |  39.45 KB |
|     Serialize_Newtonsoft |        1000 | 1,781.06 us | 433.746 us | 23.7751 us | 119.1406 | 58.5938 | 58.5938 | 668.72 KB |
| Serialize_SystemTextJson |        1000 | 1,882.00 us | 338.485 us | 18.5535 us |  58.5938 | 58.5938 | 58.5938 | 392.77 KB |

Tarmil avatar Aug 10 '19 14:08 Tarmil

Got struct record serialization working, with similar results:

BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i7-6700 CPU 3.40GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.0.100-preview7-012821
  [Host]   : .NET Core 3.0.0-preview7-27912-14 (CoreCLR 4.700.19.32702, CoreFX 4.700.19.36209), 64bit RyuJIT DEBUG
  ShortRun : .NET Core 3.0.0-preview7-27912-14 (CoreCLR 4.700.19.32702, CoreFX 4.700.19.36209), 64bit RyuJIT

Job=ShortRun  Runtime=Core  IterationCount=3
LaunchCount=1  WarmupCount=3

|                   Method | ArrayLength |        Mean |      Error |     StdDev |    Gen 0 |    Gen 1 |   Gen 2 | Allocated |
|------------------------- |------------ |------------:|-----------:|-----------:|---------:|---------:|--------:|----------:|
|     Serialize_Newtonsoft |          10 |    20.23 us |   7.511 us |  0.4117 us |   2.2278 |        - |       - |   9.16 KB |
| Serialize_SystemTextJson |          10 |    21.09 us |   5.055 us |  0.2771 us |   1.6479 |        - |       - |    6.8 KB |
|     Serialize_Newtonsoft |         100 |   198.45 us |  55.239 us |  3.0278 us |  22.2168 |   0.4883 |       - |  91.45 KB |
| Serialize_SystemTextJson |         100 |   208.40 us |  35.732 us |  1.9586 us |  16.1133 |        - |       - |  66.21 KB |
|     Serialize_Newtonsoft |        1000 | 1,945.98 us |  28.079 us |  1.5391 us | 175.7813 | 113.2813 | 58.5938 | 787.93 KB |
| Serialize_SystemTextJson |        1000 | 2,138.60 us | 241.417 us | 13.2329 us | 117.1875 |  58.5938 | 58.5938 | 660.35 KB |

Tarmil avatar Aug 10 '19 16:08 Tarmil

Just wanted to say that this PR is very instructional and I'm very hopeful for it! With Npgsql recently releasing support for System.Text.Json for postgresql json/jsonb columns it'd be wonderful to have a more 'safe' serializer for types coming directly from the database.

baronfel avatar Aug 19 '19 14:08 baronfel

@Tarmil Thought: is it worth packaging out the reflection-emit + type cache into a separate package? It would be nice to use them in other projects as well and share the same instances.

baronfel avatar Oct 30 '19 15:10 baronfel

Yeah, I've considered publishing this as an emit-based non-allocating alternative to FSharp.Reflection. I need to at least implement it for unions though, and it's harder than records 🙂

Tarmil avatar Oct 30 '19 15:10 Tarmil

There's an interesting PR on the dotnet/fsharp repo that might sidestep this PR: https://github.com/dotnet/fsharp/pull/9714

If that gets in, then this repo could just use the new method to generate a mostly-optimized reader func. Still not quite as complete/fast as your implementation here I believe, though.

baronfel avatar Jul 18 '20 17:07 baronfel

This looks like a nice improvement, thanks for the heads up @baronfel !

Tarmil avatar Jul 18 '20 17:07 Tarmil

Aaaaand take two is coming already: https://github.com/dotnet/fsharp/pull/9784/files

baronfel avatar Jul 25 '20 19:07 baronfel

Will this be an optional feature if it is implemented? Since the NativeAOT mode of .NET 7 does not support System.Reflection.Emit and my humble opinion is that AOT usage cases are worth considering

shanoaice avatar Feb 02 '23 03:02 shanoaice

As the author of Bolero, which runs on WebAssembly, I fully agree that environments where Emit is unavailable must and will always remain supported!

That being said, as you can see this is quite an old and partial branch, and I really don't know if and when I'll resume work on it anyway.

Tarmil avatar Feb 12 '23 11:02 Tarmil