Browse Source

Updated Benchmarks to use latest recommended Giraffe version (#6198)

Dustin Moris Gorski 4 years ago
parent
commit
bf92a9bd02

+ 3 - 0
frameworks/FSharp/giraffe/.gitignore

@@ -36,3 +36,6 @@ BDN.Generated/
 binaries/
 binaries/
 global.json
 global.json
 *.sln
 *.sln
+
+.fake
+.ionide

+ 16 - 12
frameworks/FSharp/giraffe/README.md

@@ -1,15 +1,20 @@
-# girrafe Tests on Linux
-This includes tests for plaintext and json serialization.
+# Giraffe Benchmarks on Linux
+
+This application tests Giraffe in 3 modes:
+
+- Default: Using Giraffe's Endpoint Routing APIs with the `System.Text.Json` serializer
+- Utf8Json: Testing the JSON endpoint with the `Utf8Json` serializer
+- Newtonsoft: Testing the JSON endpoint with the `NewtonsoftJson` serializer
 
 
 ## Infrastructure Software Versions
 ## Infrastructure Software Versions
 
 
 **Language**
 **Language**
 
 
-* F# 4.6
+* F# 5.0
 
 
 **Platforms**
 **Platforms**
 
 
-* .NET Core (Windows and Linux)
+* .NET 5 (Windows and Linux)
 
 
 **Web Servers**
 **Web Servers**
 
 
@@ -17,16 +22,15 @@ This includes tests for plaintext and json serialization.
 
 
 **Web Stack**
 **Web Stack**
 
 
-* [giraffe](https://github.com/giraffe-fsharp/Giraffe)
+* [Giraffe](https://github.com/giraffe-fsharp/Giraffe)
 * ASP.NET Core
 * ASP.NET Core
 
 
 ## Paths & Source for Tests
 ## Paths & Source for Tests
 
 
-* [Plaintext](src/App/Stock.fs): "/plaintext"
-* [Plaintext handwritten](src/App/Custom.fs): "/plaintext"
-* [JSON serialization](src/App/Stock.fs): "/json"
-* [JSON serialization via utf8json lib](src/App/Custom.fs): "/json"
-* [Fortunes using Dapper](src/App/Stock.fs): "/fortunes"
-* [Fortunes using Dapper and Custom renderer](src/App/Custom.fs): "/fortunes"
+All source code is inside `Program.fs`.
+
+App listens for a signle command line argument to pick the desired JSON implementation:
 
 
-App listents for command line arguments to pick specific implementation. If "stock" passed as command line argument it will use out of the box handlers, otherwise will use custom ones.
+    - `system`: `System.Text.Json`
+    - `utf8`: `Utf8Json`
+    - `newtonsoft`: `Newtonsoft.Json`

+ 6 - 24
frameworks/FSharp/giraffe/benchmark_config.json

@@ -18,7 +18,7 @@
         "webserver": "Kestrel",
         "webserver": "Kestrel",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
-        "display_name": "Giraffe, Dapper",
+        "display_name": "Giraffe, Default with Dapper",
         "notes": "",
         "notes": "",
         "versus": "aspcore"
         "versus": "aspcore"
       },
       },
@@ -36,17 +36,17 @@
         "webserver": "Kestrel",
         "webserver": "Kestrel",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
-        "display_name": "Giraffe, utf8json",
+        "display_name": "Giraffe, Utf8Json",
         "notes": "",
         "notes": "",
         "versus": "aspcore"
         "versus": "aspcore"
       },
       },
-      "utf8direct": {
-        "plaintext_url": "/plaintext",
+      "newtonsoft": {
+        "json_url": "/json",
         "port": 8080,
         "port": 8080,
         "approach": "Realistic",
         "approach": "Realistic",
         "classification": "Micro",
         "classification": "Micro",
         "database": "None",
         "database": "None",
-        "framework": "giraffe",
+        "framework": "Giraffe",
         "language": "F#",
         "language": "F#",
         "orm": "Raw",
         "orm": "Raw",
         "platform": ".NET",
         "platform": ".NET",
@@ -54,25 +54,7 @@
         "webserver": "Kestrel",
         "webserver": "Kestrel",
         "os": "Linux",
         "os": "Linux",
         "database_os": "Linux",
         "database_os": "Linux",
-        "display_name": "Giraffe, Direct utf8",
-        "notes": "",
-        "versus": "aspcore"
-      },
-      "stripped": {
-        "fortune_url": "/fortunes",
-        "port": 8080,
-        "approach": "Stripped",
-        "classification": "Micro",
-        "database": "Postgres",
-        "framework": "giraffe",
-        "language": "F#",
-        "orm": "micro",
-        "platform": ".NET",
-        "flavor": "CoreCLR",
-        "webserver": "Kestrel",
-        "os": "Linux",
-        "database_os": "Linux",
-        "display_name": "Giraffe, Custom Rendering, Dapper",
+        "display_name": "Giraffe, NewtonsoftJson",
         "notes": "",
         "notes": "",
         "versus": "aspcore"
         "versus": "aspcore"
       }
       }

