-
Notifications
You must be signed in to change notification settings - Fork 834
Some performance optimizations and code cleanup #16302
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
Some ContainsKey double lookups to TryContainsKey Some nested list Contains to set Contains. Some FSharpLint rules also "dotnet fantomas . -r" but I bet it'll make merging harder.
…h SelfContained_Trimming_Test and StaticLinkedFSharpCore_Trimming_Test the results once.
|
Nice changes. However, I would not take the CI duration difference at face value. They sometimes wildly fluctuate between runs. |
I know there is a tiny difference on readability, but the other one makes more complex and slower AST.
I know, but that was the only thing I could measure easily the build time, it was consistently faster also in my local machine. |
Yeah, CI runs aren't trustworthy, they use different machines and containers. |
I am sure JIT will optimize branching. It sure can't be that significant. |
|
Modifying 80 files, I don't want to make separate PR per each one. And I don't want to cause more work for you. These are all little independent changes without public API, so you could do merge so that you just unstage/ignore the parts you don't like. |
I like your optimism. :-) |
It looks alright, the only singular part I'm slightly worried about is set allocation and GC. Want to hear others' opinions. |
Hm, actually, JIT generates exactly the same code for both: |
|
Ok I think I found even better way to do the check. Now reverted some changes. Interesting to see if AOT size changes again. But in longer run I think the unit-test testing exact binary byte-size of dll is not sustainable solution. It'll just make the 30min build fail twice. |
Its purpose is exactly that. To verify consciously that size change is desired. |
Can it be run locally quickly? E.g. build Release without testing then look the size? |
I'll split this PR to 2, to send the set allocations as a separate PR, so that can be explored more in detail. |
…e PR comments Will open this as separate PR
|
@vzarytovskii Your sharplab points to x86 version of code. You have to choose x64 explicitly there. X86 code is generally worse |
Either it doesn't work or x64 yields the same thing |
|
@vzarytovskii As far as I can see all the issues were resolved and this would ready for merge. |
|
I did some benchmarks (very rough ones), and the difference is within statistical error for the most part. Both compilers are not ngen'd or crossgen'd, so difference in elapsed time will be even smaller in SDK. Compiling compiler itself: Before, working set, mb: 3312 Before, GC, (Gen0, Gen1, Gen2), number of: 22, 13, 7 So it's marginally faster, migth be GC-ing and allocating a bit more. I am alright with merging it if folks are fine with trimming changes @0101 @T-Gro |
Was this with the List.contains -> Set.contains or without? As I already removed that from this PR as non-beneficial. |
With just latest commit from the branch. So probably without. Not sure where do allocations come from, they might as well come from the differences in the code we compile (less lambda lifting). |
Off-topic but look at this: |
|
Thinking of infrastructure regarding the "compiler doesn't generate performant IL":
Then we can track tests added like this to tickets in a csv fail which is maintained in the repository, and helps with the process of having a "Tracking" meta issue about all codegen / optimization related issue, and generating some release notes about code generation. I can give this a look to sketch something, taking the comments and issues opened about those here, and maybe in the issue tracker, and add to the IL baseline tests and list the tests in a csv file to make a PR. |
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 think in its current shape the changes are beneficial and benign. Up for merging.
|
I am also fine with merging, approved + marked my comments as resolved. |
|
Sigh...fantomas changes from latest Fantomas update...it's going now to affect majority of PRs :( |
|
@nojaf FYI - rather small example of what's usually happening when a lot of small formatting changes introduced. |
# Conflicts: # src/Compiler/CodeGen/IlxGen.fs # src/Compiler/Driver/CompilerDiagnostics.fs # src/Compiler/Driver/GraphChecking/TrieMapping.fs # src/Compiler/Interactive/fsi.fs # src/Compiler/Service/ServiceParamInfoLocations.fs # src/Compiler/SyntaxTree/SyntaxTreeOps.fs # src/Compiler/Utilities/sformat.fs # src/FSharp.Core/QueryExtensions.fs # src/FSharp.Core/seq.fs
|
@Thorium thanks for taking care of it. It was rather fast. Did you do manual resolution or with something more "smart"? |
|
Manual, I know what are the changes and I see what Fantomas did, and there were actually not too many conflicts. Just had to force because I didn't want to deal with GitHub partial successful automation. I recommend Kdiff3 as mergetool. |
Well, this was bound to happen for all open PRs. But again, I do not perceive this as a blocker to not update Fantomas. There will never be a good time when there are no open PRs. |


I did some code cleanup without significant changes, and managed to cut around 10% of unit-test-execution times in my laptop (which might be coincidence).
I cloned fsharp-repo and my unit-tests on
mainbranch fails forFSharp.Compiler.Scripting.UnitTests.InteractiveTests.Script with nuget package that yields out of order dependencies works correctlySo I have to do this PR to see CI results.Here are the main changes:
if whateverDict.ContainsKey x then Some whateverDict.[x] else Nonethis uses.TryContainsKeyto do the same business with only one lookup.When your nested for-loops are having list.Contains, rather convert the nested list first to a Set for faster searching. So instead of(meanwhile still applicable, reverted to separate PR due to memory concerns)for x in xs do if List.contains x ysthis doeslet ySet = ys |> Set.ofList; for x in xs do if Set.contains x ySetI also changed some small DU to a Struct, and(reverted to avoid changing public api)Some inttoValueSome intisNull xoverx = nullbecause it's fasterList.collect(fun x -> ...)overList.map(fun x -> ...) |> List.concatetc to avoid iterating lists multiple times.List.map myFuncoverList.map(fun x -> myFunc x), and I'm not sure if it will cut some extra lambda from AST or not. For this reason it also prefers>>but I generally try to avoid too much point-free style.I didn't include FSharpLint as tool because it gave also many false-positives like member-access setting to false:
MyClass(x, MyMember = false), second parameter should not be replaced to false, because it's a named setter, not Boolean comparison.