Reducing the size of self-contained .NET Core applications
Just for note keeping, I've written down some methods of reducing the size of a .NET Core application. I thought others could use it as well, so here you go.
> dotnet new console
> dotnet publish -r win-x86 -c release
Size: 53.9 MB - yuck!
> cd c:\corert
> build.bat release
Open nuget.config and add <add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
> dotnet add package ILLink.Tasks -v 0.1.4-preview-981901
Original Size
First off, let's see how much disk space a self-contained 'hello world' application takes up.> dotnet new console
> dotnet publish -r win-x86 -c release
Size: 53.9 MB - yuck!
Trimming
Microsoft has built a tool that finds unused assemblies and removes them from the distribution package. This is much needed since the 'netcoreapp2.0' profile is basically .NET Framework all over again, and it contains a truckload of assemblies our little 'hello world' application don't use.
> dotnet new console
> dotnet add package Microsoft.Packaging.Tools.Trimming -v 1.1.0-preview1-25818-01
> dotnet publish -r win-x86 -c release /p:TrimUnusedDependencies=true
Size: 15.8 MB
Much better! Although, we have only removed unused assemblies - what about unused code?
Linking
The Mono team over at Xamarin has built a linker that can remove unused code from assemblies. If you only use List<T> from System.Collections.Generic.dll, why have HashSet<T> and all the other classes? This has a huge impact reflection obviously, so use with care.
> dotnet new console
> dotnet new nuget
Open nuget.config and add <add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
> dotnet add package ILLink.Tasks -v 0.1.4-preview-981901
> dotnet publish -r win-x86 -c release
Size: 22.9 MB
Not as good as trimming it seems, but definitely better than doing nothing.
.NET Native
In the world of C, we have a thing called 'static linking' which means all assemblies get linked into a single assembly. This is very nice for small applications or deployments that can't rely on existing assemblies on the system. C# does not have this feature, but with .NET Native we get something very close.
.NET Native is actually a refactored runtime called CoreRT, which uses RyuJIT to create native assemblies. Getting it to work is not as easy as trimming or linking since unfortunately Microsoft only supports UWP as a native platform. .NET Core is therefore left out in the cold for now.
1. Download CMake 3.8 or later. I choose cmake-3.10.1-win64-x64.msi
2. Make sure you have Visual Studio 2017 with C++ support installed
3. Download the latest successful build. Check their Jenkins for commit hash.
4. Unpack the content to c:\corert\
5. Open 'x64 Native Tools Command Prompt for VS 2017'
6. Now you should compile .NET Native:
> build.bat release
Note that it takes a long time to download missing packages and compile the whole project.
7. Create a new .NET Core project and add .NET Native msbuild targets:
> mkdir c:\test
> cd c:\test
> dotnet new console
> set IlcPath=c:\corert\bin\Windows_NT.x64.Release
8. edit test.csproj and add the two lines just before '</Project>':
<Import Project="$(MSBuildSDKsPath)\Microsoft.NET.Sdk\Sdk\Sdk.targets" />
<Import Project="$(MSBuildSDKsPath)\Microsoft.NET.Sdk\Sdk\Sdk.targets" />
<Import Project="$(IlcPath)\build\Microsoft.NETCore.Native.targets" />
> dotnet publish -r win-x64 -c release
Size: 3.95 MB
Wow! Unused assembles and unused code has been removed and everything is statically compiled into an executable. Note that I used x64 and not x86 in the .NET Native build, as I'm not sure .NET Native supports x86 fully yet.
Bonus: Trim and Link
We can actually use both trimming and linking to reduce the size of the application further. It seems that the trimmer is better at finding unused assemblies than the linker is, but if we do both steps, the trimmer will remove assemblies and the linker will remove the dead code from the remaining assemblies.
Just add both steps together like this:
> dotnet new console
> dotnet new nuget
Open nuget.config and add <add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
> dotnet add package ILLink.Tasks -v 0.1.4-preview-981901
> dotnet add package Microsoft.Packaging.Tools.Trimming -v 1.1.0-preview1-25818-01
> dotnet publish -r win-x86 -c release /p:TrimUnusedDependencies=true
Size: 14.9 MB
Final size is pretty much as low as it gets right now without native compilation.
Conclusion
We can easily reduce the size of self-contained .NET Core application, we just have to add the right packages and switches. Microsoft has changed the behaviour of the .NET Core project and publish system several times now, which has made pretty difficult to find the right documentation, so hopefully, this small blog post will sort it out.
Comments
You helped me a lot !
It is a pre-release package which is why it does not show up in Visual Studio's Nuget manager by default. You can tick the "Include prerelease" checkbox in VS and it shows up in the search results.
% dotnet add package ILLink.Tasks -v 0.1.4-preview-981901
Determining projects to restore...
Writing /var/folders/xw/2sf15j3s52d1wsy1p_z4sb980000gp/T/tmpc7MlQ6.tmp
info : Adding PackageReference for package 'ILLink.Tasks' into project '/Users/username/src/MyProject/MyProject.csproj'.
info : Restoring packages for /Users/username/src/MyProject/MyProject.csproj...
info : GET https://api.nuget.org/v3-flatcontainer/illink.tasks/index.json
info : NotFound https://api.nuget.org/v3-flatcontainer/illink.tasks/index.json 159ms
error: Unable to load the service index for source https://dotnet.myget.org/F/dotnet-core/api/v3/index.json.
error: The SSL connection could not be established, see inner exception.
error: The remote certificate is invalid according to the validation procedure: RemoteCertificateNameMismatch
What can I do?
Post a Comment