Skip to content

Commit

Permalink
[F#/Oxpecker] Improved "update" and "fortunes" benchmarks (#9357)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lanayx authored Nov 25, 2024
1 parent f466068 commit c0164be
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 55 deletions.
10 changes: 5 additions & 5 deletions frameworks/FSharp/oxpecker/src/App/App.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
</ItemGroup>

<ItemGroup>
<PackageReference Update="FSharp.Core" Version="8.0.400" />
<PackageReference Include="Oxpecker" Version="0.14.1" />
<PackageReference Include="Oxpecker.ViewEngine" Version="0.14.0" />
<PackageReference Include="Npgsql" Version="8.0.3" />
<PackageReference Include="SpanJson" Version="4.2.0" />
<PackageReference Update="FSharp.Core" Version="9.0.100" />
<PackageReference Include="Oxpecker" Version="1.0.0" />
<PackageReference Include="Oxpecker.ViewEngine" Version="1.0.0" />
<PackageReference Include="Npgsql" Version="9.0.1" />
<PackageReference Include="SpanJson" Version="4.2.1" />
</ItemGroup>
</Project>
6 changes: 4 additions & 2 deletions frameworks/FSharp/oxpecker/src/App/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module Common =
[<Struct>]
[<CLIMutable>]
type JsonMessage = {
message : string
message: string
}

[<CLIMutable>]
Expand All @@ -26,7 +26,9 @@ module Common =
}

[<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"
let ConnectionString = "Server=tfb-database;Database=hello_world;User Id=benchmarkdbuser;Password=benchmarkdbpass;Maximum Pool Size=1024;NoResetOnClose=true;Enlist=false"
[<Literal>]
let MultiplexedConnectionString = ConnectionString + ";Max Auto Prepare=3;Multiplexing=true"

let FortuneComparer = {
new IComparer<Fortune> with
Expand Down
31 changes: 18 additions & 13 deletions frameworks/FSharp/oxpecker/src/App/Db.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ namespace App

open System
open System.Data
open System.Data.Common
open System.Text
open Microsoft.Extensions.ObjectPool
open Npgsql


Expand All @@ -15,6 +14,7 @@ module Db =
use db = new NpgsqlConnection(ConnectionString)
use cmd = db.CreateCommand(CommandText = "SELECT id, message FROM fortune")
do! db.OpenAsync()
do! cmd.PrepareAsync()
use! rdr = cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection)
while! rdr.ReadAsync() do
result.Add { id = rdr.GetInt32(0); message = rdr.GetString(1) }
Expand All @@ -31,7 +31,7 @@ module Db =
TypedValue = Random.Shared.Next(1, 10001)
)
cmd.Parameters.Add(id) |> ignore
cmd
struct(cmd, id)

