fsharp
fsharp copied to clipboard
F# compiler is really slow when trying to compile .net8.0 project with SDK 8.0.303
Repro steps
Provide the steps required to reproduce the problem:
- Clone this specific commit: https://github.com/BoyBaykiller/opentk/tree/5c9dc6e6f0ec4c911fe05357dbfccc7e81d1842f
- Try to compile this test project using
dotnet build -v:n .\tests\OpenTK.Tests\OpenTK.Tests.fsproj
Expected behavior
The compiler should compile the project (this particular commit has a runtime error, but that shouldn't matter).
Actual behavior
The compiler never finishes compiling the project.
Known workarounds
Switching to .net6.0 fixes the issue.
Related information
I also tried targeting and compiling the project with SDK Version: 9.0.100-preview.6.24328.19 and I still get the hang.
Provide any related information (optional):
- Operating system: Windows 10
- .NET Runtime kind (.NET Core, .NET Framework, Mono): .NET Core 8.0.303
- Editing Tools (e.g. Visual Studio Version, Visual Studio): Visual Studio 2022 Version 17.10.5
So, the bad news is that I can't reproduce it on my mac, on both net7.0 and net8.0 as targets, compilation takes about a minute (47-55 seconds) on my machine (M2 macbook). net6.0 in main with the same SDK (8.0.100) takes about 4 seconds, which leads me to believe that it is the same sort of issue as described in https://github.com/fsprojects/Fleece/issues/146.
TL;DR of the issue is: around .NET7 BCL rolled out "Static abstracts in interfaces" feature (which led to much bigger interfaces hirearchy), at the same time we rolled out support for static abstracts as well as some changes to SRTPs. And what I think is happening is that once you introduce new BCL, some of the functionality in your tests is making typechecking go brrr, and performance degrades significantly, see discussion in the issue above, as well as Don's comment in https://github.com/fsharp/fslang-design/commit/71cd2e04bc181264a6a68c731ff018369848d1f9.
Conclusion: with all this in mind, the the best current course of action (for us or contributors) would be to:
- Take current compiler from
main. - Build it (Release).
- Create a
global.jsonfile pointing to latest.NET9SDK (preview 6 at the moment of me commenting it). - Include it in the project from OP (tests project, from
masterbranch, point to custom compiler viaUseLocalCompiler.Directory.Build.propsimporting). - Profile it with
net6.0(default) target. - Change target to
net8.0(or switch branch tootk5-add-matrx-simd-equal,bba4019df27b0a54ff48ca2205d681dbe990824bat the moment of me commenting it). - Include custom compiler there again. Profile it again.
- See what's different/what's wrong.
- Try to make it better.
As a temporary workaround(s), there were some comments from @gusty in the original issue in Fleece, which might help.
The worst part is that the more people will move to net8.0 as TFM, the more people might hit this issue. I will try to collect some profiles once I have some free time, unless someone will beat me to it.
Profiles for
masterbranch with tests project upgraded tonet6.0: net6.zipotk5-add-matrx-simd-equalbranch built with no changes (net8.0): net8.zip
Running via .NET9p6 runtime host, with net8 SDK assemblies, on latest main of compiler.
In case if someone wants to look at it.
Compiler spent a minute (out of ~2 under profiling) in just getting interfaces for all types:
Additionally, some type directed conversions are involved:
(I presume, when we try to find conversions we need to traverse all interfaces)
I will experiment with caching stuff more once I'm off of work.
Is it possible to link the changes that fixes this issue?
Is it possible to link the changes that fixes this issue?
https://github.com/dotnet/fsharp/pull/17668 it didn't link for some reason
Is there an ETA on this? Has been bugging me for months and makes working in some repositories pretty much impossible.
Is there an ETA on this? Has been bugging me for months and makes working in some repositories pretty much impossible.
It will be released with 9.0.200 in January, but will be under preview flag, and only for compilation.
Is this also going to be backported to 8.0? We compile all our repositories with the .NET 8 SDK due to this issue.
Is this also going to be backported to 8.0? We compile all our repositories with the .NET 8 SDK due to this issue.
It will be up to the team. @t-gro, @kevinransom thoughts?
Is this also going to be backported to 8.0? We compile all our repositories with the .NET 8 SDK due to this issue.
Can you elaborate please? The issue is about a binary format mismatch between NET6 and NET7.
Are you encountering the same/similar between NET8 and NET9 ? If yes, I would prefer to fix that (to benefit everyone) instead of backporting the caching layer.
We would definitely appreciate your support in identifying a problem which prevents you from building using .NET9 SDK.
We could probably use .NET 9 for our builds if we absolutely had to, I'm not aware of any current incompatibilities like with 6 and 7.
But back when .NET 7 was released we had to force the build system in dozens of repositories to .NET 6. Now with the phasing out of .NET 6 we thought it'd be a good idea to keep using the LTS version of the SDK to be resilient against any issues that might arise with the .NET 9 SDK. Also seeing that the issue has never been addressed made us a bit cautious. So in short, we opt to use the .NET 8 to be on the safe side.
But I think that's beside the point anyway. I'd expect a major regression like this one to be fixed in the current LTS version.
For this bug, it's has not been a regression of the compiler - it was a consequence of many interface layers added to numeric types in NET. The cache has been added as a brand new feature, even under the language version flag for langversion=preview and NET10 eventually.
In general, we are not backporting preview language features into older versions of the language.
I do understand the desire, but a manually cherry-picked migration of this feature into NET8 SDK, pretending this is a NET8 feature and not a preview feature, would only add more risks. It would also slow down fixes in this area due to inability to automatically backport changes once a conflict like this would be created.
even under the language version flag for langversion=preview and NET10 eventually.
There is some urgency about this for F#. Being unusably slow compiling and providing intellisense for dotnet8 and dotnet9 isn't an acceptable situation. dotnet8 and dotnet9 are the only supported versions of dotnet.
Once it's fixed (in both compilation and tooling) it needs fix dotnet9 compilation without the need to set preview flags. For every developer that discovers the preview flag there will be 10 that don't and end up with a terrible experience of using F#.
Our team is lucky because we have a lot of things that are or can be netstandard. But again, few teams will know that that is a workaround (and we didn't and had a bad experience for several months after moving to net8/net9 - we assumed it as a bad VS update).
Well, i totally understand backporting larger fixes is problematic, but i second @charlesroddie and i just want to underline the importance of "trustworthy" LTS versions, especially important for people working in larger ecosystems.
That said, I'm a long term F# advocate and pushed technologies towards F# in various companies. I'm using it for teaching, and my company of course also is mostly F#. Performance regressions make all of this hard. And it reduces trust in the platform for everyone involved in technical decisions. There is always a long latency from defect detection to really having it available in the wild. For this reason there is always the idea to stick with LTS not to stall the whole ecosystem waiting for a F# compiler problem to go away for end-users.
I see that there are technical stories behind it but from a user perspective going from already slow 24s for a particular project to 76 is a bummer (and users would actually identify it as a regression not caring about technical side). It does not sound like this severe but it is. Arguing for F# with its slow compiler is already hard enough - going to 76 is a huge one.
Slow intellisense is so frustrating for new users also. With a large ecosystem and libraries (not just apps), such things take for a long time to clear out, e.g. this one, quite mild will be a trap for users for months until all libraries and examples get fixed.
Maybe we could assemble a set of "hard-to-compile" real-world files/projects (if appreciated). Maybe this could be part of a performance test suite @hyazinthh @krauthaufen @luithefirst
IIRC Vlad did a fix for this but enabling it in tooling depends on this https://github.com/dotnet/fsharp/pull/18190
Indeed, this is now only implemented for standalone CLI compilation. I will follow up on that PR to progress it to tooling as well.
This regression was particularly difficult to spot because it came "from the side" by the static abstracts feature and the implosion of interface hierarchies in size this has caused in the BCL.
@haraldsteinlechner: I have a dream of having a suite of pluggable compiler-CI extensions, ideally just a list of triplets: repo url, commit hash (can be updated on demand), and a build+test script to run. Designed exactly for spotting compiler regressions for popular/important/"special" libraries in the ecosystem.
@T-Gro you should talk to Jared about his complog tool. I think Roslyn uses it in part to make it easy to extract out these kind of calls into something that can be independently tested across versions.
@T-Gro thanks for clarification and the great work. Yes i will set something up!
Is this still not fixed for the .net8 or .net9 sdk?
I still have multi-minute builds and it's very bad...
Hi @NogginBops, the fix is currently enabled only under language version preview. Please try setting
<PropertyGroup>
<LangVersion>preview</LangVersion>
</PropertyGroup>
in your projects.
Using <LangVersion>preview</LangVersion> does indeed help compile times, though it does still take around a minute to compile the project. That's down from a 17 minute build, so it definitely helps, thanks.
Given that the preview exists, I'll redo https://github.com/dotnet/fsharp/issues/18225 and get some timings of builds with netstandard2.0, dotnet9 without language preview, and dotnet9 with language preview.
Hopefully it works and then we can push to get this out of LangPreview and also into IDEs!
I'm coming back to this issue again and again. IntelliSense is unusably slow, it takes multiple minutes to get error messages and even syntax highlighting is broken. I guess this is also possibly related to (or made worse by) #18807 as we are also using xUnit in this project.
Compiling from command line does seem to alleviate the issue, but it doesn't help IDE performance...
@NogginBops I think the changes to caching to make this faster are already in. At least in VS preview.
Edit: NOPE.
The issues I'm describing are experienced on the opentk5.0 branch.
https://github.com/opentk/opentk/tree/opentk5.0
The current master branch uses net6.0 and isn't affected by this problem.
Here is a video showing what I'm experiencing: https://github.com/user-attachments/assets/10fa70e2-c7cc-4aed-8dd5-8e84d74a2624
The issues I'm describing are experienced on the
opentk5.0branch. https://github.com/opentk/opentk/tree/opentk5.0 The current master branch usesnet6.0and isn't affected by this problem.Here is a video showing what I'm experiencing: https://github.com/user-attachments/assets/10fa70e2-c7cc-4aed-8dd5-8e84d74a2624
What's the VS version? The initial fix was only affecting compiler, not IDE. VS fix went in much later, May 1st (https://github.com/dotnet/fsharp/pull/18499), and was merged in VS even later, June 16th (https://github.com/dotnet/fsharp/issues/18691).
I have suspicion this didn't end up in 17.14? @T-Gro
The issues I'm describing are experienced on the
opentk5.0branch.
Right! I forgot.
Unfortunately, I can confirm the fix is not in most recent 17.14.