Skip to content

Inline results module - #16106

Merged
psfinaki merged 8 commits into
dotnet:mainfrom
bradcypert:main
Oct 20, 2023
Merged

Inline results module#16106
psfinaki merged 8 commits into
dotnet:mainfrom
bradcypert:main

Conversation

@bradcypert

Copy link
Copy Markdown
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
bradcypert requested a review from a team as a code ownerOctober 10, 2023 21:25
@bradcypert

Copy link
Copy Markdown
ContributorAuthor

@dotnet-policy-service agree

@smoothdeveloper

Copy link
Copy Markdown
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
Copy Markdown
ContributorAuthor

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
Copy Markdown
Member

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

@vzarytovskii

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 2 pipeline(s).

@bradcypert

Copy link
Copy Markdown
ContributorAuthor

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

vzarytovskii commented Oct 11, 2023

Copy link
Copy Markdown
Member

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
Copy Markdown
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
Copy Markdown
Contributor

I included the entire benchmarking code in my PRs.

@bradcypert

Copy link
Copy Markdown
ContributorAuthor
Benchmarking Code
openBenchmarkDotNet.AttributesopenBenchmarkDotNet.ConfigsmoduleInline =let inlinemap mapping result =match result with| Error e -> Error e
| Ok x -> Ok(mapping x)let inlinemapError mapping result =match result with| Error e -> Error(mapping e)| Ok x -> Ok x
let inlinebind binder result =match result with| Error e -> Error e
| Ok x -> binder x
let inlinedefaultValue value result =match result with| Error _-> value
| Ok v -> v
let inlinedefaultWith defThunk result =match result with| Error error -> defThunk error
| Ok v -> v
let inlinecount result =match result with| Error _->0| Ok _->1let inlinefold<'T,'Error,'State>folder (state:'State)(result:Result<'T,'Error>)=match result with| Error _-> state
| Ok x -> folder state x
let inlinefoldBack<'T,'Error,'State>folder (result:Result<'T,'Error>)(state:'State)=match result with| Error _-> state
| Ok x -> folder x state
let inlineexists predicate result =match result with| Error _->false| Ok x -> predicate x
let inlineforall predicate result =match result with| Error _->true| Ok x -> predicate x
let inlineiter action result =match result with| Error _->()| Ok x -> action x
let inlinetoArray result =match result with| Error _->[||]| Ok x ->[| x |]let inlinetoList result =match result with| Error _->[]| Ok x ->[ x ]let inlinetoOption result =match result with| Error _-> None
| Ok x -> Some x
let inlinetoValueOption result =match result with| Error _-> ValueNone
| Ok x -> ValueSome x
moduleInlineAndLambda =let inlinemap([<InlineIfLambda>]mapping)result =match result with| Error e -> Error e
| Ok x -> Ok(mapping x)let inlinemapError([<InlineIfLambda>]mapping)result =match result with| Error e -> Error(mapping e)| Ok x -> Ok x
let inlinebind([<InlineIfLambda>]binder)result =match result with| Error e -> Error e
| Ok x -> binder x
let inlinedefaultWith([<InlineIfLambda>]defThunk)result =match result with| Error error -> defThunk error
| Ok v -> v
let inlinefold<'T,'Error,'State>([<InlineIfLambda>]folder)(state:'State)(result:Result<'T,'Error>)=match result with| Error _-> state
| Ok x -> folder state x
let inlinefoldBack<'T,'Error,'State>([<InlineIfLambda>]folder)(result:Result<'T,'Error>)(state:'State)=match result with| Error _-> state
| Ok x -> folder x state
let inlineexists([<InlineIfLambda>]predicate)result =match result with| Error _->false| Ok x -> predicate x
let inlineforall([<InlineIfLambda>]predicate)result =match result with| Error _->true| Ok x -> predicate x
let inlineiter([<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 InlineIfLambdalet inliney()=if1/1=1then100/100else2/3[<MemoryDiagnoser>]typeCurrent()=letr= Ok (System.DateTime.Now.Day)lete= Error ()[<NoCompilerInlining>]letf=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>]typeInline()=letr= Ok (System.DateTime.Now.Day)lete= Error ()[<NoCompilerInlining>]letf=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>]typeInlineAndLambda()=letr= Ok (System.DateTime.Now.Day)lete= Error ()[<NoCompilerInlining>]letf=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
TypeMethodMeanErrorStdDevMedianGen0Allocated
CurrentDefaultWithSingletonError2.0235 ns0.0172 ns0.0161 ns2.0229 ns--
InlineDefaultWithSingletonError0.4335 ns0.0020 ns0.0018 ns0.4332 ns--
InlineAndLambdaDefaultWithSingletonError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentDefaultWithError8.2374 ns0.0581 ns0.0515 ns8.2229 ns0.003824 B
InlineDefaultWithError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaDefaultWithError0.0065 ns0.0043 ns0.0040 ns0.0046 ns--
CurrentMapSingletonOk4.4937 ns0.1136 ns0.1115 ns4.5018 ns--
InlineMapSingletonOk0.8768 ns0.0117 ns0.0103 ns0.8750 ns--
InlineAndLambdaMapSingletonOk0.8775 ns0.0213 ns0.0189 ns0.8708 ns--
CurrentMapSingletonError3.9444 ns0.0306 ns0.0286 ns3.9523 ns--
InlineMapSingletonError0.1617 ns0.0107 ns0.0100 ns0.1603 ns--
InlineAndLambdaMapSingletonError0.1499 ns0.0135 ns0.0126 ns0.1437 ns--
CurrentCountError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineCountError0.0045 ns0.0015 ns0.0014 ns0.0046 ns--
InlineAndLambdaBindWithOk0.8840 ns0.0053 ns0.0042 ns0.8860 ns--
CurrentCountOk0.1332 ns0.0037 ns0.0029 ns0.1342 ns--
InlineCountOk0.1186 ns0.0015 ns0.0013 ns0.1186 ns--
InlineAndLambdaMapOk0.9739 ns0.0033 ns0.0027 ns0.9733 ns--
CurrentMapOk13.9269 ns0.1200 ns0.1063 ns13.9216 ns0.003824 B
InlineMapOk0.9403 ns0.0225 ns0.0199 ns0.9352 ns--
InlineAndLambdaMapError0.1552 ns0.0135 ns0.0127 ns0.1467 ns--
CurrentMapError6.1748 ns0.0272 ns0.0255 ns6.1779 ns0.003824 B
InlineMapError0.1525 ns0.0105 ns0.0098 ns0.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

kerams commented Oct 12, 2023

Copy link
Copy Markdown
Contributor

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
Copy Markdown
ContributorAuthor

@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
openBenchmarkDotNet.AttributesopenBenchmarkDotNet.ConfigsmoduleInline =let inlinemap mapping result =match result with| Error e -> Error e
| Ok x -> Ok(mapping x)let inlinemapError mapping result =match result with| Error e -> Error(mapping e)| Ok x -> Ok x
let inlinebind binder result =match result with| Error e -> Error e
| Ok x -> binder x
let inlinedefaultValue value result =match result with| Error _-> value
| Ok v -> v
let inlinedefaultWith defThunk result =match result with| Error error -> defThunk error
| Ok v -> v
let inlinecount result =match result with| Error _->0| Ok _->1let inlinefold<'T,'Error,'State>folder (state:'State)(result:Result<'T,'Error>)=match result with| Error _-> state
| Ok x -> folder state x
let inlinefoldBack<'T,'Error,'State>folder (result:Result<'T,'Error>)(state:'State)=match result with| Error _-> state
| Ok x -> folder x state
let inlineexists predicate result =match result with| Error _->false| Ok x -> predicate x
let inlineforall predicate result =match result with| Error _->true| Ok x -> predicate x
let inlineiter action result =match result with| Error _->()| Ok x -> action x
let inlinetoArray result =match result with| Error _->[||]| Ok x ->[| x |]let inlinetoList result =match result with| Error _->[]| Ok x ->[ x ]let inlinetoOption result =match result with| Error _-> None
| Ok x -> Some x
let inlinetoValueOption result =match result with| Error _-> ValueNone
| Ok x -> ValueSome x
moduleInlineAndLambda =let inlinemap([<InlineIfLambda>]mapping)result =match result with| Error e -> Error e
| Ok x -> Ok(mapping x)let inlinemapError([<InlineIfLambda>]mapping)result =match result with| Error e -> Error(mapping e)| Ok x -> Ok x
let inlinebind([<InlineIfLambda>]binder)result =match result with| Error e -> Error e
| Ok x -> binder x
let inlinedefaultWith([<InlineIfLambda>]defThunk)result =match result with| Error error -> defThunk error
| Ok v -> v
let inlinefold<'T,'Error,'State>([<InlineIfLambda>]folder)(state:'State)(result:Result<'T,'Error>)=match result with| Error _-> state
| Ok x -> folder state x
let inlinefoldBack<'T,'Error,'State>([<InlineIfLambda>]folder)(result:Result<'T,'Error>)(state:'State)=match result with| Error _-> state
| Ok x -> folder x state
let inlineexists([<InlineIfLambda>]predicate)result =match result with| Error _->false| Ok x -> predicate x
let inlineforall([<InlineIfLambda>]predicate)result =match result with| Error _->true| Ok x -> predicate x
let inlineiter([<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 InlineIfLambdalet inliney()=if1/1=1then100/100else2/3[<MemoryDiagnoser>]typeCurrent()=letr= Result<int, unit>.Ok (System.DateTime.Now.Day)lete= Result<int, unit>.Error ()[<NoCompilerInlining>]letf=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>]typeInline()=letr= Result<int, unit>.Ok (System.DateTime.Now.Day)lete= Result<int, unit>.Error ()[<NoCompilerInlining>]letf=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>]typeInlineAndLambda()=letr= Result<int, unit>.Ok (System.DateTime.Now.Day)lete= Result<int, unit>.Error ()[<NoCompilerInlining>]letf=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

TypeMethodMeanErrorStdDevMedianGen0Allocated
CurrentDefaultWithSingletonError1.6953 ns0.0132 ns0.0117 ns1.6906 ns--
InlineDefaultWithSingletonError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaDefaultWithSingletonError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentDefaultWithError7.9019 ns0.0405 ns0.0338 ns7.8853 ns0.003824 B
InlineDefaultWithError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaDefaultWithError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentMapSingletonOk1.4592 ns0.0262 ns0.0245 ns1.4520 ns--
InlineMapSingletonOk0.0058 ns0.0108 ns0.0101 ns0.0000 ns--
InlineAndLambdaMapSingletonOk0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentMapSingletonError1.5383 ns0.0290 ns0.0257 ns1.5243 ns--
InlineMapSingletonError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaMapSingletonError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentBindWithError0.8131 ns0.0206 ns0.0193 ns0.8090 ns--
InlineBindWithError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaBindWithError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentBindWithOk1.6556 ns0.0294 ns0.0229 ns1.6664 ns--
InlineBindWithOk0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaBindWithOk0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentCountError0.0003 ns0.0006 ns0.0006 ns0.0000 ns--
InlineCountError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaMapOk0.0575 ns0.0013 ns0.0012 ns0.0571 ns--
CurrentCountOk0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineCountOk0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaMapError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentMapOk9.9199 ns0.0340 ns0.0284 ns9.9138 ns0.003824 B
InlineMapOk0.0004 ns0.0010 ns0.0009 ns0.0000 ns--
InlineAndLambdaFoldOk0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentMapError4.2032 ns0.0244 ns0.0228 ns4.2131 ns0.003824 B
InlineMapError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaFoldError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentFoldOk8.2265 ns0.0861 ns0.0805 ns8.1814 ns0.003824 B
InlineFoldOk0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaFoldBackOk0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentFoldError4.2379 ns0.0665 ns0.0556 ns4.2333 ns0.003824 B
InlineFoldError0.0002 ns0.0006 ns0.0006 ns0.0000 ns--
InlineAndLambdaFoldBackError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentFoldBackOk8.2017 ns0.0411 ns0.0343 ns8.1956 ns0.003824 B
InlineFoldBackOk0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaExistsOk0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentFoldBackError4.2486 ns0.0282 ns0.0235 ns4.2520 ns0.003824 B
InlineFoldBackError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaExistsError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentExistsOk8.8224 ns0.1152 ns0.1021 ns8.8128 ns0.003824 B
InlineExistsOk0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaForAllOk0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentExistsError4.1916 ns0.0478 ns0.0424 ns4.1692 ns0.003824 B
InlineExistsError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaForAllError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentForAllOk8.7139 ns0.0524 ns0.0490 ns8.7126 ns0.003824 B
InlineForAllOk0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaToIterError0.2744 ns0.0060 ns0.0056 ns0.2750 ns--
CurrentForAllError4.1518 ns0.0102 ns0.0085 ns4.1505 ns0.003824 B
InlineForAllError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
InlineAndLambdaToIterOk0.2683 ns0.0124 ns0.0116 ns0.2699 ns--
CurrentToIterError3.8850 ns0.0164 ns0.0137 ns3.8807 ns0.003824 B
InlineToIterError0.0566 ns0.0018 ns0.0017 ns0.0563 ns--
CurrentToIterOk7.7336 ns0.0106 ns0.0099 ns7.7328 ns0.003824 B
InlineToIterOk0.2751 ns0.0142 ns0.0133 ns0.2700 ns--
CurrentToArrayError0.3090 ns0.0246 ns0.0230 ns0.3015 ns--
InlineToArrayError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentToArrayOk3.0690 ns0.0191 ns0.0169 ns3.0659 ns0.005132 B
InlineToArrayOk2.6856 ns0.0056 ns0.0044 ns2.6858 ns0.005132 B
CurrentToListError0.3587 ns0.0026 ns0.0024 ns0.3589 ns--
InlineToListError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentToListOk5.2518 ns0.0254 ns0.0212 ns5.2452 ns0.005132 B
InlineToListOk4.6953 ns0.0083 ns0.0065 ns4.6957 ns0.005132 B
CurrentToOptionError0.3352 ns0.0191 ns0.0179 ns0.3331 ns--
InlineToOptionError0.0006 ns0.0008 ns0.0008 ns0.0003 ns--
CurrentToOptionOk2.8391 ns0.0076 ns0.0068 ns2.8370 ns0.003824 B
InlineToOptionOk2.4477 ns0.0207 ns0.0184 ns2.4563 ns0.003824 B
CurrentToValueOptionError0.1728 ns0.0075 ns0.0067 ns0.1722 ns--
InlineToValueOptionError0.0000 ns0.0000 ns0.0000 ns0.0000 ns--
CurrentToValueOptionOk0.3568 ns0.0134 ns0.0119 ns0.3517 ns--
InlineToValueOptionOk0.0134 ns0.0014 ns0.0013 ns0.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

bradcypert commented Oct 14, 2023

Copy link
Copy Markdown
ContributorAuthor

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
Copy Markdown
ContributorAuthor

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

@majochamajocha mentioned this pull request Nov 20, 2025
3 tasks
@keramskerams mentioned this pull request Dec 8, 2025
3 tasks
@TheAngryByrdTheAngryByrd mentioned this pull request Mar 18, 2026
3 tasks
@omajidomajid mentioned this pull request Apr 10, 2026
3 tasks
@Youssef1313Youssef1313 mentioned this pull request Apr 17, 2026
3 tasks
@majochamajocha mentioned this pull request May 25, 2026
3 tasks
@keramskerams mentioned this pull request May 30, 2026
3 tasks
@keramskerams mentioned this pull request Jun 21, 2026
3 tasks
Sign up for freeto 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

@bradcypert@smoothdeveloper@vzarytovskii@kerams@abonie@T-Gro@psfinaki