Uh oh!
There was an error while loading. Please reload this page.
Some performance optimizations and code cleanup - #16302
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.
Thorium
commented
Nov 19, 2023
…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. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Thorium
commented
Nov 19, 2023
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. |
vzarytovskii
commented
Nov 19, 2023
Yeah, CI runs aren't trustworthy, they use different machines and containers. |
vzarytovskii
commented
Nov 19, 2023
I am sure JIT will optimize branching. It sure can't be that significant. |
Thorium
commented
Nov 19, 2023
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. |
Thorium
commented
Nov 19, 2023
I like your optimism. :-) |
vzarytovskii
commented
Nov 19, 2023
It looks alright, the only singular part I'm slightly worried about is set allocation and GC. Want to hear others' opinions. |
vzarytovskii
commented
Nov 19, 2023
Hm, actually, JIT generates exactly the same code for both: |
Thorium
commented
Nov 19, 2023
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. |
vzarytovskii
commented
Nov 19, 2023
Its purpose is exactly that. To verify consciously that size change is desired. |
Thorium
commented
Nov 19, 2023
Can it be run locally quickly? E.g. build Release without testing then look the size? |
Thorium
commented
Nov 19, 2023
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
En3Tho
commented
Nov 19, 2023
@vzarytovskii Your sharplab points to x86 version of code. You have to choose x64 explicitly there. X86 code is generally worse |
vzarytovskii
commented
Nov 19, 2023
Either it doesn't work or x64 yields the same thing |
Thorium
commented
Nov 19, 2023
@vzarytovskii As far as I can see all the issues were resolved and this would ready for merge. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
vzarytovskii
commented
Nov 20, 2023
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 |
Uh oh!
There was an error while loading. Please reload this page.
Thorium
commented
Nov 20, 2023
Was this with the List.contains -> Set.contains or without? As I already removed that from this PR as non-beneficial. |
vzarytovskii
commented
Nov 20, 2023
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). |
Thorium
commented
Nov 20, 2023
Off-topic but look at this: |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
smoothdeveloper
commented
Nov 20, 2023
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. |
psfinaki
left a comment
There was a problem hiding this comment.
I think in its current shape the changes are beneficial and benign. Up for merging.
T-Gro
commented
Nov 21, 2023
I am also fine with merging, approved + marked my comments as resolved. |
vzarytovskii
commented
Nov 22, 2023
Sigh...fantomas changes from latest Fantomas update...it's going now to affect majority of PRs :( |
vzarytovskii
commented
Nov 22, 2023
@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
vzarytovskii
commented
Nov 22, 2023
@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. |
nojaf
commented
Nov 23, 2023
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.