Fsharp: Compiling with FSharp.Core outputs localized satellite assemblies FSharp.Core.resources.dll, loading of which slows startup time of F# applications

Created on 13 Dec 2018  Â·  16Comments  Â·  Source: dotnet/fsharp

I was researching startup time of my F# console project (.NET Framework, but similar behavior can be seen with .NET Core, see https://github.com/fsharp/fsharp/issues/868), I noticed a considerable time was spent loading satellite assemblies.

Repro steps

I'll update with a repro, if necessary. Though first-off, it seems odd that previously FSharp.Core.dll was all the dlls needed to ship F# projects, and now, at least by default, the FSharp.Core.resources.dll (15x!) are deployed as well, and I couldn't find this change documented anywhere, so I'm wondering if it is a bug.

Expected behavior

Honestly, not sure. I wouldn't expect the resource files of 15 languages to be output, esp. if your own application or dll doesn't need localization. I couldn't find an option to switch it off (though, obviously, I could simply delete them).

If this change was intentional (it looks like it is), it shouldn't have a negative impact on startup performance.

Actual behavior

On my system, after NGEN'ing my application the startup went from 1.8s to 0.32s. That used to be 0.2s, so I researched what caused the (new) delay. It turned out that ~0.125s (in the image ~0143s, but that was a worst case run) is used to locate the satellite dlls, even though the resources are never used. The reason they were loaded is because .cctors have let-bindings that call the resources directly. Whenever any part of such module is loaded (reference of function etc etc), it will also force-load the resources.

image

From my observations it seems that these resources are only needed in raising certain (localized) exceptions, which suggests that the obvious solution would be is to delay-load the satellite assembly until the resources are actually needed.

Note that earlier versions of F# projects didn't require the satellite assemblies. I'm wondering if they could be assembly-merged and/or inlined somehow if you only ever need one language.

Known workarounds

Do not use functions or types of modules that also have let-bindings that require loading of the satellite assemblies, but this is hard (for instance, using the map type causes this already).

Related information

Any recent version of VS2017 and F#, from 4.3, I believe. Reported before in August by others, but on the wrong location, where it didn't get attention, hence reporting it here (see: https://github.com/fsharp/fsharp/issues/868).

Area-Compiler Severity-Low bug

Most helpful comment

I'd love to have these not be included. It's just icky..

All 16 comments

I proposed to fix the forced loading of resources in #4465 and the final fix that landed is #4503 (See discussion in #4465) But I don't know if it's already published.

One thing to note is that the slowdown also happens when the satellite assemblies aren't there as simply loading the resource translation subsystem is very slow (But I think it's even slower if the dll is there as it attempt to load them)

It's also possible that it was fixed an regressed somehow

I'd love to have these not be included. It's just icky..

We're upgrading to vs2017 now with FSharp.Core 4.5.4 and Visual F# Tools 10.2 for F# 4.5 15.8.0.0. Looks like the fix was merged in March, maybe something changed since then?

4503 was merged into VS 2017 version 15.7. So either that didn't address this issue, or something else regressed it. cc @KevinRansom

@abelbraaksma The resourcemanager.GetString is worrying, since a hit count of 5 is the exact number as before and it is occurring in the PrimTypes class constructor.

Can you let me know, what version of VS?, what version of FSharp.Core? and what version of the dotnet cli you see this with?

In the meantime I will see what I can find out here.

@KevinRansom, I'm currently having trouble upgrading to 4.5 (it requires changing the project files, I can't find an automatic way of doing this, which in turn may prevent others to upgrade as well, the option for 4.5 isn't even shown on existing projects), so this is F# 4.3.4 (I think, latest before 4.5)

