-
Notifications
You must be signed in to change notification settings - Fork 65
Benchmarks for Alternative Implementations, Inline, and InlineIfLambda #166
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
Conversation
Benchmarks explorationTypes of Implementations:
Variations for each:
I've chosen a few functions, specifically And as I've benchmarked and done deep analysis, I've notices the composition operator
Composition doesn't apply the
I'm going to show the benchmarks first but then explore each of these in detail below by showing the F# Code, the decompiled code in C# and any interesting findings. I've included a SharpLab link for each so you can see the code for yourself. Benchmark ResultsEitherMap
Map2
Result Normal EitherMapThis is base case implementation. It doesn't try to do anything fancy, in fact it tries to re-use existing functions so the implmentations don't vary. However as we can see, this seems to be leading to some performance issues. Interesting Findings (rnem)I do find the C# decomplilation of the Result Normal No Composition EitherMapThis is removing the Interesting Findings (rnncem)Nothing really different from the Normal implementation. Result Normal Inline EitherMapThis is only adding the Interesting Findings (rniem)
Result Normal Inline No Composition EitherMapThis is adding the Interesting findings (rnincem)While there's no noticible performance gains, there's quite a bit less C# code by comparison. Result Normal InlineIfLambda EitherMapThis is adding the Interesting findings (rniilem)No performance differences from Result Normal InlineIfLambda No Composition EitherMapThis is adding the Interesting findings (rniilncem)Again no noticible perfomance differences, however the C# code is greatly reduced and very readable compared to any of the previous variations. It doesn't seem to be generating classes and other static methods. Result Alt EitherMapInstead of writing this in terms of the Interesting findings (raem)Again no noticible perfomance differences, but is making classes again. Result Alt Inline EitherMapSame with the previous variation except it also includes the Interesting findings (raiem)Again no noticible perfomance differences, but with like the [Result Normal InlineIfLambda NoComposition EitherMap], it produces simple C# code. Result Alt InlineIfLambda EitherMapSame with the previous variation except it also includes the Interesting findings (raiilem)Again no noticible perfomance differences, but with like the [Result Normal InlineIfLambda NoComposition EitherMap], it produces simple C# code. Result Normal Map2Interesting findings (rnm2)As expected, a lot of code and a slow benchmark Result Normal No Composition Map2Interesting findings (rnncm2)As expected, a lot of code and a slow benchmark Result Normal Inline Map2Interesting findings (rnim2)While it performed better, the code generated is even more than the non inlined versions Result Normal Inline No Composition Map2Interesting findings (rnincm2)Still quite a bit of code generated Result Normal InlineIfLambda Map2Interesting findings (rniilm2)Faster benchmarks again, Example_Map2 is pretty small in terms of code generation Result Normal InlineIfLambda No Composition Map2Interesting findings (rniifncm2)No change in benchmarks, nor much change in code generation Result Alt Map2Interesting findings (ram2)Benchmarks are improved, as the code genratred is much smaller Result Alt Inline Map2Interesting findings (raim2)Benchmarks are improved, as the code genratred is much smaller Result Alt InlineIfLambda Map2Interesting findings (raiilm2)No improvements to benchmarks or code generation |
Given the |
After reading the docs on InlineIfLambda
Turning this on for the benchmarks.fsproj gives quite a few warnings. Going to keep digging at how to use this properly. |
let inline map ([<InlineIfLambda>] mapper : 'ok -> 'ok2) value =
match value with
| Ok x -> Ok(mapper x)
| Error e -> Error e
let inline bind ([<InlineIfLambda>] binder: 'ok -> Result<'ok2, 'err>) value =
match value with
| Ok x -> binder x
| Error e -> Error e
Does this mean these implementations can't take advantage of this attribute? cc @baronfel @dsyme @mrange |
@TheAngryByrd TBH I haven't looked closely on this but my guess is that you get this error message when you pass a function value that is not a lambda. Something like this: bind x.MyBind v
AFAIK if you provide a lambda it shouldn't give you the warning but I haven't tried. bind (fun x -> doSomeStuff x) v The warning seems to be generated here: PS. the performance results looks promising AFAICT. |
[<MemoryDiagnoser>]
type MapBenchmarks () =
[<Benchmark(Baseline = true)>]
member this.Result_Normal_Map () =
Result.map (fun x -> x + 2 ) (Ok 1) : Result<_,int>
[<Benchmark>]
member this.Result_Alt_InlinedLambda_Map () =
Result.Alt.InlinedLambda.map (fun x -> x + 2 ) (Ok 1) : Result<_,int>
[<MemoryDiagnoser>]
type BindBenchmarks () =
[<Benchmark(Baseline = true)>]
member this.Result_Normal_Bind () =
Result.bind (fun x -> Ok(x + 2) ) (Ok 1) : Result<int,int>
[<Benchmark>]
member this.Result_Alt_InlinedLambda_Bind () =
Result.Alt.InlinedLambda.bind (fun x -> Ok(x + 2) ) (Ok 1) : Result<int,int> This is how I'm using it currently, which is using a lambda which is why I'm a bit confused 😕 |
Now it really seems to shine in Computation Expressions.
NormalInlineInlineIfLambdaInteresting findingsThe |
6148f7e
to
d1ec032
Compare
d1ec032
to
e68bb0f
Compare
This goes to show how big of a deal Really nice work here. |
3c26ad5
to
d0ab342
Compare
42a7e5e
to
96ea76d
Compare
- [Moves many functions to inline with InlineIfLambda for performance](#166) Credits [@TheAngryByrd](https://github.com/TheAngryByrd)
- [Moves many functions to inline with InlineIfLambda for performance](#166) Credits [@TheAngryByrd](https://github.com/TheAngryByrd)
- [Moves many functions to inline with InlineIfLambda for performance](#166) Credits [@TheAngryByrd](https://github.com/TheAngryByrd) - [Native Tasks for TaskResult, TaskOption, and TaskResultOption](#169) Credits [@TheAngryByrd](https://github.com/TheAngryByrd)
- [Moves many functions to inline with InlineIfLambda for performance](#166) Credits [@TheAngryByrd](https://github.com/TheAngryByrd) - [Native Tasks for TaskResult, TaskOption, and TaskResultOption](#169) Credits [@TheAngryByrd](https://github.com/TheAngryByrd) - [Add explicit type parameters to ignore functions](#174) Credits [@cmeeren](https://github.com/cmeeren)
- [Moves many functions to inline with InlineIfLambda for performance](#166) Credits [@TheAngryByrd](https://github.com/TheAngryByrd) - [Native Tasks for TaskResult, TaskOption, and TaskResultOption](#169) Credits [@TheAngryByrd](https://github.com/TheAngryByrd) - [Add explicit type parameters to ignore functions](#174) Credits [@cmeeren](https://github.com/cmeeren) - [Adds CancellableTaskResult](#172) Credits [@TheAngryByrd](https://github.com/TheAngryByrd)
- [Moves many functions to inline with InlineIfLambda for performance](#166) Credits [@TheAngryByrd](https://github.com/TheAngryByrd) - [Native Tasks for TaskResult, TaskOption, and TaskResultOption](#169) Credits [@TheAngryByrd](https://github.com/TheAngryByrd) - [Add explicit type parameters to ignore functions](#174) Credits [@cmeeren](https://github.com/cmeeren) - [Adds CancellableTaskResult](#172) Credits [@TheAngryByrd](https://github.com/TheAngryByrd)
- [Moves many functions to inline with InlineIfLambda for performance](#166) Credits [@TheAngryByrd](https://github.com/TheAngryByrd) - [Native Tasks for TaskResult, TaskOption, and TaskResultOption](#169) Credits [@TheAngryByrd](https://github.com/TheAngryByrd) - [Add explicit type parameters to ignore functions](#174) Credits [@cmeeren](https://github.com/cmeeren) - [Adds CancellableTaskResult](#172) Credits [@TheAngryByrd](https://github.com/TheAngryByrd) - [Fixes TaskResultCE breaking with a bind in branching such as if](#177) Credits [@TheAngryByrd](https://github.com/TheAngryByrd)
- [Fixing stackoverflows in large while loops](#182) Credits [@TheAngryByrd](https://github.com/TheAngryByrd) - [Moves many functions to inline with InlineIfLambda for performance](#166) Credits [@TheAngryByrd](https://github.com/TheAngryByrd) - [Native Tasks for TaskResult, TaskOption, and TaskResultOption](#169) Credits [@TheAngryByrd](https://github.com/TheAngryByrd) - [Add explicit type parameters to ignore functions](#174) Credits [@cmeeren](https://github.com/cmeeren) - [Adds CancellableTaskResult](#172) Credits [@TheAngryByrd](https://github.com/TheAngryByrd) - [Fixes TaskResultCE breaking with a bind in branching such as if](#177) Credits [@TheAngryByrd](https://github.com/TheAngryByrd)
Proposed Changes
Benchmarks for Alternative Implementations, Inline, and InlineIfLambda
InlineIfLambda RFC and PR
Types of changes
What types of changes does your code introduce to MyLib.1?
Put an
x
in the boxes that applyChecklist
Put an
x
in the boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of what we are going to look for before merging your code.Further comments
If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc...