Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions FSharp.Json.Benchmarks/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BenchmarkDotNet.Artifacts
25 changes: 25 additions & 0 deletions FSharp.Json.Benchmarks/FSharp.Json.Benchmarks.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>

<ItemGroup>
<Compile Include="Serializers.fs" />
<Compile Include="Types.fs" />
<Compile Include="Main.fs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.11.5" />
<PackageReference Include="FsCheck" Version="3.0.0-alpha4" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\FSharp.Json\FSharp.Json.fsproj" />
</ItemGroup>

</Project>
11 changes: 11 additions & 0 deletions FSharp.Json.Benchmarks/Main.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module FSharp.Json.Benchmarks.Main

open System.Reflection
open BenchmarkDotNet.Running

[<EntryPoint>]
let main args =
let assembly = Assembly.GetExecutingAssembly()
let switcher = new BenchmarkSwitcher(assembly)
let summaries = switcher.Run args
0
75 changes: 75 additions & 0 deletions FSharp.Json.Benchmarks/Serializers.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
namespace FSharp.Json.Benchmarks

open System.IO
open System.Threading

open BenchmarkDotNet.Attributes
open FSharp.Json

type ISerializer =
abstract Name : string
abstract Serialize : Stream -> 'T -> unit
abstract Deserialize : Stream -> 'T

type JsonDotNetSerializer () =
let jdn = Newtonsoft.Json.JsonSerializer.Create()

