Skip to content

Commit c0164be

Browse files
authored
[F#/Oxpecker] Improved "update" and "fortunes" benchmarks (#9357)
1 parent f466068 commit c0164be

File tree

5 files changed

+60
-55
lines changed

5 files changed

+60
-55
lines changed

frameworks/FSharp/oxpecker/src/App/App.fsproj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
</ItemGroup>
1414

1515
<ItemGroup>
16-
<PackageReference Update="FSharp.Core" Version="8.0.400" />
17-
<PackageReference Include="Oxpecker" Version="0.14.1" />
18-
<PackageReference Include="Oxpecker.ViewEngine" Version="0.14.0" />
19-
<PackageReference Include="Npgsql" Version="8.0.3" />
20-
<PackageReference Include="SpanJson" Version="4.2.0" />
16+
<PackageReference Update="FSharp.Core" Version="9.0.100" />
17+
<PackageReference Include="Oxpecker" Version="1.0.0" />
18+
<PackageReference Include="Oxpecker.ViewEngine" Version="1.0.0" />
19+
<PackageReference Include="Npgsql" Version="9.0.1" />
20+
<PackageReference Include="SpanJson" Version="4.2.1" />
2121
</ItemGroup>
2222
</Project>

frameworks/FSharp/oxpecker/src/App/Common.fs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module Common =
99
[<Struct>]
1010
[<CLIMutable>]
1111
type JsonMessage = {
12-
message : string
12+
message: string
1313
}
1414

1515
[<CLIMutable>]
@@ -26,7 +26,9 @@ module Common =
2626
}
2727

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

