Uh oh!
There was an error while loading. Please reload this page.
Inline results module - #16106
Conversation
bradcypert
commented
Oct 10, 2023
@dotnet-policy-service agree |
smoothdeveloper
commented
Oct 10, 2023
@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 ( Hopefully someone else will confirm, the conservative approach is to stick to only those where you applied Thanks for joining the contributors to this repository! |
bradcypert
commented
Oct 10, 2023
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
commented
Oct 11, 2023
Would also like to see some beanchmark results, like it was done for Option change. |
vzarytovskii
commented
Oct 11, 2023
/azp run |
|
Azure Pipelines successfully started running 2 pipeline(s). |
bradcypert
commented
Oct 11, 2023
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? Sorry, I haven't ran DotNet benchmarks before so this is all a little new to me :) |
I think @kerams was testing changes for Option.
Alternatively, to have more precise results and run in "one go", the following can be done (more sophisticated):
|
vzarytovskii
commented
Oct 11, 2023
That would be the most straightforward way, I'd say. Have two copies - with and without changes. |
kerams
commented
Oct 11, 2023
I included the entire benchmarking code in my PRs. |
bradcypert
commented
Oct 12, 2023
Benchmarking CodeopenBenchmarkDotNet.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))|> ignoreI commented out a few of the benchmarks that I tried to run, but was receiving an error about benchmarking generics. Output
// * Warnings * // * Hints * // * Legends * // * Diagnostic Output - MemoryDiagnoser * // ***** BenchmarkRunner: End ***** I know I am missing a few different benchmarks but am currently blocked until I figure out the benchmarking with generics: 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. |
Instead of Very diligent of you to benchmark every single method:). Please rerun it all with .NET 8 rc 2 though. |
bradcypert
commented
Oct 14, 2023
@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 codeopenBenchmarkDotNet.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))|> ignoreBenchmark Results
// * Warnings * // * Hints * // * Legends * // * Diagnostic Output - MemoryDiagnoser * // ***** BenchmarkRunner: End ***** |
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
commented
Oct 16, 2023
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 |
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!