Skip to content

Conversation

@cannorin
Copy link
Member

Related: #501

@cannorin cannorin requested a review from gusty September 13, 2022 06:05
@gusty gusty mentioned this pull request Sep 13, 2022
@cannorin
Copy link
Member Author

cannorin commented Sep 13, 2022

perf script:

#r "src/FSharpPlus/bin/Release/netstandard2.0/FSharpPlus.dll"
// #r "nuget:FSharpPlus"
open System
open FSharpPlus

// try `traverse`
// let xs = traverse Some [1;2;3]
// let ys = traverse List.singleton [1;2;3]

// type without IsLeftZero or Empty
type Wrapped<'a> = Wrapped of 'a with
  static member Return (x: 'a) = Wrapped x
  static member (>>=) (Wrapped a, f) = f a

// type with Empty
type Maybe<'a> = Just of 'a | Nothing with
  static member Empty : Maybe<'a> = Nothing

// type with IsLeftZero
type Either<'a, 'b> =  Left of 'a | Right of 'b with
  static member IsLeftZero(x: Either<'a, 'b>) =
    match x with Right _ -> true | _ -> false

// struct type without IsLeftZero or Empty
type [<Struct>] SWrapped<'a> = SWrapped of 'a with
  static member Return (x: 'a) = SWrapped x
  static member (>>=) (SWrapped a, f) = f a

// struct type with Empty
type [<Struct>] SMaybe<'a> = SJust of 'a | SNothing with
  static member Empty : SMaybe<'a> = SNothing

// struct type with IsLeftZero
type [<Struct>] SEither<'a, 'b> = SLeft of l:'a | SRight of r:'b with
  static member IsLeftZero(x: SEither<'a, 'b>) =
    match x with SRight _ -> true | _ -> false

// perf
open FSharpPlus.Control

let [<Literal>] N = 10000000

let before = DateTime.Now
for _ = 1 to N do
  // built-in types
  assert not (IsLeftZero.Invoke(Some 42))
  assert IsLeftZero.Invoke(None : int option)
  assert not (IsLeftZero.Invoke([4; 2]))
  assert IsLeftZero.Invoke([] : int list)
  assert not (IsLeftZero.Invoke([|"4"; "2"|]))
  assert IsLeftZero.Invoke([||] : string array)
  assert not (IsLeftZero.Invoke(Seq.singleton 42))
  assert IsLeftZero.Invoke(Seq.empty : int seq)
  assert not (IsLeftZero.Invoke(Ok 42 : Result<int, string>))
  assert IsLeftZero.Invoke(Error "msg" : Result<int, string>)
  assert not (IsLeftZero.Invoke(Choice1Of2 42 : Choice<int, string>))
  assert IsLeftZero.Invoke(Choice2Of2 "msg" : Choice<int, string>)

  // user-defined types
  assert not (IsLeftZero.Invoke(Wrapped 42))
  assert not (IsLeftZero.Invoke(Just 42))
  assert IsLeftZero.Invoke(Nothing : Maybe<int>)
  assert not (IsLeftZero.Invoke(Left 42 : Either<int, string>))
  assert IsLeftZero.Invoke(Right "msg" : Either<int, string>)

  // user-defined struct types
  assert not (IsLeftZero.Invoke(SWrapped 42))
  assert not (IsLeftZero.Invoke(SJust 42))
  assert IsLeftZero.Invoke(SNothing : SMaybe<int>)
  assert not (IsLeftZero.Invoke(SLeft 42 : SEither<int, string>))
  assert IsLeftZero.Invoke(SRight "msg" : SEither<int, string>)

let after = DateTime.Now
let total = (after - before).TotalMilliseconds

printfn "OK: %fms total, %fms avg" (total) (total / float N)

The result on my PC: 9ms to 12ms of overhead regardless of:

  • The number of N
  • The number of IsLeftZero.Invoke calls
  • The number of types used

UPDATE: tried adding struct types to see if boxing/unboxing would affect the performance, but it seems not (still constant ~10ms overhead).

@cannorin cannorin merged commit bffed59 into master Sep 15, 2022
gusty added a commit that referenced this pull request Nov 14, 2022
gusty added a commit that referenced this pull request Nov 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants