Skip to content

Commit

Permalink
Merge pull request #310 from krymtkts:feature/benchmark
Browse files Browse the repository at this point in the history
Add benchmarks for `Pocof` module
  • Loading branch information
krymtkts authored Jan 25, 2025
2 parents be5b28d + 9c4416d commit 784207c
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 140 deletions.
98 changes: 98 additions & 0 deletions src/pocof.Benchmark/Benchmarks.fs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
module pocof.Benchmark

open BenchmarkDotNet.Attributes
open BenchmarkDotNet.Engines

open System
open System.Collections
open System.Management.Automation

open Pocof
open Pocof.Data
open Pocof.Test

[<MemoryDiagnoser>]
type PocofBenchmarks() =
Expand Down Expand Up @@ -210,3 +212,99 @@ type QueryBenchmarks() =
[<Benchmark>]
member __.run_dict_property() =
Query.run __.PropertyContext __.Dicts props

[<MemoryDiagnoser>]
type PocofInteractBenchmarks() =
let prompt = ">"

let state: InternalState =
{ QueryState =
{ Query = "foo"
Cursor = 3
WindowBeginningCursor = 0
WindowWidth = 0
InputMode = InputMode.Input }
QueryCondition =
{ Matcher = Matcher.Match
Operator = Operator.And
CaseSensitive = true
Invert = false }
PropertySearch = PropertySearch.NoSearch
SuppressProperties = false
Refresh = Refresh.Required }

let publishEvent _ = ()

[<Params(10, 100, 1000)>]
member val EntryCount = 0 with get, set

[<Params(1, 3, 5, 7, 9)>]
member val QueryCount = 0 with get, set

member val Objects: Entry pseq = PSeq.empty with get, set
member val Dicts: Entry pseq = PSeq.empty with get, set
member val Keys: ConsoleKeyInfo option list = [] with get, set

[<GlobalSetup>]
member __.GlobalSetup() =
__.Objects <-
seq { 1 .. __.EntryCount }
|> Seq.map (string >> PSObject.AsPSObject >> Entry.Obj)
|> PSeq.ofSeq

__.Dicts <-
seq { 1 .. __.EntryCount }
|> Seq.map (fun x -> ("key", x) |> DictionaryEntry |> Entry.Dict)
|> PSeq.ofSeq

__.Keys <-
[ __.QueryCount .. 1 ]
|> List.collect (fun x ->
[ match x with
| 1
| 3
| 5
| 7
| 9 ->
let c = x |> (+) 48 |> char
MockRawUI.ConsoleKey c (Enum.Parse(typeof<ConsoleKey>, c.ToString()) :?> ConsoleKey)
| _ -> None
MockRawUI.ConsoleKey ' ' ConsoleKey.Spacebar ])
|> List.append [ MockRawUI.ConsoleKey '\000' ConsoleKey.Enter ]
|> List.rev

[<Benchmark>]
member __.interact_obj() =
let rui = new MockRawUI(60, 30, __.Keys)

let config: InternalConfig =
{ NotInteractive = true
Layout = Layout.TopDown
Keymaps = Keys.defaultKeymap
WordDelimiters = ";:,.[]{}()/\\|!?^&*-=+'\"–—―"
Prompt = prompt
PromptLength = prompt |> String.length
Properties = []
PropertiesMap = Map [] }

use buff = Screen.init (fun _ -> rui) (fun _ -> Seq.empty) config.Layout prompt
// NOTE: use Seq.length to force strict evaluation of the sequence
Pocof.interact config state buff publishEvent __.Objects |> Seq.length |> ignore

[<Benchmark>]
member __.interact_dict() =
let rui = new MockRawUI(60, 30, __.Keys)

let config: InternalConfig =
{ NotInteractive = true
Layout = Layout.TopDown
Keymaps = Keys.defaultKeymap
WordDelimiters = ";:,.[]{}()/\\|!?^&*-=+'\"–—―"
Prompt = prompt
PromptLength = prompt |> String.length
Properties = []
PropertiesMap = Map [] }

use buff = Screen.init (fun _ -> rui) (fun _ -> Seq.empty) config.Layout prompt
// NOTE: use Seq.length to force strict evaluation of the sequence
Pocof.interact config state buff publishEvent __.Dicts |> Seq.length |> ignore
9 changes: 1 addition & 8 deletions src/pocof.Benchmark/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,7 @@ open pocof.Benchmark

[<EntryPoint>]
let main argv =
BenchmarkSwitcher
.FromTypes(
[| typeof<PocofBenchmarks>
typeof<KeysBenchmarks>
typeof<HandleBenchmarks>
typeof<QueryBenchmarks> |]
)
.Run(argv)
BenchmarkSwitcher.FromAssembly(typeof<PocofBenchmarks>.Assembly).Run(argv)
|> ignore

