Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inline results module #16106

Merged
merged 8 commits into from
Oct 20, 2023
Merged

Inline results module #16106

merged 8 commits into from
Oct 20, 2023

Conversation

bradcypert
Copy link
Contributor

Inline the results module functions similar to the issues: #14927 and #15709.

New to contributing to F# so please let me know if I need to accept a CLA, need to run any specific tooling, or have just generally missed the mark with this one :)

Thanks and look forward to contributing to such an awesome language!

@bradcypert
Copy link
Contributor Author

@dotnet-policy-service agree

@smoothdeveloper
Copy link
Contributor

@bradcypert I'm not fully aware of the background, but it looks like the RFC is about functions that take a lambda and call it at most once.

There are other functions that seem to be adjusted to inline in your current PR (toArray, toList, etc.) and I think for those, the decision would be guided on some form of benchmarking and other considerations.

Hopefully someone else will confirm, the conservative approach is to stick to only those where you applied InlineIfLambda, making sure the lambda is called at most once.

Thanks for joining the contributors to this repository!

@bradcypert
Copy link
Contributor Author

I based my changes off the changes to the option type in a previous PR. It also involved making those other functions inline as well, but I'm happy to revert my changes for the functions such as ToArray, etc.

@vzarytovskii
Copy link
Member

Would also like to see some beanchmark results, like it was done for Option change.

@vzarytovskii
Copy link
Member

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 2 pipeline(s).

@bradcypert
Copy link
Contributor Author

I'd be happy to add some benchmarks, but I'll need to learn more about that process to successfully be able to do that.

Are benchmarks ran on a dedicated machine?
If not, should I just create a new fsharp project, move my code to a module for benchmarking and then call those functions using BenchmarkDotNet?

Sorry, I haven't ran DotNet benchmarks before so this is all a little new to me :)

@vzarytovskii
Copy link
Member

vzarytovskii commented Oct 11, 2023

I think @kerams was testing changes for Option.
I would assume it's as easy as:

  1. Create benchmark project
  2. Add <DisableImplicitFSharpCoreReference>true</DisableImplicitFSharpCoreReference> to the property group to the benchmark project.
  3. Reference FSharp.Core.fsproj
  4. Build with dotnet build -c Release ./FSharp.Compiler.Service.sln
  5. Run benchmark
  6. Make inline changes
  7. Repeat number 4
  8. Run benchmark

Alternatively, to have more precise results and run in "one go", the following can be done (more sophisticated):

  1. Extract result type and result module to the separate benchmark project
  2. Make N copies of the functions (with and without inline + with and without InlineIfLambda, depending what you're trying to test).
  3. Write and run benchmakrs for each one of them, BDN (benchmark.net) will output gihub markdown file with results.

@vzarytovskii
Copy link
Member

If not, should I just create a new fsharp project, move my code to a module for benchmarking and then call those functions using BenchmarkDotNet?

That would be the most straightforward way, I'd say. Have two copies - with and without changes.

@kerams
Copy link
Contributor

kerams commented Oct 11, 2023

I included the entire benchmarking code in my PRs.

@bradcypert
Copy link
Contributor Author

Benchmarking Code
open BenchmarkDotNet.Attributes
open BenchmarkDotNet.Configs

module Inline =
    let inline map mapping result =
        match result with
        | Error e -> Error e
        | Ok x -> Ok(mapping x)

    let inline mapError mapping result =
        match result with
        | Error e -> Error(mapping e)
        | Ok x -> Ok x

    let inline bind binder result =
        match result with
        | Error e -> Error e
        | Ok x -> binder x
    
    let inline defaultValue value result =
        match result with
        | Error _ -> value
        | Ok v -> v

    let inline defaultWith defThunk result =
        match result with
        | Error error -> defThunk error
        | Ok v -> v

    let inline count result =
        match result with
        | Error _ -> 0
        | Ok _ -> 1

    let inline fold<'T, 'Error, 'State> folder (state: 'State) (result: Result<'T, 'Error>) =
        match result with
        | Error _ -> state
        | Ok x -> folder state x

    let inline foldBack<'T, 'Error, 'State> folder (result: Result<'T, 'Error>) (state: 'State) =
        match result with
        | Error _ -> state
        | Ok x -> folder x state

    let inline exists predicate result =
        match result with
        | Error _ -> false
        | Ok x -> predicate x
    
    let inline forall predicate result =
        match result with
        | Error _ -> true
        | Ok x -> predicate x

    let inline iter action result =
        match result with
        | Error _ -> ()
        | Ok x -> action x

    let inline toArray result =
        match result with
        | Error _ -> [||]
        | Ok x -> [| x |]

    let inline toList result =
        match result with
        | Error _ -> []
        | Ok x -> [ x ]

    let inline toOption result =
        match result with
        | Error _ -> None
        | Ok x -> Some x

    let inline toValueOption result =
        match result with
        | Error _ -> ValueNone
        | Ok x -> ValueSome x

module InlineAndLambda =
    let inline map ([<InlineIfLambda>] mapping) result =
        match result with
        | Error e -> Error e
        | Ok x -> Ok(mapping x)

    let inline mapError ([<InlineIfLambda>] mapping) result =
        match result with
        | Error e -> Error(mapping e)
        | Ok x -> Ok x

    let inline bind ([<InlineIfLambda>] binder) result =
        match result with
        | Error e -> Error e
        | Ok x -> binder x

    let inline defaultWith ([<InlineIfLambda>] defThunk) result =
        match result with
        | Error error -> defThunk error
        | Ok v -> v

    let inline fold<'T, 'Error, 'State> ([<InlineIfLambda>] folder) (state: 'State) (result: Result<'T, 'Error>) =
        match result with
        | Error _ -> state
        | Ok x -> folder state x

    let inline foldBack<'T, 'Error, 'State> ([<InlineIfLambda>] folder) (result: Result<'T, 'Error>) (state: 'State) =
        match result with
        | Error _ -> state
        | Ok x -> folder x state

    let inline exists ([<InlineIfLambda>] predicate) result =
        match result with
        | Error _ -> false
        | Ok x -> predicate x
    
    let inline forall ([<InlineIfLambda>] predicate) result =
        match result with
        | Error _ -> true
        | Ok x -> predicate x

    let inline iter ([<InlineIfLambda>] action) result =
        match result with
        | Error _ -> ()
        | Ok x -> action x

