Add linker instructions (#878)

* Add linker instructions

* Improve examples

* Add advanced instructions

* Format commands

* Update per feedback
This commit is contained in:
Rich Lander 2017-08-24 11:23:25 -07:00 committed by Russell C Hadley
parent ede1f4d017
commit 9a6dcec01f
3 changed files with 144 additions and 0 deletions

View file

@ -0,0 +1,63 @@
# Using IL Linker Advanced Features
This document describes the more advanced features for the IL Linker and provides more insight into how it functions.
The basic features and instructions for the linker are described in the [Using the .NET IL Linker](linker-instructions.md) document.
## How the linker works
At a high level, the linker uses a mark-and-sweep algorithm to remove unused code: starting from some set of roots in the code (roots can be classes, methods, properties), the linker scans the IL using mono/cecil to look for code that gets called in other functions, classes, and dlls, marking the code that is reachable from the set of roots as it goes. In the sweep phase, the linker will scan through all of the code, removing any parts that weren't marked in the mark phase.
The dynamic features of .NET make it hard for this analysis to catch all cases in which code is called from the set of roots. In particular, reflection enables developers to do things like scan for assemblies at runtime and call their code. In general, it is impossible for the linker to determine exactly which code will get called at runtime, so there will be cases in which the linker removes code that should not be removed. We may add more sophisticated heuristics in the future to catch some common patterns of reflection usage, but the general problem remains, which necessitates a mechanism by which developers can explicitly tell the linker which parts of the code to consider as roots. Thus the linker operates on a set of assemblies and a set of specified code roots, producing an output set of assemblies with unused dependencies removed.
## Using the tasks package
The linker tasks package ([ILLink.Tasks](https://dotnet.myget.org/feed/dotnet-core/package/nuget/Illink.Tasks)) contains MSBuild targets that become a part of the referencing project's build, using the [mechanism described in the nuget docs](https://docs.microsoft.com/en-us/nuget/create-packages/creating-a-package#including-msbuild-props-and-targets-in-a-package).
When this package is referenced in a project, the publish target is augmented with additional targets that run the linker on the app's assemblies before they are placed in the publish directory.
These targets will run the linker on all managed assemblies that are a part of the app, including dependencies from project references and package references. The linker will attempt to determine which parts of the code (in the project and its dependencies) are unnecessary, and it will remove assemblies or parts of assemblies that it determines to be safe to remove. By default this behavior is fairly conservative: the linker will always keep code in the application and its non-framework dependencies, only removing unused parts of the framework assemblies (this may change in the future as we improve the linker's heuristics).
Even with the current conservative behavior, there may be cases in which the linker removes code that the application expects to be present at runtime. For example, the application may use reflection to load and call code at runtime, and the linker will not be able to catch these cases perfectly. To explicitly tell the linker to keep certain code in the linked output, it is possible to specify additional roots via MSBuild properties and xml root descriptor files.
Root assemblies can be specified with the LinkerRootAssemblies ItemGroup:
```xml
<ItemGroup>
<LinkerRootAssemblies Include="MyAssembly" />
</ItemGroup>
```
This ItemGroup should contain the logical names of assemblies, not the filenames (so the assembly names should not have extensions).
The linker roots can also be specified at a more granular level using xml root descriptor files, whose format is documented in the mono/linker repo. These files should be specified in the LinkerRootDescriptors ItemGroup:
```xml
<ItemGroup>
<LinkerRootDescriptors Include="path/to/rootDescriptor.xml" />
</ItemGroup>
```
## For more control
It is also possible to use the link task directly, which may be useful for more complicated builds. To turn off the default behavior introduced by the package, use:
```xml
<PropertyGroup>
<LinkDuringPublish>false</LinkDuringPublish>
</PropertyGroup>
```
The ILLink task can be invoked just like any other msbuild task. For example, it could be called from a target as follows:
```xml
<Target Name="CustomLinkerExample">
<ILLink AssemblyPaths="@(AssemblyFilesToLink)"
RootAssemblyNames="@(LinkerRootAssemblies)"
RootDescriptorFiles="@(LinkerRootDescriptors)"
OutputDirectory="output"
ExtraArgs="-t -c link -l none" />
</Target>
```
Here, the ItemGroups `AssemblyFilesToLink`, `LinkerRootAssemblies`, and `LinkerRootDescriptors` would be defined elsewhere in the project, and the `ExtraArgs` input consists of flags described in the [mono/linker documentation](https://github.com/mono/linker/blob/master/linker/README). By default, the `RootAssemblyNames` are rooted as if illink had been called with `-a RootAssemblyName1 -a RootAssemblyName2` ... as arguments, but this behavior is subject to change and should not be relied upon.

View file

@ -0,0 +1,75 @@
# Using the .NET IL Linker
The .NET team has built a linker to reduce the size of .NET Core applications. It is built on top of the excellent and battle-tested [mono linker](https://github.com/mono/linker). The Xamarin tools also use this linker.
In trivial cases, the linker can reduce the size of applications by 50%. The size wins may be more favorable or more moderate for larger applications. The linker removes code in your application and dependent libraries that are not reached by any code paths. It is effectively an application-specific dead code analysis.
## Sample
You can test the linker with a sample application:
* [.NET Core self-contained application Docker Production Sample -- using .NET IL Linker](https://github.com/dotnet/dotnet-docker-samples/blob/master/dotnetapp-selfcontained/README.md#build-run-and-publish-the-sample-locally)
For more advanced IL Linker instructions, see [Using IL Linker Advanced Features](linker-instructions-advanced.md).
## Instructions
The instructions assume you are using [.NET Core 2.0](https://github.com/dotnet/core/blob/master/release-notes/download-archive.md) or [.NET Core daily builds](https://github.com/dotnet/core/blob/master/daily-builds.md). You can validate your .NET Core SDK version by typing `dotnet --version`.
1. Select a project to test with. If you don't have one, you can do one of the following:
* Create one with `dotnet new console -o testapp`; `cd testapp`, or
* Clone / download the [.NET Core self-contained application Docker Production Sample -- using .NET IL Linker].
1. Add a NuGet.Config file in the root of your project, using the following:
* `dotnet new nuget`
* Add this line to nuget.config, under `<clear />`: `<add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />`
* The final file should look like [nuget.config](nuget.config).
1. Add a reference to the [latest version of the linker package](https://dotnet.myget.org/feed/dotnet-core/package/nuget/Illink.Tasks) in your .csproj, using the following:
* `dotnet add package ILLink.Tasks -v 0.1.4-preview-906439`
1. Publish the application, using the following:
* `dotnet publish -c release -r <RID> -o out`
* where `<RID>` is one of `win-x64`, `win-x86`, `linux-x64`, `osx-x64` depending the OS that you want to publish for.
* Example: `dotnet publish -c release -r win-x64 -o out`
1. Run the application, with the following and depending the name of the application:
* Windows: `out\testapp`
* Linux/macOS: `./out/testapp`
## Linker Switches
The linker can be controlled with the following commandline switches.
* `/p:LinkDuringPublish=false` -- Disable the linker.
* `/p:ShowLinkerSizeComparison=true` -- Displays a table of size reductions for the application.
You must disable the linker if you want to publish a [framework dependent application](https://docs.microsoft.com/en-us/dotnet/core/deploying/) while you have ILLink.Tasks as a dependency. This behavior will be changed in a later release.
## Determining Code Size Reduction
There are two straightforward approaches to determining the code size reduction provided by the linker.
* Publish an application with the linker enabled and pass the `/p:ShowLinkerSizeComparison=true` flag. The output will describe the benefit. For example:
* `dotnet publish -c release -r osx-x64 -o out /p:ShowLinkerSizeComparison=true`
* Publish an application two different ways:
* `dotnet publish -c release -r linux-x64 -o linkedapp`
* `dotnet publish -c release -r linux-x64 -o notlinkedapp /p:LinkDuringPublish=false`
Note: The `osx-x64` and `linux-x64` runtime IDs are used in the examples above. Feel free to use any runtime ID that you would like, such as `win-x64`.
## Caveats
The linker has the following caveats.
* Currently only supports publishing self-contained applications. It will fail unless you specify a runtime ID.
* It is currently an experimental feature. We intend to productize it in a subsequent .NET Core release.
* Linking only happens during publish, and therefore the linked app needs to be tested after publish, not just after build.
* The linker can and will break some apps that use reflection. See [Using IL Linker Advanced Features](linker-instructions-advanced.md) for more information about managing reflection usage.
### Feedback
Please provide feedback on your use of the linker. We are actively looking for feedback to improve the linker. In particular, we are looking for feedback on the following topics:
* Linker throughput.
* Cases where the linker can be more aggressive.
* Cases where the link is too aggressive and causes applications to fail at runtime.
* The linker provided an excellent result for a large application.
Please create issue with your feedback at either [mono/linker](https://github.com/mono/linker) or [dotnet/core](https://github.com/dotnet/core). Also feel free to contact the team at dotnet@microsoft.com.

6
samples/nuget.config Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="dotnet-core" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
</packageSources>
</configuration>