3133
let FortuneComparer = {
3234
new IComparer<Fortune> with

frameworks/FSharp/oxpecker/src/App/Db.fs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ namespace App
22

33
open System
44
open System.Data
5-
open System.Data.Common
6-
open System.Text
5+
open Microsoft.Extensions.ObjectPool
76
open Npgsql
87

98

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

3636
let private readSingleRow (cmd: NpgsqlCommand) =
3737
task {
@@ -43,41 +43,46 @@ module Db =
4343
let loadSingleRow () =
4444
task {
4545
use db = new NpgsqlConnection(ConnectionString)
46+
let struct(cmd', _) = createReadCommand db
47+
use cmd = cmd'
4648
do! db.OpenAsync()
47-
use cmd = createReadCommand db
49+
do! cmd.PrepareAsync()
4850
return! readSingleRow cmd
4951
}
5052

5153
let private readMultipleRows (count: int) (conn: NpgsqlConnection) =
5254
let result = Array.zeroCreate count
5355
task {
54-
use cmd = createReadCommand conn
56+
let struct(cmd', idParam) = createReadCommand conn
57+
use cmd = cmd'
5558
for i in 0..result.Length-1 do
56-
cmd.Parameters["@Id"].Value <- Random.Shared.Next(1, 10001)
5759
let! row = readSingleRow cmd
5860
result[i] <- row
61+
idParam.TypedValue <- Random.Shared.Next(1, 10001)
5962
return result
6063
}
6164

6265
let loadMultipleRows (count: int) =
6366
task {
64-
use db = new NpgsqlConnection(ConnectionString)
67+
use db = new NpgsqlConnection(MultiplexedConnectionString)
6568
do! db.OpenAsync()
6669
return! readMultipleRows count db
6770
}
6871

6972
let private maxBatch = 500
7073
let private queries = Array.zeroCreate (maxBatch + 1)
74+
let private stringBuilderPool = DefaultObjectPoolProvider().CreateStringBuilderPool()
7175
let private batchUpdateString batchSize =
7276
match queries[batchSize] with
7377
| null ->
7478
let lastIndex = batchSize - 1
75-
let sb = StringBuilder()
79+
let sb = stringBuilderPool.Get()
7680
sb.Append("UPDATE world SET randomNumber = temp.randomNumber FROM (VALUES ") |> ignore
7781
for i in 0..lastIndex-1 do
7882
sb.AppendFormat("(@Id_{0}, @Rn_{0}), ", i) |> ignore
7983
sb.AppendFormat("(@Id_{0}, @Rn_{0}) ORDER BY 1) AS temp(id, randomNumber) WHERE temp.id = world.id", lastIndex) |> ignore
8084
let result = sb.ToString()
85+
stringBuilderPool.Return(sb)
8186
queries[batchSize] <- result
8287
result
8388
| q ->
@@ -95,15 +100,15 @@ module Db =
95100
for i in 0..results.Length-1 do
96101
let randomNumber = Random.Shared.Next(1, 10001)
97102
let struct(rnParamName, idParamName) = paramNames[i]
98-
let random = NpgsqlParameter<int>(ParameterName = rnParamName, DbType = DbType.Int32, TypedValue = randomNumber)
99-
command.Parameters.Add(random) |> ignore
100-
let id = NpgsqlParameter<int>(ParameterName = idParamName, DbType = DbType.Int32, TypedValue = results[i].id)
101-
command.Parameters.Add(id) |> ignore
103+
command.Parameters.Add(NpgsqlParameter<int>(
104+
ParameterName = rnParamName, DbType = DbType.Int32, TypedValue = randomNumber)) |> ignore
105+
command.Parameters.Add(NpgsqlParameter<int>(
106+
ParameterName = idParamName, DbType = DbType.Int32, TypedValue = results[i].id)) |> ignore
102107
results[i] <- { results[i] with randomnumber = randomNumber }
103108

104109
let doMultipleUpdates (count: int) =
105110
task {
106-
use conn = new NpgsqlConnection(ConnectionString)
111+
use conn = new NpgsqlConnection(MultiplexedConnectionString)
107112
do! conn.OpenAsync()
108113
let! results = readMultipleRows count conn
109114
use cmd = conn.CreateCommand(CommandText = batchUpdateString count)

frameworks/FSharp/oxpecker/src/App/Program.fs

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@ namespace App
22

33
open System
44
open Oxpecker
5-
open System.Runtime.InteropServices
65

76
[<RequireQualifiedAccess>]
8-
module HtmlViews =
7+
module HttpHandlers =
8+
open System.Text
9+
open Microsoft.AspNetCore.Http
10+
open SpanJson
911
open Oxpecker.ViewEngine
1012

11-
let private head, tail =
13+
let private extra =
14+
{
15+
id = 0
16+
message = "Additional fortune added at request time."
17+
}
18+
19+
let private fortunesHeadAndTail =
1220
(fun (content: HtmlElement) ->
1321
html() {
1422
head() {
@@ -26,32 +34,11 @@ module HtmlViews =
2634
} :> HtmlElement
2735
) |> RenderHelpers.prerender
2836

29-
let fortunes (fortunesData: ResizeArray<Fortune>) =
30-
let fragment = __()
31-
for fortune in CollectionsMarshal.AsSpan fortunesData do
32-
tr() {
33-
td() { raw <| string fortune.id }
34-
td() { fortune.message }
35-
}
36-
|> fragment.AddChild
37-
RenderHelpers.combine head tail fragment
38-
39-
[<RequireQualifiedAccess>]
40-
module HttpHandlers =
41-
open System.Text
42-
open Microsoft.AspNetCore.Http
43-
open SpanJson
44-
45-
let private extra =
46-
{
47-
id = 0
48-
message = "Additional fortune added at request time."
49-
}
50-
5137
let rec private renderFortunes (ctx: HttpContext) (data: ResizeArray<Fortune>) =
5238
data.Add extra
5339
data.Sort FortuneComparer
54-
data |> HtmlViews.fortunes |> ctx.WriteHtmlViewChunked
40+
RenderHelpers.CombinedElement(fortunesHeadAndTail, data)
41+
|> ctx.WriteHtmlViewChunked
5542

5643
let fortunes : EndpointHandler =
5744
fun ctx ->
Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,27 @@
11
module RenderHelpers
22

33
open System.Text
4+
open App
45
open Oxpecker.ViewEngine
56

7+
type HeadAndTail =
8+
{
9+
Head: string
10+
Tail: string
11+
}
12+
13+
[<Struct>]
14+
type CombinedElement(ht: HeadAndTail, fortunesData: ResizeArray<Fortune>) =
15+
interface HtmlElement with
16+
member this.Render(sb) =
17+
sb.Append(ht.Head) |> ignore
18+
for fortune in fortunesData do
19+
(tr() {
20+
td() { raw <| string fortune.id }
21+
td() { fortune.message }
22+
}).Render(sb)
23+
sb.Append(ht.Tail) |> ignore
24+
625
let prerender (view: HtmlElement -> HtmlElement) =
726
let sb = StringBuilder()
827
let mutable head = ""
@@ -13,12 +32,4 @@
1332
sb.Clear() |> ignore }
1433
let readyView = view fakeHole
1534
readyView.Render(sb)
16-
(head, sb.ToString())
17-
18-
let inline combine (head: string) (tail: string) (hole: HtmlElement) =
19-
{ new HtmlElement with
20-
member this.Render(sb) =
21-
sb.Append(head) |> ignore
22-
hole.Render(sb)
23-
sb.Append(tail) |> ignore
24-
}
35+
{ Head = head; Tail = sb.ToString() }

0 commit comments

Comments
 (0)