// Some function with a bunch of instructions that isn't going to cause lambda inlining without InlineIfLambda
let inline y () =
    if 1 / 1 = 1 then
        100 / 100
    else
        2 / 3

[<MemoryDiagnoser>]
type Current () =

    let r = Ok (System.DateTime.Now.Day)

    let e = Error ()

    [<NoCompilerInlining>]
    let f = 10

    [<Benchmark>]
    member _.DefaultWithSingletonError() =
        Result.defaultWith (fun () -> 41 + y ()) e

    [<Benchmark>]
    member _.DefaultWithError() =
        Result.defaultWith (fun () -> 42 + f + y ()) e

    [<Benchmark>]
    member _.MapSingletonOk() =
        Result.map (fun x -> x + 43 + y ()) r

    [<Benchmark>]
    member _.MapSingletonError() =
        Result.map (fun x -> x + 44 + y ()) e

    // [<Benchmark>]
    // member _.BindWithError() =
    //     Result.bind (fun x -> Ok x) e

    // [<Benchmark>]
    // member _.BindWithOk() =
    //     Result.bind (fun x -> Ok x) r

    [<Benchmark>]
    member _.CountError() =
        Result.count e

    [<Benchmark>]
    member _.CountOk() =
        Result.count r

    [<Benchmark>]
    member _.MapOk() =
        Result.map (fun x -> x + f + y ()) r

    [<Benchmark>]
    member _.MapError() =
        Result.map (fun x -> x + f + y ()) e

    // [<Benchmark>]
    // member _.ToArrayError() =
    //     Result.toArray e
    
    // [<Benchmark>]
    // member _.ToArrayOk() =
    //     Result.toArray r

    // [<Benchmark>]
    // member _.ToListError() =
    //     Result.toList e
    
    // [<Benchmark>]
    // member _.ToListOk() =
    //     Result.toList r

    // [<Benchmark>]
    // member _.ToOptionError() =
    //     Result.toOption e
    
    // [<Benchmark>]
    // member _.ToOptionOk() =
    //     Result.toOption r

    // [<Benchmark>]
    // member _.ToValueOptionError() =
    //     Result.toValueOption e
    
    // [<Benchmark>]
    // member _.ToValueOptionOk() =
    //     Result.toValueOption r

[<MemoryDiagnoser>]
type Inline () =

    let r = Ok (System.DateTime.Now.Day)

    let e = Error ()

    [<NoCompilerInlining>]
    let f = 10

    [<Benchmark>]
    member _.DefaultWithSingletonError() =
        Inline.defaultWith (fun () -> 41 + y ()) e

    [<Benchmark>]
    member _.DefaultWithError() =
        Inline.defaultWith (fun () -> 42 + f + y ()) e

    [<Benchmark>]
    member _.MapSingletonOk() =
        Inline.map (fun x -> x + 43 + y ()) r

    [<Benchmark>]
    member _.MapSingletonError() =
        Inline.map (fun x -> x + 44 + y ()) e

    // [<Benchmark>]
    // member _.BindWithError() =
    //     Inline.bind (fun x -> Ok x) e

    // [<Benchmark>]
    // member _.BindWithOk() =
    //     Inline.bind (fun x -> Ok x) r

    [<Benchmark>]
    member _.CountError() =
        Inline.count e

    [<Benchmark>]
    member _.CountOk() =
        Inline.count r

    [<Benchmark>]
    member _.MapOk() =
        Inline.map (fun x -> x + f + y ()) r

    [<Benchmark>]
    member _.MapError() =
        Inline.map (fun x -> x + f + y ()) e

    // [<Benchmark>]
    // member _.ToArrayError() =
    //     Inline.toArray e
    
    // [<Benchmark>]
    // member _.ToArrayOk() =
    //     Inline.toArray r

    // [<Benchmark>]
    // member _.ToListError() =
    //     Inline.toList e
    
    // [<Benchmark>]
    // member _.ToListOk() =
    //     Inline.toList r

    // [<Benchmark>]
    // member _.ToOptionError() =
    //     Inline.toOption e
    
    // [<Benchmark>]
    // member _.ToOptionOk() =
    //     Inline.toOption r

    // [<Benchmark>]
    // member _.ToValueOptionError() =
    //     Inline.toValueOption e
    
    // [<Benchmark>]
    // member _.ToValueOptionOk() =
    //     Inline.toValueOption r

[<MemoryDiagnoser>]
type InlineAndLambda () =

    let r = Ok (System.DateTime.Now.Day)

    let e = Error ()

    [<NoCompilerInlining>]
    let f = 10

    [<Benchmark>]
    member _.DefaultWithSingletonError() =
        InlineAndLambda.defaultWith (fun () -> 41 + y ()) e

    [<Benchmark>]
    member _.DefaultWithError() =
        InlineAndLambda.defaultWith (fun () -> 42 + f + y ()) e

    [<Benchmark>]
    member _.MapSingletonOk() =
        InlineAndLambda.map (fun x -> x + 43 + y ()) r

    [<Benchmark>]
    member _.MapSingletonError() =
        InlineAndLambda.map (fun x -> x + 44 + y ()) e

    // [<Benchmark>]
    // member _.BindWithError() =
    //     InlineAndLambda.bind (fun x -> Ok x) e

    [<Benchmark>]
    member _.BindWithOk() =
        InlineAndLambda.bind (fun x -> Ok x) r

    [<Benchmark>]
    member _.MapOk() =
        InlineAndLambda.map (fun x -> x + f + y ()) r

    [<Benchmark>]
    member _.MapError() =
        InlineAndLambda.map (fun x -> x + f + y ()) e

BenchmarkDotNet.Running.BenchmarkRunner.Run (
    typeof<Current>.Assembly,
    DefaultConfig.Instance.WithOption (ConfigOptions.JoinSummary, true))
|> ignore

I commented out a few of the benchmarks that I tried to run, but was receiving an error about benchmarking generics.

Output
BenchmarkDotNet v0.13.9+228a464e8be6c580ad9408e98f18813f6407fb5a, macOS Ventura 13.4 (22F66) [Darwin 22.5.0]
Apple M1 2.40GHz, 1 CPU, 8 logical and 8 physical cores
.NET SDK 7.0.306
  [Host]     : .NET 7.0.9 (7.0.923.32018), Arm64 RyuJIT AdvSIMD DEBUG
  DefaultJob : .NET 7.0.9 (7.0.923.32018), Arm64 RyuJIT AdvSIMD
Type Method Mean Error StdDev Median Gen0 Allocated
Current DefaultWithSingletonError 2.0235 ns 0.0172 ns 0.0161 ns 2.0229 ns - -
Inline DefaultWithSingletonError 0.4335 ns 0.0020 ns 0.0018 ns 0.4332 ns - -
InlineAndLambda DefaultWithSingletonError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current DefaultWithError 8.2374 ns 0.0581 ns 0.0515 ns 8.2229 ns 0.0038 24 B
Inline DefaultWithError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda DefaultWithError 0.0065 ns 0.0043 ns 0.0040 ns 0.0046 ns - -
Current MapSingletonOk 4.4937 ns 0.1136 ns 0.1115 ns 4.5018 ns - -
Inline MapSingletonOk 0.8768 ns 0.0117 ns 0.0103 ns 0.8750 ns - -
InlineAndLambda MapSingletonOk 0.8775 ns 0.0213 ns 0.0189 ns 0.8708 ns - -
Current MapSingletonError 3.9444 ns 0.0306 ns 0.0286 ns 3.9523 ns - -
Inline MapSingletonError 0.1617 ns 0.0107 ns 0.0100 ns 0.1603 ns - -
InlineAndLambda MapSingletonError 0.1499 ns 0.0135 ns 0.0126 ns 0.1437 ns - -
Current CountError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Inline CountError 0.0045 ns 0.0015 ns 0.0014 ns 0.0046 ns - -
InlineAndLambda BindWithOk 0.8840 ns 0.0053 ns 0.0042 ns 0.8860 ns - -
Current CountOk 0.1332 ns 0.0037 ns 0.0029 ns 0.1342 ns - -
Inline CountOk 0.1186 ns 0.0015 ns 0.0013 ns 0.1186 ns - -
InlineAndLambda MapOk 0.9739 ns 0.0033 ns 0.0027 ns 0.9733 ns - -
Current MapOk 13.9269 ns 0.1200 ns 0.1063 ns 13.9216 ns 0.0038 24 B
Inline MapOk 0.9403 ns 0.0225 ns 0.0199 ns 0.9352 ns - -
InlineAndLambda MapError 0.1552 ns 0.0135 ns 0.0127 ns 0.1467 ns - -
Current MapError 6.1748 ns 0.0272 ns 0.0255 ns 6.1779 ns 0.0038 24 B
Inline MapError 0.1525 ns 0.0105 ns 0.0098 ns 0.1497 ns - -

// * Warnings *
MultimodalDistribution
InlineAndLambda.DefaultWithError: Default -> It seems that the distribution is bimodal (mValue = 3.25)
InlineAndLambda.MapSingletonError: Default -> It seems that the distribution can have several modes (mValue = 3)
InlineAndLambda.MapError: Default -> It seems that the distribution is bimodal (mValue = 3.33)
ZeroMeasurement
InlineAndLambda.DefaultWithSingletonError: Default -> The method duration is indistinguishable from the empty method duration
Inline.DefaultWithError: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.DefaultWithError: Default -> The method duration is indistinguishable from the empty method duration
Current.CountError: Default -> The method duration is indistinguishable from the empty method duration
Inline.CountError: Default -> The method duration is indistinguishable from the empty method duration

// * Hints *
Outliers
Inline.DefaultWithSingletonError: Default -> 1 outlier was removed (1.62 ns)
Current.DefaultWithError: Default -> 1 outlier was removed (9.69 ns)
Inline.MapSingletonOk: Default -> 1 outlier was removed (2.40 ns)
InlineAndLambda.MapSingletonOk: Default -> 1 outlier was removed (4.40 ns)
Current.CountError: Default -> 1 outlier was removed (1.23 ns)
InlineAndLambda.BindWithOk: Default -> 3 outliers were removed (2.34 ns..2.35 ns)
Current.CountOk: Default -> 3 outliers were removed, 4 outliers were detected (1.29 ns, 1.31 ns..1.33 ns)
Inline.CountOk: Default -> 1 outlier was removed (1.30 ns)
InlineAndLambda.MapOk: Default -> 2 outliers were removed (2.42 ns, 2.43 ns)
Current.MapOk: Default -> 1 outlier was removed (15.72 ns)
Inline.MapOk: Default -> 1 outlier was removed (2.55 ns)
Current.MapError: Default -> 2 outliers were detected (7.55 ns, 7.55 ns)

// * Legends *
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
Median : Value separating the higher half of all measurements (50th percentile)
Gen0 : GC Generation 0 collects per 1000 operations
Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
1 ns : 1 Nanosecond (0.000000001 sec)

// * Diagnostic Output - MemoryDiagnoser *