+ 1 - 1
frameworks/FSharp/giraffe/giraffe-stripped.dockerfile → frameworks/FSharp/giraffe/giraffe-newtonsoft.dockerfile

@@ -8,4 +8,4 @@ ENV ASPNETCORE_URLS http://+:8080
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 
 
-ENTRYPOINT ["dotnet", "App.dll"]
+ENTRYPOINT ["dotnet", "App.dll", "newtonsoft"]

+ 0 - 11
frameworks/FSharp/giraffe/giraffe-utf8direct.dockerfile

@@ -1,11 +0,0 @@
-FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
-WORKDIR /app
-COPY src/App .
-RUN dotnet publish -c Release -o out
-
-FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS runtime
-ENV ASPNETCORE_URLS http://+:8080
-WORKDIR /app
-COPY --from=build /app/out ./
-
-ENTRYPOINT ["dotnet", "App.dll"]

+ 1 - 1
frameworks/FSharp/giraffe/giraffe-utf8json.dockerfile

@@ -8,4 +8,4 @@ ENV ASPNETCORE_URLS http://+:8080
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 
 
-ENTRYPOINT ["dotnet", "App.dll"]
+ENTRYPOINT ["dotnet", "App.dll", "utf8"]

+ 1 - 1
frameworks/FSharp/giraffe/giraffe.dockerfile

@@ -8,4 +8,4 @@ ENV ASPNETCORE_URLS http://+:8080
 WORKDIR /app
 WORKDIR /app
 COPY --from=build /app/out ./
 COPY --from=build /app/out ./
 
 
-ENTRYPOINT ["dotnet", "App.dll", "stock"]
+ENTRYPOINT ["dotnet", "App.dll", "system"]

+ 1 - 10
frameworks/FSharp/giraffe/src/App/App.fsproj

@@ -2,25 +2,16 @@
 
 
   <PropertyGroup>
   <PropertyGroup>
     <TargetFramework>net5.0</TargetFramework>
     <TargetFramework>net5.0</TargetFramework>
-    <DebugType>portable</DebugType>
-    <AssemblyName>App</AssemblyName>
-    <OutputType>Exe</OutputType>
     <EnableDefaultContentItems>false</EnableDefaultContentItems>
     <EnableDefaultContentItems>false</EnableDefaultContentItems>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>
     <PackageReference Include="Dapper" Version="2.0.35" />
     <PackageReference Include="Dapper" Version="2.0.35" />
-    <PackageReference Include="Giraffe" Version="5.0.0-alpha-003" />
+    <PackageReference Include="Giraffe" Version="5.0.0-rc-6" />
     <PackageReference Include="Npgsql" Version="5.0.0-alpha1" />
     <PackageReference Include="Npgsql" Version="5.0.0-alpha1" />
-    <PackageReference Update="FSharp.Core" Version="5.0.0-beta.20417.1" />
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>
-    <Compile Include="Models.fs" />
-    <Compile Include="HtmlViews.fs" />
-    <Compile Include="StatefullRendering.fs" />
-    <Compile Include="Custom.fs" />
-    <Compile Include="Stock.fs" />
     <Compile Include="Program.fs" />
     <Compile Include="Program.fs" />
   </ItemGroup>
   </ItemGroup>
 
 

+ 0 - 115
frameworks/FSharp/giraffe/src/App/Custom.fs