0 // return an integer exit code
1 change: 1 addition & 0 deletions src/pocof.Benchmark/pocof.Benchmark.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

<ItemGroup>
<ProjectReference Include="..\pocof\pocof.fsproj" />
<ProjectReference Include="..\pocof.Test\pocof.Test.fsproj" />
</ItemGroup>

</Project>
137 changes: 137 additions & 0 deletions src/pocof.Test/Mock.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
namespace Pocof.Test

open System

open Pocof.LanguageExtension
open Pocof.Screen

[<AutoOpen>]
module Mock =
let generateLine x y =
List.replicate y <| String.replicate x " "

type MockRawUI =
val caAsInput: bool
val mutable height: int
val mutable width: int
val mutable x: int
val mutable y: int
val mutable screen: string list
val mutable keys: ConsoleKeyInfo option list
val mutable forceCancel: bool
static member xx = 50
static member yy = 30

new() =
// NOTE: accessing Console.TreatControlCAsInput will raise System.IO.IOException when running on GitHub Actions windows runner.
{ caAsInput = true
x = MockRawUI.xx
y = MockRawUI.yy
width = MockRawUI.xx
height = MockRawUI.yy
screen = generateLine MockRawUI.xx MockRawUI.yy
keys = [ MockRawUI.ConsoleKey '\000' ConsoleKey.Enter ]
forceCancel = false }

new(x: int, y: int) =
// NOTE: accessing Console.TreatControlCAsInput will raise System.IO.IOException when running on GitHub Actions windows runner.
{ caAsInput = true
x = x
y = y
width = x
height = y
screen = generateLine x y
keys = [ MockRawUI.ConsoleKey '\000' ConsoleKey.Enter ]
forceCancel = false }

new(x: int, y: int, keys: ConsoleKeyInfo option list) =
// NOTE: accessing Console.TreatControlCAsInput will raise System.IO.IOException when running on GitHub Actions windows runner.
{ caAsInput = true
x = x
y = y
width = x
height = y
screen = generateLine x y
keys = keys
forceCancel = false }

new(x: int, y: int, keys: ConsoleKeyInfo option list, forceCancel: bool) =
// NOTE: accessing Console.TreatControlCAsInput will raise System.IO.IOException when running on GitHub Actions windows runner.
{ caAsInput = true
x = x
y = y
width = x
height = y
screen = generateLine x y
keys = keys
forceCancel = forceCancel }

interface IRawUI with
member __.GetCursorPosition() = __.x, __.y

member __.SetCursorPosition (x: int) (y: int) =
__.x <- x
__.y <- y

member __.GetLengthInBufferCells(s: string) =
let isFullWidth c = // NOTE: simple full-width character detection for test.
let code = int c
code >= 0xFF00 && code <= 0xFF60

s |> Seq.cast<char> |> Seq.sumBy (fun c -> if isFullWidth c then 2 else 1)

member __.GetWindowWidth() = __.width
member __.GetWindowHeight() = __.height

member __.Write x y s =
__.screen <-
__.screen
|> List.mapi (fun i ss ->
match i with
| ii when ii = y -> ss |> String.upToIndex x |> (+) s
| _ ->
let l = (__ :> IRawUI).GetLengthInBufferCells ss

match l <= __.width with
| true -> ss + String.replicate (__.width - l) " "
| _ -> ss)

member __.ReadKey(_) =
match __.keys with
| [] -> failwith "key sequence is empty. check your test key sequence."
| k :: ks ->
match k with
| None -> failwith "key is none. check your test key sequence."
| Some k ->
__.keys <- ks
k

member __.KeyAvailable() =
match __.keys with
| [] ->
if __.forceCancel then
__.keys <- [ MockRawUI.ConsoleKey '\000' ConsoleKey.Escape ]
__.forceCancel <- false

false
| k :: ks ->
match k with
| None ->
__.keys <- ks
false
| Some _ -> true

member __.HideCursorWhileRendering() =
{ new IDisposable with
member _.Dispose() = () }

interface IDisposable with
member __.Dispose() = ()

static member ConsoleKey keyChar key =
new ConsoleKeyInfo(keyChar, key, false, false, false) |> Some

member __.Check() =
match __.keys with
| [] -> ()
| _ -> failwith "keys remains. probably test is broken."
2 changes: 1 addition & 1 deletion src/pocof.Test/Pocof.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ open FsUnitTyped

open Pocof
open Pocof.Data
open Screen.Mock
open Pocof.Test

let toObj x = x |> (PSObject.AsPSObject >> Entry.Obj)

Expand Down
Loading

0 comments on commit 784207c

Please sign in to comment.