{"id":29533,"date":"2020-08-31T13:09:11","date_gmt":"2020-08-31T20:09:11","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=29533"},"modified":"2020-08-31T13:09:11","modified_gmt":"2020-08-31T20:09:11","slug":"app-trimming-in-net-5","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/app-trimming-in-net-5\/","title":{"rendered":"App Trimming in .NET 5"},"content":{"rendered":"<h2>\n    What is trimming\n<\/h2>\n<p>\n    One of the big differences between .NET Core and .NET Framework is that .NET Core supports self-contained deployment \u2013 everything needed to run the application is bundled together. It doesn\u2019t depend on having the framework separately installed. From an\n    application developer perspective, this means that you know exactly which version of the runtime is being used, and the installation\/setup is easier. The downside is the size \u2013 it pulls along a complete copy of the runtime &amp; framework.\n<\/p>\n<p>\n    To resolve the size problem, we introduced an option to trim unused assemblies as part of publishing self-contained applications. We first made <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/core\/deploying\/trim-self-contained\">assembly trimming<\/a>    available as part of .NET Core 3.0. It is also sometimes called the &#8220;assembly linker&#8221;. Optionally during the publish process, a trim phase occurs which does an exhaustive walk of the code paths identifying the assemblies that are used by the code.\n    It will then only package those assemblies into the app, thereby reducing the size of the application.\n<\/p>\n<p>\n    In .NET 5, we are taking this further, and cracking open the assemblies, and removing the types and members that are not used by the application, further reducing the size. For example, for a Hello World app (<code>dotnet new\n    console<\/code>), the sizes of assemblies are:\n<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\" width=\"504\">\n<tbody>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    <strong>Assembly<\/strong>\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    <strong>Trimmed (k)<\/strong>\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    <strong>Member Trimmed (k)<\/strong>\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    System.Collections.Concurrent.dll\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    184.9\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    23.5\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    System.Collections.dll\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    85.5\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    15.5\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    System.Collections.Immutable.dll\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    171.5\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    20.0\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    System.Console.dll\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    61.5\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    31.5\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    System.Diagnostics.StackTrace.dll\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    33.9\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    8.5\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    System.IO.Compression.dll\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    88.5\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    33.0\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    System.IO.FileSystem.dll\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    84.5\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    18.5\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    System.IO.MemoryMappedFiles.dll\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    28.0\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    22.5\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    System.Linq.dll\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    128.0\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    &#8211;\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    System.Private.CoreLib.dll\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    9,127.4\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    1,781.5\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    System.Private.Uri.dll\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    &#8211;\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    64.5\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    System.Reflection.Metadata.dll\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    432.0\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    109.5\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    System.Runtime.CompilerServices.Unsafe.dll\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    7.0\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    &#8211;\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    System.Runtime.Serialization.Formatters.dll\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    112.5\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    84.5\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td nowrap=\"\" valign=\"bottom\">\n<p>\n                    Total\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    10,545.1\n                <\/p>\n<\/td>\n<td nowrap=\"\" valign=\"bottom\">\n<p align=\"right\">\n                    2,213.0\n                <\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\n    * As the commercials say, \u201cresults not typical\u201d. The size reductions above are probably better than can be expected for most real apps \u2013 the app simply outputs \u201cHello World!\u201d, so makes minimal usage of most of the assemblies. The size above is just for\n    the framework assemblies, it doesn\u2019t account for the runtime which is a fixed cost that applies to every app.\n<\/p>\n<p>\n    Trimming sounds great, but as with most good things, there is a catch. The trimming does a static analysis of the code and therefore can only identify types and members when they are referenced from code. However .NET offers a great deal of dynamism,\n    typically depending on reflection. For example, Dependency Injection in ASP.NET Core uses reflection to select appropriate constructors. This is largely transparent to the static analysis, so it needs to either be told about the required types or\n    be able to detect common dynamism patterns \u2013 otherwise it will trim away code that is needed by the application which will result in runtime crashes..\n<\/p>\n<h2>\n    Assembly-level trimming\n<\/h2>\n<p>\n    To assembly-level trim, use the dotnet publish command either without a trim mode, or use <code>&lt;TrimMode&gt;CopyUsed&lt;\/TrimMode&gt;<\/code> in the project file:\n<\/p>\n<pre>\r\n&lt;Project Sdk=\"Microsoft.NET.Sdk\"&gt;\r\n    &lt;PropertyGroup&gt;\r\n        &lt;OutputType&gt;Exe&lt;\/OutputType&gt;\r\n        &lt;TargetFramework&gt;net5.0&lt;\/TargetFramework&gt;\r\n        &lt;RuntimeIdentifier&gt;win10-x64&lt;\/RuntimeIdentifier&gt;\r\n        &lt;PublishTrimmed&gt;true&lt;\/PublishTrimmed\r\n        &lt;TrimMode&gt;CopyUsed&lt;\/TrimMode&gt;\r\n    &lt;\/PropertyGroup&gt;\r\n&lt;\/Project&gt;\r\n<\/pre>\n<p>\n    Or on the command line:\n<\/p>\n<pre>\r\ndotnet publish -r win10-x64 -p:PublishTrimmed=True [-p:TrimMode=CopyUsed]\r\n<\/pre>\n<h2>\n    Member-Level Trimming\n<\/h2>\n<p>\n    .NET 5 can take it two levels further and remove types and members that are not used. This can have a big effect where only a small subset of an assembly is used \u2013 for example, the console application above. Member-level trimming has more risk than assembly\n    level trimming, and so is being released as an experimental feature, <em>that is not yet ready for mainstream adoption<\/em>. With assembly level trimming, its more obvious when a required assembly is missing, with member level trimming you need\n    to have exhaustive testing of the app to ensure that nothing has been trimmed that could be required.\n<\/p>\n<p>\n    The default for .NET 5 is to use the same conservative assembly level trimming as .NET Core 3. Further gains can be made by enabling Member-level trimming by putting TrimMode=Link in the project file\n<\/p>\n<pre>\r\n&lt;Project Sdk=\"Microsoft.NET.Sdk\"&gt;\r\n    &lt;PropertyGroup&gt;\r\n        &lt;OutputType&gt;Exe&lt;\/OutputType&gt;\r\n        &lt;TargetFramework&gt;net5.0&lt;\/TargetFramework&gt;\r\n        &lt;RuntimeIdentifier&gt;linux-x64&lt;\/RuntimeIdentifier&gt;\r\n        &lt;PublishTrimmed&gt;true&lt;\/PublishTrimmed\r\n        &lt;TrimMode&gt;Link&lt;\/TrimMode&gt;\r\n    &lt;\/PropertyGroup&gt;\r\n&lt;\/Project&gt;\r\n<\/pre>\n<p>\n    Or passing <code>-p:PublishTrimmed=True<\/code> and\n    <code>-p:TrimMode=Link <\/code>to the dotnet publish command. For example the following sizes are for the <a href=\"https:\/\/github.com\/microsoft\/reverse-proxy\/tree\/master\/samples\/ReverseProxy.Sample\">YARP sample project<\/a>:\n<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p>\n                    <strong>Trim Mode<\/strong>\n                <\/p>\n<\/td>\n<td valign=\"top\">\n<p>\n                    <strong>Self Contained Linux x64 (MB)<\/strong>\n                <\/p>\n<\/td>\n<td valign=\"top\">\n<p>\n                    <strong>Single File Linux x64 (MB)<\/strong>\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>\n                    No Trimming\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    78.9\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    65.3\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>\n                    Assembly Trimming\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    39.7\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    26.1\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>\n                    Member Trimming\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    31.5\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    17.9\n                <\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\n    Member level-trimming also strips the\n    <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/core\/whats-new\/dotnet-core-3-0#readytorun-images\">ReadyToRun<\/a> (R2R) code from the assemblies \u2013 the results are smaller, but application startup will be slower. See further below for more details.\n<\/p>\n<h2>\n    Dynamic code pattern analysis\n<\/h2>\n<p>\n    In the context of trimming applications, there are two main concerns:\n<\/p>\n<ul>\n<li> Any code and data that is <em>used<\/em> by the application <em>must be preserved<\/em> in the application.<\/li>\n<li>Any code or data that are <em>not used<\/em> by the application <em>should be removed<\/em> from the application.\n    <\/li>\n<\/ul>\n<p>\n    Note the difference in &#8220;must&#8221; and &#8220;should&#8221; between those two concerns. An application that works is preferred over a smaller application that doesn&#8217;t. Therefore, it is critical that any necessary code must be preserved in the application. The problem\n    with .NET is that it\u2019s not always obvious as to what the necessary code is, particularly when it comes to dynamic code patterns \u2013 where the exact code that will executed is determined at runtime.\n<\/p>\n<p>\n    The big challenge for trimming is whether ILLink (the tool that does the trimming) can correctly discover all the code that can be reached by the application. The trimmer does a static analysis of the code, walking each of the code paths to determine\n    the scope of what can be reached. There are a number of ways that dynamic code patterns can be used in the application that causes problems for trimming, the primary examples are:\n<\/p>\n<ul>\n<li>\n        Using reflection &#8211; assemblies, types and members can be iterated over, or queried for by name, and then invoked. Eg<br>\n        <code>Type.GetType(\"Foo\").GetMethod(\"Bar\").ReturnType.GetMethod(\"Baz\")<\/code>,<\/li>\n<li>\n        Dynamic code loading \u2013 At runtime, assemblies are loaded and code within them executed. This is problematic if those assemblies have not been packaged within the app, or code that they depend on has been trimmed.\n    <\/li>\n<\/ul>\n<p>\n    The problem with the dynamic patterns is that the trimmer cannot discover which types and members are going to be used at runtime, and so may be overly aggressive in trimming them out, which could result in catastrophic errors when they are used at runtime.\n    For example, at runtime all the types that implement a specific interface could be queried for and instantiated. If those types are not used elsewhere in code that is reached, then they will be trimmed by the publish command. Later in the app\u2019s execution\n    a type that was expected will not be there. This could happen at startup, or only in an infrequently used code path of the app \u2013 so the omission might not be caught in a simple test, and may not show up in unit tests unless they use the types in exactly\n    the same pattern. Unit tests can also mask problems if they reference types or members directly, changing what the trimmer think is reachable.\n<\/p>\n<p>\n    This similar problem was faced by the .NET Native compiler used for Windows Store applications. The .NET Native compiler attempted to recognize examples of reflection, such as dynamic in C# and <code>Type.GetType(\"Foo\").GetMethod(\"Bar\").ReturnType.GetMethod(\"Baz\")<\/code>,\n    and try to make it just work. Despite significant effort, it was far from perfect, and the long tail of bugs meant that user assemblies could not be trimmed for reliability reasons. As .NET Native only did the native compilation as part of a retail\n    build, issues did not show up for the \u201cF5\u201d inner loop of code-compile-run-debug, and only when the app was published, causing developer angst.\n<\/p>\n<p>\n    With .NET 5+, we want to take a different approach \u2013 we want the trimmer to provide predictable results to the developer: If the trimmer can\u2019t be assured of the correctness of the trim, it will provide warnings based on the code used. The problem for\n    dynamic code is not that it\u2019s broken in a trimmed environment, but that the trimmer needs to know which assemblies\/types\/members will be used. A set of attributes have been added that enables code to be <a href=\"https:\/\/github.com\/mono\/linker\/blob\/master\/docs\/design\/reflection-flow.md\">annotated<\/a>    to tell the trimmer what code should be included, or which API usage should prevent trimming.\n<\/p>\n<table cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p>[<a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/blob\/404d81767784552b0a148cb8c437332ebe726ae9\/src\/Shared\/CodeAnalysis\/DynamicallyAccessedMembersAttribute.cs#L29\">DynamicallyAccessedMembers<\/a>]<\/p>\n<\/td>\n<td valign=\"top\">\n<p>\n                    Applied to instances of System.Type (or strings containing a type name) to tell the trimmer which members of the type will be accessed dynamically.\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>\n                    [<a href=\"https:\/\/github.com\/dotnet\/runtime\/blob\/a5159b1a8840632ad34cf59c5aaf77040cb6ceda\/src\/libraries\/System.Private.CoreLib\/src\/System\/Diagnostics\/CodeAnalysis\/UnconditionalSuppressMessageAttribute.cs#L21\">UnconditionalSuppressMessage<\/a>]\n                <\/p>\n<\/td>\n<td valign=\"top\">\n<p>\n                    Used to suppress a warning message from the trimmer if the use case is known to be safe. For example, if an &#8220;Equals&#8221; method is written by retrieving all the fields on a type and looping over them comparing each field. If a field is unused, and therefore\n                    trimmed, there is no need to compare that field for equality.\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>\n                    [<a href=\"https:\/\/github.com\/dotnet\/runtime\/blob\/6072e4d3a7a2a1493f514cdf4be75a3d56580e84\/src\/libraries\/System.Private.CoreLib\/src\/System\/Diagnostics\/CodeAnalysis\/RequiresUnreferencedCodeAttribute.cs#L15\">RequiresUnreferencedCode<\/a>]\n                <\/p>\n<\/td>\n<td valign=\"top\">\n<p>\n                    Tells the trimmer that the method is incompatible with trimming and so warnings should be presented where the method is called. This will suppress warnings for the code paths that are called by this method reducing the noise, and making it clearer to\n                    the developer which methods they call are problematic.\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>\n                    [<a href=\"https:\/\/github.com\/dotnet\/runtime\/blob\/6072e4d3a7a2a1493f514cdf4be75a3d56580e84\/src\/libraries\/System.Private.CoreLib\/src\/System\/Diagnostics\/CodeAnalysis\/DynamicDependencyAttribute.cs#L21\">DynamicDependency<\/a>]\n                <\/p>\n<\/td>\n<td valign=\"top\">\n<p>\n                    Specifies an explicit dependency from a method to other code, if the method is preserved the other code will also be preserved. Used in cases where the dependency is not expressed by the method, but rather by external factors, like native code.\n                <\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\n    We\u2019ll go into further detail on these attributes and how to use them in a subsequent blog post.\n<\/p>\n<p>\n    Due to the ambiguous nature of detecting issues, the trimmer tends towards reporting false positive issues when the code may be safe, rather than allowing for problems at runtime. The trimmer warns on all the (potential) problems it finds in the assemblies\n    consumed by the app &#8211; this can yield hundreds of issues for the simplest of projects.\n<\/p>\n<p>\n    For .NET 5 we have a couple of mitigations:\n<\/p>\n<ol type=\"a\">\n<li> Issues are reported as warnings.<\/li>\n<li>\n        By default, the trimmer will suppress the trim warnings as they are not actionable by app developers when they are coming from the .NET Framework or other libraries.\n        <br \/>\n        <code>&lt;SuppressTrimAnalysisWarnings&gt;false&lt;\/SuppressTrimAnalysisWarnings&gt;<\/code> can be used to show these warnings.\n    <\/li>\n<\/ol>\n<h2>\n    Testing Trimmed Apps\n<\/h2>\n<p>\n    When an app is trimmed, it is essential to perform exhaustive end-to-end testing of the published version of the application. Unit tests are often not useful because they change the environment too much and the trimming will work differently. Testing\n    of a trimmed app should be driven externally rather than by code within the app.\n<\/p>\n<h2>\n    Trimming and Ready2Run\n<\/h2>\n<p>\n    Ready2Run (R2R) is an AOT technology that native compiles code at build time for faster app startup. The assemblies in the .NET Libraries include R2R native code to improve startup for all scenarios. R2R is a tradeoff favoring performance over size. Using\n    R2R, methods don\u2019t need to be JIT before they are used for the first time. Therefore R2R has most effect on startup, but there should be little difference once the process is warmed up, for example when processing web requests.\n<\/p>\n<p>\n    When an application is trimmed, the assemblies may need to be modified \u2013 even for assembly level trimming. When trimming if type-forwarder assemblies are eliminated, the remaining assemblies need to be modified to redirect the forwards. When an assembly\n    is re-written due to trimming, the pre-built R2R data is removed, thereby optimizing size over performance.\n<\/p>\n<p>\n    Using <code>&lt;TrimMode&gt;Link&lt;\/TrimMode&gt;<\/code> will remove R2R data, unless its specified to be added by using the <code>&lt;PublishReadyToRun&gt;True&lt;\/PublishReadyToRun&gt;<\/code> option as part of dotnet publish.\n<\/p>\n<p>\n    The relative sizes for the console app template with different modes of trimming, with and without R2R.\n<\/p>\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"0\">\n<tbody>\n<tr>\n<td valign=\"top\">\n<p>\n                    <strong>Trim Mode<\/strong>\n                <\/p>\n<\/td>\n<td valign=\"top\">\n<p>\n                    <strong>Self Contained Linux x64 (MB)<\/strong>\n                <\/p>\n<\/td>\n<td valign=\"top\">\n<p>\n                    <strong>Single File Linux x64 (MB)<\/strong>\n                <\/p>\n<\/td>\n<td valign=\"top\">\n<p>\n                    <strong>Self Contained Win10 x64 (MB)<\/strong>\n                <\/p>\n<\/td>\n<td valign=\"top\">\n<p>\n                    <strong>Single File Win10 x64 (MB)<\/strong>\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>\n                    No Trimming\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    78.9\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    65.3\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    64.1\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    57.6\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>\n                    Assembly Trimming\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    39.7\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    26.1\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    24.4\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    17.9\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>\n                    Assembly Trimming + R2R\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    41.7\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    28.1\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    26.4\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    19.8\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>\n                    Member Trimming\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    31.5\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    17.9\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    16.2\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    9.7\n                <\/p>\n<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">\n<p>\n                    Member Trimming + R2R\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    34.5\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    20.8\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    19.4\n                <\/p>\n<\/td>\n<td valign=\"bottom\">\n<p>\n                    12.9\n                <\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\n    As the application author, you should decide whether you want to favor size versus startup performance.\n<\/p>\n<h2>\n    Making .NET Trimmable\n<\/h2>\n<p>\n    .NET has a long history, and dynamic code is prevalent throughout the framework and library ecosystem. The exercise to annotate the .NET libraries has begun, but will probably take multiple releases to complete \u2013 we are prioritizing based on usage.\n<\/p>\n<p>\n    In addition to using attribution, we are looking at using\n    <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/introducing-c-source-generators\/\">\n        source generators\n    <\/a> to move functionality from runtime reflection to build-time code generation. This not only improves performance, but means that the generated code doesn\u2019t use reflection so the trimmer can walk it to determine what needs to be kept. Good\n    examples of where source generators can be used to aid trimming include:\n<\/p>\n<ul>\n<li> Dependency Injection (DI) \u2013 the source generator can figure out which types implement the given interfaces, and generate static code that does the hookup rather than relying on reflection at runtime.\n    <\/li>\n<li> Object serialization \u2013 serializers typically use reflection to walk the properties and fields of objects to be serialized, examining annotations if necessary. Sometimes they even do dynamic method creation at runtime to improve the performance of\n        many serializations. A source generator can create the equivalent code at build time, which means less runtime work, and it\u2019s linker friendly.\n    <\/li>\n<\/ul>\n<p>\n    The library ecosystem, such as nuget package authors, will also have a role to play in making their assemblies trimmable. We\u2019ll go into more details in a subsequent post.\n<\/p>\n<h2>\n    Removing Framework Features\n<\/h2>\n<p>\n    One of the differences between the Mono and .NET Core frameworks is that Mono was designed for mobile applications where package size is a concern; .NET framework is fuller featured and so larger. In the move to consolidate the two we needed a mechanism\n    to retain the size benefits of Mono while using the larger framework.\n<\/p>\n<p>\n    One mechanism we are adding that will allow us to conditionally remove code from applications is\n    <a href=\"https:\/\/github.com\/dotnet\/runtime\/blob\/master\/docs\/workflow\/trimming\/feature-switches.md#available-feature-switches\">\n        feature switches\n    <\/a> . Using this mechanism, an SDK (like Xamarin) or the developer can decide if a feature that would normally be available in the libraries can be removed, both at runtime and during linking. An example of an existing feature switch we have\n    today is\n    <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/core\/run-time-config\/globalization\">\n        InvariantGlobalization\n    <\/a> . We will add more switches to the libraries to allow large, optional pieces of code to be removed by the trimmer.\n<\/p>\n<h2>\n    Blazor Web Assemblies\n<\/h2>\n<p>\n    One of the biggest needs for size reduction is in the context of Blazor web assemblies. The\n    <a href=\"https:\/\/devblogs.microsoft.com\/aspnet\/blazor-webassembly-3-2-0-now-available\/\">\n        3.2 release of Blazor\n    <\/a> in May was based on the Mono framework. The .NET 5 release will use the .NET 5 framework, which is larger, and we didn\u2019t want to regress the size of Blazor Apps.\n<\/p>\n<p>\n    The following trimming approach is being taken for Blazor Apps in .NET 5:\n<\/p>\n<ul>\n<li>\n        Assemblies from the shared framework\/runtimepack get member-level trimming\n    <\/li>\n<li>\n        Microsoft.Extensions.* assemblies get type-level trimming from TrimMode=Link\n    <\/li>\n<li>\n        Assemblies from Microsoft.AspNetCore.* are trimmed via hand generated XML file\n    <\/li>\n<li>\n        All other assemblies are not trimmed\n    <\/li>\n<\/ul>\n<p>\n    Blazor apps are also configured to disable the following framework features:\n<\/p>\n<ul>\n<li>\n        EventSourceSupport\n    <\/li>\n<li>\n        EnableUnsafeUTF7Encoding\n    <\/li>\n<li>\n        HttpActivityPropagationSupport\n    <\/li>\n<li>\n        DebuggerSupport (for retail builds)\n    <\/li>\n<\/ul>\n<p>\n    And enable:\n<\/p>\n<ul>\n<li>\n        UseSystemResourceKeys\n    <\/li>\n<\/ul>\n<p>\n    This results in Blazor apps with comparable sizes while using the fuller framework of .NET 5.\n<\/p>\n<p>\n    In the next post we will talk about how you can use attributes and xml files to further tweak the trimming operation.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Trimming is an option as part of publishing self contained apps in .NET Core. This post goes into more detail about the trimming options in .NET 5 including type and member level trimming.<\/p>\n","protected":false},"author":8635,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685],"tags":[],"class_list":["post-29533","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet"],"acf":[],"blog_post_summary":"<p>Trimming is an option as part of publishing self contained apps in .NET Core. This post goes into more detail about the trimming options in .NET 5 including type and member level trimming.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/29533","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/8635"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=29533"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/29533\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=29533"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=29533"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=29533"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}