// ***** BenchmarkRunner: End *****
Global total time: 00:11:04 (664.12 sec), executed benchmarks: 23

I know I am missing a few different benchmarks but am currently blocked until I figure out the benchmarking with generics:

Benchmark method BindWithError is generic.
Generic Benchmark methods are not supported.

and I also wanted to get feedback to make sure the benchmarking is headed in the right direction. I will keep digging into the generics error message this weekend.

@kerams
Copy link
Contributor

kerams commented Oct 12, 2023

blocked until I figure out the benchmarking with generics

Instead of Ok x, do Result<int, unit>.Ok x, possibly also with some other generic type arguments, in order to test performance issues caused by struct (mis)alignment (Result is a struct) that I made a note of in the last PR.

Very diligent of you to benchmark every single method:). Please rerun it all with .NET 8 rc 2 though.

@bradcypert
Copy link
Contributor Author

@kerams Thank you for your guidance here! I was able to get that issue resolved and have updated the benchmark code and results and am sharing those below :)

Benchmark code
open BenchmarkDotNet.Attributes
open BenchmarkDotNet.Configs

module Inline =
    let inline map mapping result =
        match result with
        | Error e -> Error e
        | Ok x -> Ok(mapping x)

    let inline mapError mapping result =
        match result with
        | Error e -> Error(mapping e)
        | Ok x -> Ok x

    let inline bind binder result =
        match result with
        | Error e -> Error e
        | Ok x -> binder x
    
    let inline defaultValue value result =
        match result with
        | Error _ -> value
        | Ok v -> v

    let inline defaultWith defThunk result =
        match result with
        | Error error -> defThunk error
        | Ok v -> v

    let inline count result =
        match result with
        | Error _ -> 0
        | Ok _ -> 1

    let inline fold<'T, 'Error, 'State> folder (state: 'State) (result: Result<'T, 'Error>) =
        match result with
        | Error _ -> state
        | Ok x -> folder state x

    let inline foldBack<'T, 'Error, 'State> folder (result: Result<'T, 'Error>) (state: 'State) =
        match result with
        | Error _ -> state
        | Ok x -> folder x state

    let inline exists predicate result =
        match result with
        | Error _ -> false
        | Ok x -> predicate x
    
    let inline forall predicate result =
        match result with
        | Error _ -> true
        | Ok x -> predicate x

    let inline iter action result =
        match result with
        | Error _ -> ()
        | Ok x -> action x

    let inline toArray result =
        match result with
        | Error _ -> [||]
        | Ok x -> [| x |]

    let inline toList result =
        match result with
        | Error _ -> []
        | Ok x -> [ x ]

    let inline toOption result =
        match result with
        | Error _ -> None
        | Ok x -> Some x

    let inline toValueOption result =
        match result with
        | Error _ -> ValueNone
        | Ok x -> ValueSome x

module InlineAndLambda =
    let inline map ([<InlineIfLambda>] mapping) result =
        match result with
        | Error e -> Error e
        | Ok x -> Ok(mapping x)

    let inline mapError ([<InlineIfLambda>] mapping) result =
        match result with
        | Error e -> Error(mapping e)
        | Ok x -> Ok x

    let inline bind ([<InlineIfLambda>] binder) result =
        match result with
        | Error e -> Error e
        | Ok x -> binder x

    let inline defaultWith ([<InlineIfLambda>] defThunk) result =
        match result with
        | Error error -> defThunk error
        | Ok v -> v

    let inline fold<'T, 'Error, 'State> ([<InlineIfLambda>] folder) (state: 'State) (result: Result<'T, 'Error>) =
        match result with
        | Error _ -> state
        | Ok x -> folder state x

    let inline foldBack<'T, 'Error, 'State> ([<InlineIfLambda>] folder) (result: Result<'T, 'Error>) (state: 'State) =
        match result with
        | Error _ -> state
        | Ok x -> folder x state

    let inline exists ([<InlineIfLambda>] predicate) result =
        match result with
        | Error _ -> false
        | Ok x -> predicate x
    
    let inline forall ([<InlineIfLambda>] predicate) result =
        match result with
        | Error _ -> true
        | Ok x -> predicate x

    let inline iter ([<InlineIfLambda>] action) result =
        match result with
        | Error _ -> ()
        | Ok x -> action x

// Some function with a bunch of instructions that isn't going to cause lambda inlining without InlineIfLambda
let inline y () =
    if 1 / 1 = 1 then
        100 / 100
    else
        2 / 3