interface ISerializer with
member __.Name = "Json.Net"
member __.Serialize (stream : Stream) (x : 'T) =
use writer = new StreamWriter(stream)
jdn.Serialize(writer, x)
writer.Flush()

member __.Deserialize (stream : Stream) : 'T =
use reader = new StreamReader(stream)
jdn.Deserialize(reader, typeof<'T>) :?> 'T

type FSharpJsonSerializer() =
let serializerConfig : JsonConfig =
{
unformatted = true
serializeNone = SerializeNone.Null
deserializeOption = DeserializeOption.AllowOmit
jsonFieldNaming = id
allowUntyped = true
enumValue = EnumMode.Name
}

interface ISerializer with
member __.Name = "FSharp.Json"
member __.Serialize (stream : Stream) (x : 'T) =
let json = FSharp.Json.Json.serializeEx serializerConfig x
let bytes = System.Text.Encoding.ASCII.GetBytes json
stream.Write(bytes, 0, bytes.Length)

member __.Deserialize (stream : Stream) : 'T =
use sr = new StreamReader(stream)
let json = sr.ReadToEnd()
FSharp.Json.Json.deserializeEx serializerConfig json

module Serializer =

let memoryStream =
new ThreadLocal<_>(fun () ->
{ new MemoryStream() with
override __.Close() = () })

let roundTrip (serializer : ISerializer) (value : 'T) =
let m = memoryStream.Value
m.Position <- 0L
serializer.Serialize m value
m.Position <- 0L
let _ = serializer.Deserialize<'T> m
()

[<AbstractClass>]
[<MarkdownExporter; CsvMeasurementsExporter; HtmlExporter; RPlotExporter>]
type RoundtripBenchmark<'T>(value : 'T) =
let nsj = new JsonDotNetSerializer()
let jfs = new FSharpJsonSerializer()

[<Benchmark(Description = "Newtonsoft.Json")>]
member __.NewtonsoftJson() = Serializer.roundTrip nsj value
[<Benchmark(Description = "FSharp.Json")>]
member __.FSharpJson() = Serializer.roundTrip jfs value
177 changes: 177 additions & 0 deletions FSharp.Json.Benchmarks/Types.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
namespace FSharp.Json.Benchmarks

open System
open FsCheck

module Poco =
[<CLIMutable>]
type T =
{ A : int ; B : string ; C : bool ; D : byte[] ;
F : DateTimeOffset ; G : TimeSpan ; H : Guid }

let value = { A = 42 ; B = "lorem ipsum" ; C = true ; D = [|1uy .. 20uy|]
F = DateTimeOffset(2018,1,1,23,11,12,TimeSpan.Zero) ;
G = TimeSpan.FromDays 30.
H = Guid.Empty }

type PocoRoundtrip() =
inherit RoundtripBenchmark<T>(value)

module FloatArray =
type T = float[]

let value =
[| for i in 1 .. 100 -> float i / float 100.
yield Double.Epsilon
yield Double.PositiveInfinity
yield Double.NaN
yield Double.NegativeInfinity |]

type FloatArrayRoundtrip() =
inherit RoundtripBenchmark<T>(value)

module BoxedArray =
type T = obj[]

let value : T = [|box 1; box "foo" ; box true ; box(1,2) ; box (ref 42) |]

type BoxedArrayRoundtrip() =
inherit RoundtripBenchmark<T>(value)

module Array3D =
type T = float [,,]

let value : T = Array3D.init 20 20 20 (fun i j k -> 0.1 * float i + float j + 10. * float k)

type Array3DRoundtrip() =
inherit RoundtripBenchmark<T>(value)

module LargeTuple =
type T = string * int * int * int * bool * string * (float * int list) option * int * int * int * string
let value : T = ("lorem ipsum dolor", 1, 2, 3, true, "", Some(3.14, [2]), 3, 2, 1, "lorem")

type LargeTupleRoundtrip() =
inherit RoundtripBenchmark<T>(value)

module FSharpList =
type T = int list
let value : T = [1..1000]

type FSharpListRoundtrip() =
inherit RoundtripBenchmark<T>(value)

module Dictionary =
open System.Collections.Generic
open Poco

type T = Dictionary<string, Poco.T>

let mkDict size =
let d = new T()
for i = 1 to size do
let key = sprintf "key-%d" i
let value = {
A = i ;
B = sprintf "value-%d" i ;
C = i % 2 = 0 ;
D = [|byte i|]
F = DateTimeOffset(2018,1,1,23,11,12,TimeSpan.Zero) ;
G = TimeSpan.FromDays 30.
H = Guid.Empty
}

d.Add(key, value)
d

let value = mkDict 1000

type DictionaryRoundtrip() =
inherit RoundtripBenchmark<T>(value)

module ExceptionBench =

let rec mkExn d =
if d = 0 then raise (FormatException "kaboom!")
else
1 + mkExn(d - 1)

let exn = try mkExn 20 |> ignore ; failwith "" with :? FormatException as e -> e

type ExceptionRoundtrip() =
inherit RoundtripBenchmark<FormatException>(exn)

module LargeObject =
type Foo = { A : int ; B : string ; C : bool }
type Bar = A of int * string | B of Foo | C
type T = Bar list list option * string list * byte []

let value : T [] =
Arb.from<T>.Generator |> Gen.sampleWithSeed (Rnd 2019UL) 20 20 |> Seq.toArray

type LargeFSharpValueRoundtrip() =
inherit RoundtripBenchmark<T []>(value)


module ISerializable =
open System.Runtime.Serialization

type T(x : int, y : string, z : bool) =
interface ISerializable with
member __.GetObjectData(si, _) =
si.AddValue("x", x)
si.AddValue("y", y)
si.AddValue("z", z)

new (si : SerializationInfo, _ : StreamingContext) =
let inline get x = si.GetValue(x, typeof<'T>) :?> 'T
new T(get "x", get "y", get "z")

let instance = new T(42, "lorem ipsum", true)

type ISerializableRoundtrip() =
inherit RoundtripBenchmark<T>(instance)


module FSharpBinTree =
type Tree = Node | Leaf of int * Tree * Tree

let rec mkTree d =
if d = 0 then Node
else
let t = mkTree (d-1)
Leaf(d, mkTree(d-1), mkTree(d-1))

let value = mkTree 10

type FSharpBinaryRoundtrip() =
inherit RoundtripBenchmark<Tree>(value)

module FSharpSet =
let value = set [1 .. 40]

type FSharpSetRoundtrip() =
inherit RoundtripBenchmark<Set<int>>(value)


module FSharpMap =
let value = [1 .. 40] |> Seq.map (fun i -> string i, i) |> Map.ofSeq

type FSharpMapRountrip() =
inherit RoundtripBenchmark<Map<string, int>>(value)


module MemberInfo =
open System.Reflection
open FSharp.Quotations.Patterns

type private Foo =
static member Method(x : int) = ()
static member Method<'T>(x : 'T, y : Foo) = ()

let value : MemberInfo =
match <@ Foo.Method("string", Unchecked.defaultof<_>) @> with
| Call(_,m,_) -> m :> _
| _ -> failwith "impossible"

type MemberInfoRoundtrip() =
inherit RoundtripBenchmark<MemberInfo>(value)
6 changes: 6 additions & 0 deletions FSharp.Json.sln
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ ProjectSection(SolutionItems) = preProject
RELEASE_NOTES.md = RELEASE_NOTES.md
EndProjectSection
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Json.Benchmarks", "FSharp.Json.Benchmarks\FSharp.Json.Benchmarks.fsproj", "{B36BCE2A-84E8-4A34-9BF5-2ECB5DC91F58}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -27,5 +29,9 @@ Global
{5D95196B-06AF-4158-9FF0-FAF132900A21}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5D95196B-06AF-4158-9FF0-FAF132900A21}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5D95196B-06AF-4158-9FF0-FAF132900A21}.Release|Any CPU.Build.0 = Release|Any CPU
{B36BCE2A-84E8-4A34-9BF5-2ECB5DC91F58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B36BCE2A-84E8-4A34-9BF5-2ECB5DC91F58}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B36BCE2A-84E8-4A34-9BF5-2ECB5DC91F58}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B36BCE2A-84E8-4A34-9BF5-2ECB5DC91F58}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal