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

Optionally bypass structure checks for performance #105

Merged
merged 3 commits into from
Mar 6, 2018

Conversation

vbfox
Copy link
Contributor

@vbfox vbfox commented Mar 5, 2018

  • Move lot of things to Lazy<T>
  • Allow to bypass the checks that would force all theses lazy to materialize (Because the discriminated union structure is checked)
  • Add a performance tips page to the docs
  • Other small optimizations

Note: Some optimizations and additional fields (Like the addition of GroupedSwitchRegex) can seem to make no sense. They are here to pave the way for the next optimization that will allow to serialize the whole UnionArgInfo structure and loading it instead of computing it. The corresponding WIP branch can be found here : https://github.com/vbfox/Argu/tree/perf

let result = System.Collections.Generic.Dictionary<'key, 'value>(s.Length)
for pair in s do
result.Add(fst pair, snd pair)
result :> System.Collections.Generic.IDictionary<_,_>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the need to upcast?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was to keep the signature the same but as the result is only ever used in private types it's useless. Fixed it.

|> Seq.append helpParam.Flags
|> Seq.filter (fun name -> name.Length = 2 && name.[0] = '-' && Char.IsLetterOrDigit name.[1])
|> Seq.map (fun name -> name.[1])
|> System.Linq.Enumerable.Distinct
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the Linq distinct function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An artefact of an optimization ;)

The F# version ends up initializing some special comparer (For F# types) that references things in a module from FSharp.Core.
When initialized this module (fsharp.core!<StartupCode$FSharp-Core>.$Prim-types..cctor()) loads translation strings for standard exceptions (invalidOp & friends), by searching for dlls on disk and loading them in memory to load resources and this ends up costing ~80ms

In the end i'm not using this function anymore in the "load serialized data" side so I'll change it back (Other functions call into $Prim-types in the main usage, no way to avoid it here)

@eiriktsarpalis
Copy link
Member

This is awesome work, well done!

@vbfox
Copy link
Contributor Author

vbfox commented Mar 6, 2018

BTW here are the perf results with / without checking the structure

Method Args checkStructure Mean Error StdDev
Parse add nuget Foo.Bar True 498.1 ms 8.908 ms 17.996 ms
Parse add nuget Foo.Bar False 229.7 ms 3.204 ms 6.472 ms
Parse install True 482.8 ms 3.880 ms 7.838 ms
Parse install False 195.7 ms 1.464 ms 2.957 ms
Parse restore True 481.8 ms 3.488 ms 7.046 ms
Parse restore False 195.1 ms 1.519 ms 3.068 ms

Flame graph with checkStructure=true:
perfviewdatacheck flamegraph1

Flame graph with checkStructure=false:
perfviewdatanocheck flamegraph1

@vbfox
Copy link
Contributor Author

vbfox commented Mar 6, 2018

Very visible on the second graph is the problem that is fsharp.core!<StartupCode$FSharp-Core>.$Prim-types..cctor() (The long bar in the top left calling into mscorlib.ni!ResourceManager.GetString for 35ms)

It come from the error strings in LanguagePrimitives.ErrorStrings as let members are compiled per-source-file (instead of per-module) they are loaded as soon as anything in $Prim-types is touched (And this module is used in a lot of things)

Maybe I should PR a change to FSharp.Core here, all other exception generation is made via functions and it's nearly the only case where they are resolved in the static constructor...

@forki
Copy link
Member

forki commented Mar 6, 2018

PR to FSharp.Core ftw!

@eiriktsarpalis
Copy link
Member

@vbfox Cool. I'd be interested to see the numbers compared to the current release of Argu.

@eiriktsarpalis eiriktsarpalis merged commit e44fddf into fsprojects:master Mar 6, 2018
@toburger
Copy link

toburger commented Mar 7, 2018

Does something speak against setting checkStructure automatically to false for release builds?

@vbfox
Copy link
Contributor Author

vbfox commented Mar 7, 2018

@toburger Nothing as long as you test each version in debug mode first, that's what I suggest in http://fsprojects.github.io/Argu/perf.html#Bypassing-structure-checks Other solution is to force the check in unit tests (So if you have them run on PRs you see if a PR introduce something invalid)

@vbfox
Copy link
Contributor Author

vbfox commented Mar 7, 2018

@eiriktsarpalis Here is a run on master before my changes :

Method Args Mean Error StdDev
Parse add nuget Foo.Bar 547.7 ms 16.360 ms 33.05 ms
Parse install 512.5 ms 19.997 ms 40.40 ms
Parse restore 478.4 ms 6.257 ms 12.64 ms

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants