-
Notifications
You must be signed in to change notification settings - Fork 830
Optimize trackErrors usage #15864
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
Optimize trackErrors usage #15864
Conversation
|
I was actually about to do this. Glad I checked first :) |
vzarytovskii
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice
|
I feel like we can also inline a bunch of stuff in the builder, so it won't be wasting time on calls. |
| trackErrors { | ||
| for tpr, _ in vs do | ||
| return! SolveTypStaticReqTypar csenv trace req tpr | ||
| do! SolveTypStaticReqTypar csenv trace req tpr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure this doesn't cause an early return on the first item in the loop, so do! should be clearer.
Benchmark
open BenchmarkDotNet.Attributes
open BenchmarkDotNet.Configs
open System.Diagnostics
[<NoEquality; NoComparison>]
type OperationResult<'T> =
| OkResult of warnings: exn list * result: 'T
| ErrorResult of warnings: exn list * error: exn
let inline ErrorD err = ErrorResult([], err)
let inline WarnD err = OkResult([ err ], ())
let inline CompleteD() = OkResult([], ())
let inline ResultD x = OkResult([], x)
[<DebuggerHidden; DebuggerStepThrough>]
let bind f res =
match res with
| OkResult ([], res) -> (* tailcall *) f res
| OkResult (warns, res) ->
match f res with
| OkResult (warns2, res2) -> OkResult(warns @ warns2, res2)
| ErrorResult (warns2, err) -> ErrorResult(warns @ warns2, err)
| ErrorResult (warns, err) -> ErrorResult(warns, err)
[<DebuggerHidden; DebuggerStepThrough>]
let inline bindI f res =
match res with
| OkResult ([], res) -> (* tailcall *) f res
| OkResult (warns, res) ->
match f res with
| OkResult (warns2, res2) -> OkResult(warns @ warns2, res2)
| ErrorResult (warns2, err) -> ErrorResult(warns @ warns2, err)
| ErrorResult (warns, err) -> ErrorResult(warns, err)
type TrackErrorsBuilderVanilla() =
member x.Bind(res, k) = bind k res
member x.Return res = ResultD res
member x.ReturnFrom res = res
member x.Combine(expr1, expr2) = bind expr2 expr1
member x.Zero() = CompleteD ()
member x.Delay fn = fun () -> fn ()
member x.Run fn = fn ()
type TrackErrorsBuilderNoClosure() =
member x.Bind(res, k) = bind k res
member x.Return res = ResultD res
member x.ReturnFrom res = res
member x.Combine(expr1, expr2) = bind expr2 expr1
member x.Zero() = CompleteD ()
member x.Delay fn = fn
member x.Run fn = fn ()
type TrackErrorsBuilderInline() =
member inline x.Bind(res, k) = bind k res
member inline x.Return res = ResultD res
member inline x.ReturnFrom res = res
member inline x.Combine(expr1, expr2) = bind expr2 expr1
member inline x.Zero() = CompleteD ()
member inline x.Delay fn = fn
member inline x.Run fn = fn ()
type TrackErrorsBuilderInlineDouble() =
member inline x.Bind(res, k) = bindI k res
member inline x.Return res = ResultD res
member inline x.ReturnFrom res = res
member inline x.Combine(expr1, expr2) = bindI expr2 expr1
member inline x.Zero() = CompleteD ()
member inline x.Delay fn = fn
member inline x.Run fn = fn ()
[<MemoryDiagnoser>]
type InterpolateBenchmarks () =
let vanilla = TrackErrorsBuilderVanilla ()
let noClosure = TrackErrorsBuilderNoClosure ()
let inlin = TrackErrorsBuilderInline ()
let inlin2 = TrackErrorsBuilderInlineDouble ()
let CompleteD = CompleteD ()
let e = System.Exception ()
[<Benchmark>]
member this.Vanilla () =
vanilla {
do! CompleteD
do! CompleteD
do! CompleteD
return! WarnD e
}
[<Benchmark>]
member this.NoClosure () =
noClosure {
do! CompleteD
do! CompleteD
do! CompleteD
return! WarnD e
}
[<Benchmark>]
member this.Inline () =
inlin {
do! CompleteD
do! CompleteD
do! CompleteD
return! WarnD e
}
[<Benchmark>]
member this.Inline2 () =
inlin2 {
do! CompleteD
do! CompleteD
do! CompleteD
return! WarnD e
}
BenchmarkDotNet.Running.BenchmarkRunner.Run (
typeof<InterpolateBenchmarks>.Assembly,
DefaultConfig.Instance.WithOption (ConfigOptions.JoinSummary, true))
|> ignore
The only downside with inlining
|
|
I'm seeing allocations go from ~117MB to ~115MB in |

I suggest you hide whitespace when reviewing.