It's a .NET Framework 4.7.1 project, haven't tested it with Core. Running VS2017 15.9.3 (or 15.9 4, I'm not behind my computer, but I upgraded yesterday). It's a console exe.

@abelbraaksma
4.3.4, does not have the changes.
The 5 eagerly evaluated strings are still backed by eagerly evaluated properties:
Converting to the nuget package is pretty easy

  • In VS unload the project using right mouse/Unload Project
  • Edit it using right mouse/Edit Project
  • Delete the current reference to FSharp.Core looks similar to:

<Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <Private>True</Private> </Reference>

  • Save the file and reload the project
  • Use manage nuget references right mouse/Manage Nuget references
  • In the dialogue that appears select browse and type FSharp.Core to search for the FSharp.Core package.
  • Select Latest Stable 4.5.4 and press Install, your project should then be up to date.
  • Rebuild Project and all should be well.
  • Nuget will have add a package.config to your project, or updated it if it already existed and add this reference:
    <Reference Include="FSharp.Core"> <HintPath>..\packages\FSharp.Core.4.5.4\lib\net45\FSharp.Core.dll</HintPath> </Reference>
    FSharp.Core is used and localized to a set of languages, which is why the localized strings are shipped in our fsharp.core nuget package. I am unaware of a mechanism for turning of deployment and packaging of localization libraries. But it's msbuild, I suppose you can always write a target that does that, if you really need to.

In my solution it’s the issue with 4.5.4 (latest on nuget) as well. And I went through all projects during upgrade replacing includes already. Is there anything else that needs to be changed?

@luajalla

If your issue is that it's loading all of the localized assemblies during startup. Then that shouldn't happen. The fix was to ensure that FSharp.Core only loads those strings on-demand. There is nothing else that you need to do, other than ensure you are loading that assembly.

How are you verifying that the localized assemblies are being loaded during FSharp.Core startup?

Thanks

Kevin

@KevinRansom
Sorry, i somehow didn't see the full message from the phone, I was referring only to not being able to disable copying of all resources, which seems to be like it is by design? Didn't check performance yet as it is not in a runnable shape atm - we just started with upgrade of a relatively big solution and run into all kinds of things (e.g. like https://github.com/Microsoft/visualfsharp/issues/3221), hopefully mostly harmless/fixed with VS restart...

@luajalla

Ahh … that makes sense. I am not aware of how to disable copying localized resources. I took a look in the code and can't see an option to turn it off. Given that it’s msbuild I suppose you could write an msbuild target to do that.

So yes, it’s by design, that they are deployed. They should not however, be loaded unless they are used, And that is the case in 4.5.0 and up.

Kevin

@KevinRansom
Thanks for checking! Will go for additional step in the build script then, that's probably the cleanest solution.

@abelbraaksma, modifying project files, esp if there're many of them, is often easier outside of VS with your favourite kind of text replacement (except handling NuGet packages).

@KevinRansom thanks for the detailed explanation. Though I didn't mean to say that I don't know how to do it, just that it is unintuitive and having to change the project files by hand seems wrong. Also, it's good to have this explanation, but we should somehow tell people about it so they know how to do it themselves (either with an upgrade button showing the explanation, or adding the option back to the property pages, even if it means that we can only show a message box detailing the steps).

I'm glad that the loading issue is fixed, I'll test and perhaps I can see the performance improvement.

A little bit less glad that they're here to stay without an option to not include them. I understand why we support localization, it's just that not many projects support all locales, would be nice if we could cherry pick.

@luajalla, I agree, though with Power Commands plugin it gets easier. Also, I find the easiest way to update dozens of project files this way is to use XSLT (use 2.0 or 3.0, don't use version 1.0, supported by Microsoft, it's ancient and tedious to use), after all, it's just xml.

@abelbraaksma,

I agree about the update in Dev14 there was an actual upgrade process that we could have hooked into on project load, but that was deprecated before Dev15.

@abelbraaksma

if you are using the new sdk-style project format you can add this property to your project

<SatelliteResourceLanguages>en</SatelliteResourceLanguages>

this should ensure that these resource dlls are not deployed

@jmarolf, thanks, I didn't know of that option. I take it that that doesn't work with legacy project files? Well, at least now I have a good reason to do take on the arduous conversion task ;).

Was this page helpful?
0 / 5 - 0 ratings