let private readSingleRow (cmd: NpgsqlCommand) =
task {
Expand All @@ -43,41 +43,46 @@ module Db =
let loadSingleRow () =
task {
use db = new NpgsqlConnection(ConnectionString)
let struct(cmd', _) = createReadCommand db
use cmd = cmd'
do! db.OpenAsync()
use cmd = createReadCommand db
do! cmd.PrepareAsync()
return! readSingleRow cmd
}

let private readMultipleRows (count: int) (conn: NpgsqlConnection) =
let result = Array.zeroCreate count
task {
use cmd = createReadCommand conn
let struct(cmd', idParam) = createReadCommand conn
use cmd = cmd'
for i in 0..result.Length-1 do
cmd.Parameters["@Id"].Value <- Random.Shared.Next(1, 10001)
let! row = readSingleRow cmd
result[i] <- row
idParam.TypedValue <- Random.Shared.Next(1, 10001)
return result
}

let loadMultipleRows (count: int) =
task {
use db = new NpgsqlConnection(ConnectionString)
use db = new NpgsqlConnection(MultiplexedConnectionString)
do! db.OpenAsync()
return! readMultipleRows count db
}

let private maxBatch = 500
let private queries = Array.zeroCreate (maxBatch + 1)
let private stringBuilderPool = DefaultObjectPoolProvider().CreateStringBuilderPool()
let private batchUpdateString batchSize =
match queries[batchSize] with
| null ->
let lastIndex = batchSize - 1
let sb = StringBuilder()
let sb = stringBuilderPool.Get()
sb.Append("UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES ") |> ignore
for i in 0..lastIndex-1 do
sb.AppendFormat("(@Id_{0}, @Rn_{0}), ", i) |> ignore
sb.AppendFormat("(@Id_{0}, @Rn_{0}) ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id", lastIndex) |> ignore
let result = sb.ToString()
stringBuilderPool.Return(sb)
queries[batchSize] <- result
result
| q ->
Expand All @@ -95,15 +100,15 @@ module Db =
for i in 0..results.Length-1 do
let randomNumber = Random.Shared.Next(1, 10001)
let struct(rnParamName, idParamName) = paramNames[i]
let random = NpgsqlParameter<int>(ParameterName = rnParamName, DbType = DbType.Int32, TypedValue = randomNumber)
command.Parameters.Add(random) |> ignore
let id = NpgsqlParameter<int>(ParameterName = idParamName, DbType = DbType.Int32, TypedValue = results[i].id)
command.Parameters.Add(id) |> ignore
command.Parameters.Add(NpgsqlParameter<int>(
ParameterName = rnParamName, DbType = DbType.Int32, TypedValue = randomNumber)) |> ignore
command.Parameters.Add(NpgsqlParameter<int>(
ParameterName = idParamName, DbType = DbType.Int32, TypedValue = results[i].id)) |> ignore
results[i] <- { results[i] with randomnumber = randomNumber }

let doMultipleUpdates (count: int) =
task {
use conn = new NpgsqlConnection(ConnectionString)
use conn = new NpgsqlConnection(MultiplexedConnectionString)
do! conn.OpenAsync()
let! results = readMultipleRows count conn
use cmd = conn.CreateCommand(CommandText = batchUpdateString count)
Expand Down
39 changes: 13 additions & 26 deletions frameworks/FSharp/oxpecker/src/App/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@ namespace App

open System
open Oxpecker
open System.Runtime.InteropServices

[<RequireQualifiedAccess>]
module HtmlViews =
module HttpHandlers =
open System.Text
open Microsoft.AspNetCore.Http
open SpanJson
open Oxpecker.ViewEngine

let private head, tail =
let private extra =
{
id = 0
message = "Additional fortune added at request time."
}

let private fortunesHeadAndTail =
(fun (content: HtmlElement) ->
html() {
head() {
Expand All @@ -26,32 +34,11 @@ module HtmlViews =
} :> HtmlElement
) |> RenderHelpers.prerender

let fortunes (fortunesData: ResizeArray<Fortune>) =
let fragment = __()
for fortune in CollectionsMarshal.AsSpan fortunesData do
tr() {
td() { raw <| string fortune.id }
td() { fortune.message }
}
|> fragment.AddChild
RenderHelpers.combine head tail fragment

[<RequireQualifiedAccess>]
module HttpHandlers =
open System.Text
open Microsoft.AspNetCore.Http
open SpanJson

let private extra =
{
id = 0
message = "Additional fortune added at request time."
}

let rec private renderFortunes (ctx: HttpContext) (data: ResizeArray<Fortune>) =
data.Add extra
data.Sort FortuneComparer
data |> HtmlViews.fortunes |> ctx.WriteHtmlViewChunked
RenderHelpers.CombinedElement(fortunesHeadAndTail, data)
|> ctx.WriteHtmlViewChunked

let fortunes : EndpointHandler =
fun ctx ->
Expand Down
29 changes: 20 additions & 9 deletions frameworks/FSharp/oxpecker/src/App/RenderHelpers.fs
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
module RenderHelpers

open System.Text
open App
open Oxpecker.ViewEngine

type HeadAndTail =
{
Head: string
Tail: string
}

[<Struct>]
type CombinedElement(ht: HeadAndTail, fortunesData: ResizeArray<Fortune>) =
interface HtmlElement with
member this.Render(sb) =
sb.Append(ht.Head) |> ignore
for fortune in fortunesData do
(tr() {
td() { raw <| string fortune.id }
td() { fortune.message }
}).Render(sb)
sb.Append(ht.Tail) |> ignore

let prerender (view: HtmlElement -> HtmlElement) =
let sb = StringBuilder()
let mutable head = ""
Expand All @@ -13,12 +32,4 @@
sb.Clear() |> ignore }
let readyView = view fakeHole
readyView.Render(sb)
(head, sb.ToString())

let inline combine (head: string) (tail: string) (hole: HtmlElement) =
{ new HtmlElement with
member this.Render(sb) =
sb.Append(head) |> ignore
hole.Render(sb)
sb.Append(tail) |> ignore
}
{ Head = head; Tail = sb.ToString() }

0 comments on commit c0164be

Please sign in to comment.