[<MemoryDiagnoser>]
type Current () =

    let r = Result<int, unit>.Ok (System.DateTime.Now.Day)

    let e = Result<int, unit>.Error ()

    [<NoCompilerInlining>]
    let f = 10

    [<Benchmark>]
    member _.DefaultWithSingletonError() =
        Result.defaultWith (fun () -> 41 + y ()) e

    [<Benchmark>]
    member _.DefaultWithError() =
        Result.defaultWith (fun () -> 42 + f + y ()) e

    [<Benchmark>]
    member _.MapSingletonOk() =
        Result.map (fun x -> x + 43 + y ()) r

    [<Benchmark>]
    member _.MapSingletonError() =
        Result.map (fun x -> x + 44 + y ()) e

    [<Benchmark>]
    member _.BindWithError() =
        Result.bind (fun x -> Ok x) e

    [<Benchmark>]
    member _.BindWithOk() =
        Result.bind (fun x -> Ok x) r

    [<Benchmark>]
    member _.CountError() =
        Result.count e

    [<Benchmark>]
    member _.CountOk() =
        Result.count r

    [<Benchmark>]
    member _.MapOk() =
        Result.map (fun x -> x + f + y ()) r

    [<Benchmark>]
    member _.MapError() =
        Result.map (fun x -> x + f + y ()) e

    [<Benchmark>]
    member _.FoldOk() =
        Result.fold (fun acc x -> acc + x + f + y ()) 0 r

    [<Benchmark>]
    member _.FoldError() =
        Result.fold (fun acc x -> acc + x + f + y ()) 0 e

    [<Benchmark>]
    member _.FoldBackOk() =
        Result.foldBack (fun x acc -> acc + x + f + y ()) r 0

    [<Benchmark>]
    member _.FoldBackError() =
        Result.foldBack (fun x acc -> acc + x + f + y ()) e 0

    [<Benchmark>]
    member _.ExistsOk() =
        Result.exists (fun x -> x + f + y () > 5) r

    [<Benchmark>]
    member _.ExistsError() =
        Result.exists (fun x -> x + f + y () > 5) e

    [<Benchmark>]
    member _.ForAllOk() =
        Result.forall (fun x -> x + f + y () > 5) r

    [<Benchmark>]
    member _.ForAllError() =
        Result.forall (fun x -> x + f + y () > 5) e

    [<Benchmark>]
    member _.ToIterError() =
        Result.iter (fun x -> x + f + y () > 5 |> ignore) e
    
    [<Benchmark>]
    member _.ToIterOk() =
        Result.iter (fun x -> x + f + y () > 5 |> ignore) r

    [<Benchmark>]
    member _.ToArrayError() =
        Result.toArray e
    
    [<Benchmark>]
    member _.ToArrayOk() =
        Result.toArray r

    [<Benchmark>]
    member _.ToListError() =
        Result.toList e
    
    [<Benchmark>]
    member _.ToListOk() =
        Result.toList r

    [<Benchmark>]
    member _.ToOptionError() =
        Result.toOption e
    
    [<Benchmark>]
    member _.ToOptionOk() =
        Result.toOption r

    [<Benchmark>]
    member _.ToValueOptionError() =
        Result.toValueOption e
    
    [<Benchmark>]
    member _.ToValueOptionOk() =
        Result.toValueOption r

[<MemoryDiagnoser>]
type Inline () =

    let r = Result<int, unit>.Ok (System.DateTime.Now.Day)

    let e = Result<int, unit>.Error ()

    [<NoCompilerInlining>]
    let f = 10

    [<Benchmark>]
    member _.DefaultWithSingletonError() =
        Inline.defaultWith (fun () -> 41 + y ()) e

    [<Benchmark>]
    member _.DefaultWithError() =
        Inline.defaultWith (fun () -> 42 + f + y ()) e

    [<Benchmark>]
    member _.MapSingletonOk() =
        Inline.map (fun x -> x + 43 + y ()) r

    [<Benchmark>]
    member _.MapSingletonError() =
        Inline.map (fun x -> x + 44 + y ()) e

    [<Benchmark>]
    member _.BindWithError() =
        Inline.bind (fun x -> Ok x) e

    [<Benchmark>]
    member _.BindWithOk() =
        Inline.bind (fun x -> Ok x) r

    [<Benchmark>]
    member _.CountError() =
        Inline.count e

    [<Benchmark>]
    member _.CountOk() =
        Inline.count r

    [<Benchmark>]
    member _.MapOk() =
        Inline.map (fun x -> x + f + y ()) r

    [<Benchmark>]
    member _.MapError() =
        Inline.map (fun x -> x + f + y ()) e

    [<Benchmark>]
    member _.FoldOk() =
        Inline.fold (fun acc x -> acc + x + f + y ()) 0 r

    [<Benchmark>]
    member _.FoldError() =
        Inline.fold (fun acc x -> acc + x + f + y ()) 0 e

    [<Benchmark>]
    member _.FoldBackOk() =
        Inline.foldBack (fun x acc -> acc + x + f + y ()) r 0

    [<Benchmark>]
    member _.FoldBackError() =
        Inline.foldBack (fun x acc -> acc + x + f + y ()) e 0

    [<Benchmark>]
    member _.ExistsOk() =
        Inline.exists (fun x -> x + f + y () > 5) r

    [<Benchmark>]
    member _.ExistsError() =
        Inline.exists (fun x -> x + f + y () > 5) e

    [<Benchmark>]
    member _.ForAllOk() =
        Inline.forall (fun x -> x + f + y () > 5) r

    [<Benchmark>]
    member _.ForAllError() =
        Inline.forall (fun x -> x + f + y () > 5) e

    [<Benchmark>]
    member _.ToIterError() =
        Inline.iter (fun x -> x + f + y () > 5 |> ignore) e
    
    [<Benchmark>]
    member _.ToIterOk() =
        Inline.iter (fun x -> x + f + y () > 5 |> ignore) r

    [<Benchmark>]
    member _.ToArrayError() =
        Inline.toArray e
    
    [<Benchmark>]
    member _.ToArrayOk() =
        Inline.toArray r

    [<Benchmark>]
    member _.ToListError() =
        Inline.toList e
    
    [<Benchmark>]
    member _.ToListOk() =
        Inline.toList r

    [<Benchmark>]
    member _.ToOptionError() =
        Inline.toOption e
    
    [<Benchmark>]
    member _.ToOptionOk() =
        Inline.toOption r

    [<Benchmark>]
    member _.ToValueOptionError() =
        Inline.toValueOption e
    
    [<Benchmark>]
    member _.ToValueOptionOk() =
        Inline.toValueOption r

[<MemoryDiagnoser>]
type InlineAndLambda () =

    let r = Result<int, unit>.Ok (System.DateTime.Now.Day)

    let e = Result<int, unit>.Error ()

    [<NoCompilerInlining>]
    let f = 10

    [<Benchmark>]
    member _.DefaultWithSingletonError() =
        InlineAndLambda.defaultWith (fun () -> 41 + y ()) e

    [<Benchmark>]
    member _.DefaultWithError() =
        InlineAndLambda.defaultWith (fun () -> 42 + f + y ()) e

    [<Benchmark>]
    member _.MapSingletonOk() =
        InlineAndLambda.map (fun x -> x + 43 + y ()) r

    [<Benchmark>]
    member _.MapSingletonError() =
        InlineAndLambda.map (fun x -> x + 44 + y ()) e

    [<Benchmark>]
    member _.BindWithError() =
        InlineAndLambda.bind (fun x -> Ok x) e

    [<Benchmark>]
    member _.BindWithOk() =
        InlineAndLambda.bind (fun x -> Ok x) r

    [<Benchmark>]
    member _.MapOk() =
        InlineAndLambda.map (fun x -> x + f + y ()) r

    [<Benchmark>]
    member _.MapError() =
        InlineAndLambda.map (fun x -> x + f + y ()) e

    [<Benchmark>]
    member _.FoldOk() =
        InlineAndLambda.fold (fun acc x -> acc + x + f + y ()) 0 r

    [<Benchmark>]
    member _.FoldError() =
        InlineAndLambda.fold (fun acc x -> acc + x + f + y ()) 0 e

    [<Benchmark>]
    member _.FoldBackOk() =
        InlineAndLambda.foldBack (fun x acc -> acc + x + f + y ()) r 0

    [<Benchmark>]
    member _.FoldBackError() =
        InlineAndLambda.foldBack (fun x acc -> acc + x + f + y ()) e 0

    [<Benchmark>]
    member _.ExistsOk() =
        InlineAndLambda.exists (fun x -> x + f + y () > 5) r

    [<Benchmark>]
    member _.ExistsError() =
        InlineAndLambda.exists (fun x -> x + f + y () > 5) e

    [<Benchmark>]
    member _.ForAllOk() =
        InlineAndLambda.forall (fun x -> x + f + y () > 5) r

    [<Benchmark>]
    member _.ForAllError() =
        InlineAndLambda.forall (fun x -> x + f + y () > 5) e

    [<Benchmark>]
    member _.ToIterError() =
        InlineAndLambda.iter (fun x -> x + f + y () > 5 |> ignore) e
    
    [<Benchmark>]
    member _.ToIterOk() =
        InlineAndLambda.iter (fun x -> x + f + y () > 5 |> ignore) r

BenchmarkDotNet.Running.BenchmarkRunner.Run (
    typeof<Current>.Assembly,
    DefaultConfig.Instance.WithOption (ConfigOptions.JoinSummary, true))
|> ignore
Benchmark Results

BenchmarkDotNet v0.13.9+228a464e8be6c580ad9408e98f18813f6407fb5a, macOS Ventura 13.4 (22F66) [Darwin 22.5.0]
Apple M1 2.40GHz, 1 CPU, 8 logical and 8 physical cores
.NET SDK 8.0.100-rc.2.23502.2
[Host] : .NET 8.0.0 (8.0.23.47906), Arm64 RyuJIT AdvSIMD DEBUG
DefaultJob : .NET 8.0.0 (8.0.23.47906), Arm64 RyuJIT AdvSIMD

Type Method Mean Error StdDev Median Gen0 Allocated
Current DefaultWithSingletonError 1.6953 ns 0.0132 ns 0.0117 ns 1.6906 ns - -
Inline DefaultWithSingletonError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda DefaultWithSingletonError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current DefaultWithError 7.9019 ns 0.0405 ns 0.0338 ns 7.8853 ns 0.0038 24 B
Inline DefaultWithError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda DefaultWithError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current MapSingletonOk 1.4592 ns 0.0262 ns 0.0245 ns 1.4520 ns - -
Inline MapSingletonOk 0.0058 ns 0.0108 ns 0.0101 ns 0.0000 ns - -
InlineAndLambda MapSingletonOk 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current MapSingletonError 1.5383 ns 0.0290 ns 0.0257 ns 1.5243 ns - -
Inline MapSingletonError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda MapSingletonError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current BindWithError 0.8131 ns 0.0206 ns 0.0193 ns 0.8090 ns - -
Inline BindWithError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda BindWithError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current BindWithOk 1.6556 ns 0.0294 ns 0.0229 ns 1.6664 ns - -
Inline BindWithOk 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda BindWithOk 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current CountError 0.0003 ns 0.0006 ns 0.0006 ns 0.0000 ns - -
Inline CountError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda MapOk 0.0575 ns 0.0013 ns 0.0012 ns 0.0571 ns - -
Current CountOk 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Inline CountOk 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda MapError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current MapOk 9.9199 ns 0.0340 ns 0.0284 ns 9.9138 ns 0.0038 24 B
Inline MapOk 0.0004 ns 0.0010 ns 0.0009 ns 0.0000 ns - -
InlineAndLambda FoldOk 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current MapError 4.2032 ns 0.0244 ns 0.0228 ns 4.2131 ns 0.0038 24 B
Inline MapError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda FoldError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current FoldOk 8.2265 ns 0.0861 ns 0.0805 ns 8.1814 ns 0.0038 24 B
Inline FoldOk 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda FoldBackOk 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current FoldError 4.2379 ns 0.0665 ns 0.0556 ns 4.2333 ns 0.0038 24 B
Inline FoldError 0.0002 ns 0.0006 ns 0.0006 ns 0.0000 ns - -
InlineAndLambda FoldBackError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current FoldBackOk 8.2017 ns 0.0411 ns 0.0343 ns 8.1956 ns 0.0038 24 B
Inline FoldBackOk 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda ExistsOk 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current FoldBackError 4.2486 ns 0.0282 ns 0.0235 ns 4.2520 ns 0.0038 24 B
Inline FoldBackError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda ExistsError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current ExistsOk 8.8224 ns 0.1152 ns 0.1021 ns 8.8128 ns 0.0038 24 B
Inline ExistsOk 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda ForAllOk 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current ExistsError 4.1916 ns 0.0478 ns 0.0424 ns 4.1692 ns 0.0038 24 B
Inline ExistsError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda ForAllError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current ForAllOk 8.7139 ns 0.0524 ns 0.0490 ns 8.7126 ns 0.0038 24 B
Inline ForAllOk 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda ToIterError 0.2744 ns 0.0060 ns 0.0056 ns 0.2750 ns - -
Current ForAllError 4.1518 ns 0.0102 ns 0.0085 ns 4.1505 ns 0.0038 24 B
Inline ForAllError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
InlineAndLambda ToIterOk 0.2683 ns 0.0124 ns 0.0116 ns 0.2699 ns - -
Current ToIterError 3.8850 ns 0.0164 ns 0.0137 ns 3.8807 ns 0.0038 24 B
Inline ToIterError 0.0566 ns 0.0018 ns 0.0017 ns 0.0563 ns - -
Current ToIterOk 7.7336 ns 0.0106 ns 0.0099 ns 7.7328 ns 0.0038 24 B
Inline ToIterOk 0.2751 ns 0.0142 ns 0.0133 ns 0.2700 ns - -
Current ToArrayError 0.3090 ns 0.0246 ns 0.0230 ns 0.3015 ns - -
Inline ToArrayError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current ToArrayOk 3.0690 ns 0.0191 ns 0.0169 ns 3.0659 ns 0.0051 32 B
Inline ToArrayOk 2.6856 ns 0.0056 ns 0.0044 ns 2.6858 ns 0.0051 32 B
Current ToListError 0.3587 ns 0.0026 ns 0.0024 ns 0.3589 ns - -
Inline ToListError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current ToListOk 5.2518 ns 0.0254 ns 0.0212 ns 5.2452 ns 0.0051 32 B
Inline ToListOk 4.6953 ns 0.0083 ns 0.0065 ns 4.6957 ns 0.0051 32 B
Current ToOptionError 0.3352 ns 0.0191 ns 0.0179 ns 0.3331 ns - -
Inline ToOptionError 0.0006 ns 0.0008 ns 0.0008 ns 0.0003 ns - -
Current ToOptionOk 2.8391 ns 0.0076 ns 0.0068 ns 2.8370 ns 0.0038 24 B
Inline ToOptionOk 2.4477 ns 0.0207 ns 0.0184 ns 2.4563 ns 0.0038 24 B
Current ToValueOptionError 0.1728 ns 0.0075 ns 0.0067 ns 0.1722 ns - -
Inline ToValueOptionError 0.0000 ns 0.0000 ns 0.0000 ns 0.0000 ns - -
Current ToValueOptionOk 0.3568 ns 0.0134 ns 0.0119 ns 0.3517 ns - -
Inline ToValueOptionOk 0.0134 ns 0.0014 ns 0.0013 ns 0.0132 ns - -

// * Warnings *
MultimodalDistribution
InlineAndLambda.ToIterOk: Default -> It seems that the distribution is bimodal (mValue = 3.25)
Inline.ToIterOk: Default -> It seems that the distribution is bimodal (mValue = 3.43)
Current.ToArrayError: Default -> It seems that the distribution is bimodal (mValue = 3.75)
ZeroMeasurement
Inline.DefaultWithSingletonError: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.DefaultWithSingletonError: Default -> The method duration is indistinguishable from the empty method duration
Inline.DefaultWithError: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.DefaultWithError: Default -> The method duration is indistinguishable from the empty method duration
Inline.MapSingletonOk: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.MapSingletonOk: Default -> The method duration is indistinguishable from the empty method duration
Inline.MapSingletonError: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.MapSingletonError: Default -> The method duration is indistinguishable from the empty method duration
Inline.BindWithError: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.BindWithError: Default -> The method duration is indistinguishable from the empty method duration
Inline.BindWithOk: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.BindWithOk: Default -> The method duration is indistinguishable from the empty method duration
Current.CountError: Default -> The method duration is indistinguishable from the empty method duration
Inline.CountError: Default -> The method duration is indistinguishable from the empty method duration
Current.CountOk: Default -> The method duration is indistinguishable from the empty method duration
Inline.CountOk: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.MapError: Default -> The method duration is indistinguishable from the empty method duration
Inline.MapOk: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.FoldOk: Default -> The method duration is indistinguishable from the empty method duration
Inline.MapError: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.FoldError: Default -> The method duration is indistinguishable from the empty method duration
Inline.FoldOk: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.FoldBackOk: Default -> The method duration is indistinguishable from the empty method duration
Inline.FoldError: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.FoldBackError: Default -> The method duration is indistinguishable from the empty method duration
Inline.FoldBackOk: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.ExistsOk: Default -> The method duration is indistinguishable from the empty method duration
Inline.FoldBackError: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.ExistsError: Default -> The method duration is indistinguishable from the empty method duration
Inline.ExistsOk: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.ForAllOk: Default -> The method duration is indistinguishable from the empty method duration
Inline.ExistsError: Default -> The method duration is indistinguishable from the empty method duration
InlineAndLambda.ForAllError: Default -> The method duration is indistinguishable from the empty method duration
Inline.ForAllOk: Default -> The method duration is indistinguishable from the empty method duration
Inline.ForAllError: Default -> The method duration is indistinguishable from the empty method duration
Inline.ToArrayError: Default -> The method duration is indistinguishable from the empty method duration
Inline.ToListError: Default -> The method duration is indistinguishable from the empty method duration
Inline.ToOptionError: Default -> The method duration is indistinguishable from the empty method duration
Inline.ToValueOptionError: Default -> The method duration is indistinguishable from the empty method duration
Inline.ToValueOptionOk: Default -> The method duration is indistinguishable from the empty method duration

// * Hints *
Outliers
Current.DefaultWithSingletonError: Default -> 1 outlier was removed (2.90 ns)
Current.DefaultWithError: Default -> 2 outliers were removed (9.11 ns, 9.17 ns)
Inline.DefaultWithError: Default -> 1 outlier was removed (1.01 ns)
InlineAndLambda.DefaultWithError: Default -> 1 outlier was removed (1.00 ns)
Current.MapSingletonError: Default -> 1 outlier was removed (3.12 ns)
Inline.MapSingletonError: Default -> 3 outliers were removed (1.43 ns..1.67 ns)
Inline.BindWithError: Default -> 2 outliers were detected (1.41 ns, 1.41 ns)
Current.BindWithOk: Default -> 3 outliers were removed, 5 outliers were detected (3.04 ns, 3.04 ns, 3.95 ns..6.15 ns)
Inline.BindWithOk: Default -> 2 outliers were removed (1.33 ns, 1.34 ns)
InlineAndLambda.BindWithOk: Default -> 2 outliers were removed, 3 outliers were detected (1.29 ns, 1.29 ns, 1.29 ns)
Inline.CountError: Default -> 3 outliers were removed (1.08 ns..1.11 ns)
InlineAndLambda.MapOk: Default -> 1 outlier was removed (1.50 ns)
Current.CountOk: Default -> 3 outliers were removed (1.09 ns..1.10 ns)
Inline.CountOk: Default -> 1 outlier was removed, 2 outliers were detected (1.08 ns, 1.08 ns)
Current.MapOk: Default -> 2 outliers were removed (11.46 ns, 11.91 ns)
InlineAndLambda.FoldOk: Default -> 2 outliers were removed (1.00 ns, 1.01 ns)
Inline.MapError: Default -> 3 outliers were removed (1.46 ns..1.49 ns)
Inline.FoldOk: Default -> 3 outliers were removed (0.99 ns..1.01 ns)
Current.FoldError: Default -> 2 outliers were removed (5.86 ns, 6.62 ns)
InlineAndLambda.FoldBackError: Default -> 2 outliers were removed (1.09 ns, 1.09 ns)
Current.FoldBackOk: Default -> 2 outliers were removed (9.44 ns, 9.50 ns)
Inline.FoldBackOk: Default -> 2 outliers were removed (1.10 ns, 1.22 ns)
InlineAndLambda.ExistsOk: Default -> 2 outliers were removed (1.00 ns, 1.03 ns)
Current.FoldBackError: Default -> 2 outliers were removed, 3 outliers were detected (5.29 ns, 5.44 ns, 5.59 ns)
Inline.FoldBackError: Default -> 3 outliers were removed (1.10 ns..1.11 ns)
Current.ExistsOk: Default -> 1 outlier was removed (10.53 ns)
Inline.ExistsOk: Default -> 1 outlier was removed (0.98 ns)
InlineAndLambda.ForAllOk: Default -> 2 outliers were removed (1.00 ns, 1.00 ns)
Current.ExistsError: Default -> 1 outlier was removed (5.51 ns)
InlineAndLambda.ForAllError: Default -> 1 outlier was removed (1.10 ns)
Current.ForAllError: Default -> 2 outliers were removed (5.28 ns, 5.31 ns)
Inline.ForAllError: Default -> 2 outliers were removed (1.10 ns, 1.11 ns)
Current.ToIterError: Default -> 2 outliers were removed (5.02 ns, 5.03 ns)
Inline.ToArrayError: Default -> 3 outliers were removed (2.27 ns..2.30 ns)
Current.ToArrayOk: Default -> 1 outlier was removed (5.39 ns)
Inline.ToArrayOk: Default -> 3 outliers were removed (4.98 ns..4.98 ns)
Current.ToListOk: Default -> 2 outliers were removed (7.66 ns, 7.84 ns)
Inline.ToListOk: Default -> 3 outliers were removed, 4 outliers were detected (6.95 ns, 7.01 ns..16.70 ns)
Current.ToOptionOk: Default -> 1 outlier was removed (5.11 ns)
Inline.ToOptionOk: Default -> 1 outlier was removed, 4 outliers were detected (4.66 ns..4.68 ns, 4.74 ns)
Current.ToValueOptionError: Default -> 1 outlier was removed (1.62 ns)
Current.ToValueOptionOk: Default -> 1 outlier was removed (1.83 ns)

// * Legends *
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
Median : Value separating the higher half of all measurements (50th percentile)
Gen0 : GC Generation 0 collects per 1000 operations
Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
1 ns : 1 Nanosecond (0.000000001 sec)

// * Diagnostic Output - MemoryDiagnoser *

// ***** BenchmarkRunner: End *****
Global total time: 00:36:45 (2205.61 sec), executed benchmarks: 74
// * Artifacts cleanup *
Artifacts cleanup is finished

@bradcypert
Copy link
Contributor Author

bradcypert commented Oct 14, 2023

I am curious about the warnings and errors from my benchmarking. I will run them again but I noticed several issues that look like they resulted in 0ms benchmarks. I'll also do some digging into that error message and BenchmarkDotNet

@bradcypert
Copy link
Contributor Author

I could be wrong, but it looks like those "errors" are from a benchmark that spends less an a CPU cycle on the operation. dotnet/BenchmarkDotNet#1167

@psfinaki psfinaki merged commit 12c2a40 into dotnet:main Oct 20, 2023
24 checks passed
auduchinok pushed a commit to auduchinok/fsharp that referenced this pull request Oct 23, 2023
* Inline results module

* adjust fsi file too

---------

Co-authored-by: Vlad Zarytovskii <[email protected]>
Co-authored-by: Tomas Grosup <[email protected]>
This was referenced Jun 29, 2024
@KevinRansom KevinRansom mentioned this pull request Aug 6, 2024
@jsoref jsoref mentioned this pull request Aug 21, 2024
3 tasks
@KevinRansom KevinRansom mentioned this pull request Sep 9, 2024
3 tasks
@mmitche mmitche mentioned this pull request Sep 11, 2024
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

7 participants