@@ -1,115 +0,0 @@
-module Custom
-
-open App
-open Dapper
-open Giraffe
-open System
-open Models
-open Npgsql
-open FSharp.Control.Tasks
-open System.IO
-
-let private DefaultCapacity = 1386
-let private MaxBuilderSize = DefaultCapacity * 3
-let private BufferSize = 27
-
-type MemoryStreamCache = 
-    
-    [<ThreadStatic>]
-    [<DefaultValue>]
-    static val mutable private instance: MemoryStream
-
-    static member Get() = MemoryStreamCache.Get(DefaultCapacity)
-    static member Get(capacity:int) = 
-        
-        if capacity <= MaxBuilderSize then
-            let ms = MemoryStreamCache.instance;
-            let capacity = max capacity DefaultCapacity
-            
-            if ms <> null && capacity <= ms.Capacity then
-                MemoryStreamCache.instance <- null;
-                ms.SetLength 0L
-                ms
-            else
-                new MemoryStream(capacity)
-        else
-            new MemoryStream(capacity)
-
-    static member Release(ms:MemoryStream) = 
-        if ms.Capacity <= MaxBuilderSize then
-            MemoryStreamCache.instance <- ms
-
-let application : HttpHandler = 
-
-    let inline contentLength x = new Nullable<int64> ( int64 x )
-
-    let json' data : HttpHandler =
-        let bytes = Utf8Json.JsonSerializer.Serialize(data)
-        fun _ ctx -> 
-            ctx.Response.ContentLength <- contentLength bytes.Length
-            ctx.Response.ContentType <- "application/json"
-            ctx.Response.StatusCode <- 200
-            task {
-                do! ctx.Response.Body.WriteAsync(bytes, 0, bytes.Length)
-                return Some ctx
-            }
-
-    let text' (msg:string): HttpHandler = 
-        let bytes = System.Text.Encoding.UTF8.GetBytes(msg)
-        fun _ ctx ->
-            ctx.Response.ContentLength <- contentLength bytes.Length
-            ctx.Response.ContentType <- "text/plain"
-            ctx.Response.StatusCode <- 200
-            task {
-                do! ctx.Response.Body.WriteAsync(bytes, 0, bytes.Length)
-                return Some ctx
-            }
-  
-    let fortunes' : HttpHandler = 
-        let extra = { id = 0; message = "Additional fortune added at request time." }
-        fun _ ctx ->
-            let conn = new NpgsqlConnection(ConnectionString)
-            ctx.Response.RegisterForDispose conn
-            task {
-                let! data = conn.QueryAsync<Fortune>("SELECT id, message FROM fortune")
-
-                let fortunes = 
-                    let xs = data.AsList()
-                    xs.Add extra
-                    xs.Sort FortuneComparer
-                    xs
-
-                let html = MemoryStreamCache.Get()
-                let view = fortunes |> HtmlViews.fortunes 
-                StatefullRendering.renderHtmlToStream html view
-
-                ctx.Response.ContentType <- "text/html;charset=utf-8"
-                ctx.Response.ContentLength <- contentLength html.Length
-                ctx.Response.StatusCode <- 200
-                do! html.CopyToAsync ctx.Response.Body
-
-                MemoryStreamCache.Release html
-                return Some ctx
-            }
-
-    let routes' (routes: (string * HttpHandler) list) : HttpHandler = 
-        let table = Map.ofList routes
-        let notFound = setStatusCode 404
-
-        let go key = 
-            if table |> Map.containsKey key then
-                table.[key]
-            else
-                notFound
-
-        fun next ctx ->
-            let path = ctx.Request.Path.Value
-            let handler = go path
-            handler next ctx
-
-    routes' [
-        "/plaintext", text' "Hello, World!"
-        "/json", json' struct {| message = "Hello, World!" |}
-        "/fortunes", fortunes'
-    ]
-

+ 0 - 33
frameworks/FSharp/giraffe/src/App/HtmlViews.fs

@@ -1,33 +0,0 @@
-module HtmlViews
-
-open Giraffe.ViewEngine
-open Models
-
-let private fortunesHead = 
-    head [] [
-        title []  [ rawText "Fortunes" ]
-    ]
-
-let private layout (content: XmlNode list) =
-    html [] [
-        fortunesHead
-        body [] content
-    ]
-
-let private fortunesTableHeader = 
-    tr [] [
-        th [] [ rawText "id" ]
-        th [] [ rawText "message" ]
-    ]
-
-let fortunes (fortunes: Fortune seq) =
-    [
-        table [] [ 
-            yield fortunesTableHeader
-            for f in fortunes ->
-                tr [] [
-                    td [] [ rawText <| string f.id ]
-                    td [] [ encodedText <| f.message ]
-                ] 
-        ]
-    ] |> layout

+ 0 - 16
frameworks/FSharp/giraffe/src/App/Models.fs

@@ -1,16 +0,0 @@
-module Models
-
-open System.Collections.Generic
-open System
-
- [<CLIMutable>]
-type Fortune = { id: int; message: string }
-
-[<Literal>]
-let ConnectionString = "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=1024;NoResetOnClose=true;Enlist=false;Max Auto Prepare=3"
-
-type Implementation = Stock | Custom
-
-let FortuneComparer = { new IComparer<Fortune> with 
-    member self.Compare(a,b) = String.CompareOrdinal(a.message, b.message)
-}

+ 148 - 28
frameworks/FSharp/giraffe/src/App/Program.fs

@@ -1,28 +1,148 @@
-module App.App
-
-open Microsoft.AspNetCore.Hosting
-open Giraffe
-open Models
-
-[<EntryPoint>]
-let main args = 
-    let implementation = 
-        match args with
-        | [| "stock" |] -> Implementation.Stock
-        | _ -> Implementation.Custom
-
-    printfn "Running with %A implementation" implementation
-
-    let webApp = function
-    | Implementation.Custom -> Custom.application
-    | Implementation.Stock -> Stock.application
-
-    let app = webApp implementation
-
-    WebHostBuilder()
-        .UseKestrel()
-        .Configure(fun b -> b.UseGiraffe app)
-        .ConfigureServices(fun s -> s.AddGiraffe() |> ignore)
-        .Build()
-        .Run()
-    0
+namespace App
+
+[<AutoOpen>]
+module Common =
+    open System
+    open System.Collections.Generic
+
+    [<CLIMutable>]
+    type Fortune =
+        {
+            id      : int
+            message : string
+        }
+
+    [<Literal>]
+    let ConnectionString = "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=1024;NoResetOnClose=true;Enlist=false;Max Auto Prepare=3"
+
+    type JsonMode =
+        | System
+        | Utf8
+        | Newtonsoft
+
+    let FortuneComparer =
+        {
+            new IComparer<Fortune> with
+                member __.Compare (a, b) =
+                    String.CompareOrdinal(a.message, b.message)
+        }
+
+[<RequireQualifiedAccess>]
+module HtmlViews =
+    open Giraffe.ViewEngine
+
+    let private fortunesHead =
+        head [] [
+            title []  [ rawText "Fortunes" ]
+        ]
+
+    let private layout (content: XmlNode list) =
+        html [] [
+            fortunesHead
+            body [] content
+        ]
+
+    let private fortunesTableHeader =
+        tr [] [
+            th [] [ rawText "id" ]
+            th [] [ rawText "message" ]
+        ]
+
+    let fortunes (fortunes: Fortune seq) =
+        [
+            table [] [
+                yield fortunesTableHeader
+                for f in fortunes ->
+                    tr [] [
+                        td [] [ rawText <| string f.id ]
+                        td [] [ encodedText <| f.message ]
+                    ]
+            ]
+        ] |> layout
+
+[<RequireQualifiedAccess>]
+module HttpHandlers =
+    open Giraffe
+    open Giraffe.EndpointRouting
+    open Giraffe.ViewEngine
+    open FSharp.Control.Tasks
+    open Dapper
+    open Npgsql
+
+    let private extra =
+        {
+            id      = 0
+            message = "Additional fortune added at request time."
+        }
+
+    let private fortunes : HttpHandler =
+        fun _ ctx ->
+            task {
+                use conn = new NpgsqlConnection(ConnectionString)
+                let! data = conn.QueryAsync<Fortune>("SELECT id, message FROM fortune")
+
+                let view =
+                    let xs = data.AsList()
+                    xs.Add extra
+                    xs.Sort FortuneComparer
+                    HtmlViews.fortunes xs
+
+                let bytes = RenderView.AsBytes.htmlDocument view
+
+                ctx.SetContentType "text/html;charset=utf-8"
+                return! ctx.WriteBytesAsync bytes
+            }
+
+    let endpoints : Endpoint list =
+        [
+            route "/plaintext" (text "Hello, World!")
+            route "/json" (json {| message = "Hello, World!" |})
+            route "/fortunes" fortunes
+        ]
+
+module Main =
+    open Microsoft.AspNetCore.Builder
+    open Microsoft.AspNetCore.Hosting
+    open Microsoft.Extensions.DependencyInjection
+    open Giraffe
+    open Giraffe.EndpointRouting
+
+    [<EntryPoint>]
+    let main args =
+        let jsonMode =
+            match args with
+            | [| "newtonsoft" |] -> Newtonsoft
+            | [| "utf8" |]       -> Utf8
+            | _                  -> System
+
+        printfn "Running with %A JSON serializer" jsonMode
+
+        let jsonSerializer =
+            match jsonMode with
+            | System ->
+                SystemTextJson.Serializer(SystemTextJson.Serializer.DefaultOptions)
+                :> Json.ISerializer
+            | Utf8 ->
+                Utf8Json.Serializer(Utf8Json.Serializer.DefaultResolver)
+                :> Json.ISerializer
+            | Newtonsoft ->
+                NewtonsoftJson.Serializer(NewtonsoftJson.Serializer.DefaultSettings)
+                :> Json.ISerializer
+
+        WebHostBuilder()
+            .UseKestrel()
+            .Configure(
+                fun builder ->
+                    builder
+                        .UseRouting()
+                        .UseGiraffe HttpHandlers.endpoints |> ignore)
+            .ConfigureServices(
+                fun services ->
+                    services
+                        .AddRouting()
+                        .AddGiraffe()
+                        .AddSingleton(jsonSerializer)
+                    |> ignore)
+            .Build()
+            .Run()
+        0

+ 0 - 78
frameworks/FSharp/giraffe/src/App/StatefullRendering.fs

@@ -1,78 +0,0 @@
-namespace App
-open System.Text
-open Giraffe.ViewEngine
-open System.Net
-open System.IO
-
-module rec StatefullRendering =
-
-    let private UTF8WithoutBOM = new UTF8Encoding(false)
-
-    let inline private add (str:string) (target: StreamWriter) =
-        target.Write str
-        target
-
-    let inline private add' (str:string) (target: StreamWriter) =
-        target.Write str
-    
-    let private closingBracket = ">"
-
-    let private writeStartElement target elemName (attributes : XmlAttribute array) : unit =
-
-        match attributes with
-        | [||] -> 
-            target
-            |> add "<"
-            |> add elemName
-            |> add' closingBracket
-
-        | _  ->
-            target 
-            |> add "<" 
-            |> add' elemName
-
-            for attr in attributes do
-                match attr with
-                | KeyValue (k, v) -> 
-                    target 
-                    |> add " " 
-                    |> add k 
-                    |> add "=\"" 
-                    |> add' (WebUtility.HtmlEncode v)
-
-                | Boolean k -> 
-                    target 
-                    |> add " " 
-                    |> add' k
-
-            target 
-            |> add' closingBracket
-
-    let private writeEndElement target elemName = 
-        target 
-        |> add "</" 
-        |> add elemName 
-        |> add' ">"
-
-    let private writeParentNode target ((name, attrs) : XmlElement) (nodes : XmlNode list) =
-        writeStartElement target name attrs 
-        nodes |> List.iter (writeHtmlNode target)
-        name  |> writeEndElement target
-
-    let rec private writeHtmlNode (target: StreamWriter) (node : XmlNode)  =
-        match node with
-        | Text text -> target |> add' text
-        | ParentNode (e, nodes) -> writeParentNode target e nodes
-        | VoidElement (n, attrs) -> writeStartElement target n attrs
-    
-    let renderHtmlToStream (ms:MemoryStream) node = 
-        let sb = new StreamWriter(ms, UTF8WithoutBOM)
-        sb.WriteLine "<!DOCTYPE html>"
-        writeHtmlNode sb node
-        sb.Flush()
-        ms.Seek(0L, SeekOrigin.Begin) |> ignore
-
-    let renderHtml node =
-        let ms = new MemoryStream()
-        renderHtmlToStream ms node
-        ms

+ 0 - 35
frameworks/FSharp/giraffe/src/App/Stock.fs

@@ -1,35 +0,0 @@
-module Stock
-
-open Giraffe
-open Giraffe.ViewEngine
-open Dapper
-open Npgsql
-open Models
-open FSharp.Control.Tasks
-
-let extra = {id = 0; message = "Additional fortune added at request time."}
-
-let fortunes : HttpHandler = 
-    fun _ ctx ->
-        task {
-            use conn = new NpgsqlConnection(ConnectionString)
-            let! data = conn.QueryAsync<Fortune>("SELECT id, message FROM fortune")
-
-            let view = 
-                let xs = data.AsList()
-                xs.Add extra
-                xs.Sort FortuneComparer
-                HtmlViews.fortunes xs
-
-            let bytes = RenderView.AsBytes.htmlDocument view
-                
-            ctx.SetContentType "text/html;charset=utf-8"
-            return! ctx.WriteBytesAsync bytes
-        }
-
-let application : HttpHandler = 
-    choose [
-        route "/plaintext" >=> text "Hello, World!" 
-        route "/json" >=> json {| message = "Hello, World!" |}
-        route "/fortunes" >=> fortunes
-    ]