aspnet_compiler is slow and does not allow/support parallel compilation internally. It was never updated for performance, or for basic parallel compilation, and is "legacy junk" at this point. It is also not possible to run multiple instances concurrently on the same site. As aspnet_compiler generates many temp files, using a faster drive may help: however, even with a fastest SSD, the CPU is significantly under-utilized.
Using Roslyn can speed up compilation >2x over the .NET Framework provided compiler, and is the only way I've found to improve performance of 'the core' compilation performance. It also improves site warmup speed as a result of faster compilation.
Anecdotal performance when using Roslyn1,
- MSBuild compiles 'two hundred assemblies' in ~5 minutes
- aspnet_compiler compiles website in ~40m (was ~100m before Roslyn)
I strong recommend using Rosyln, even if this doesn't make the site compilation "fast"; it also allows access to C#7 features from views and ASPX files. Ensure that Rosyln is correctly using the VBCSCompiler.exe service model.
It is more efficient, assuming there are no compilation errors to dig through, to compile an IIS site once instead of once for each application in the site.
For CI/CD pipelines, use release builds if possible. Having both release builds and release web.config settings (ie. debug="false"
) results in faster compilation. Results appear to be more mixed when using release web.config settings with debug builds. YMMV.
Anecdotal performance when using release vs debug builds,
- CI/CD server build for debug in ~35m
- CI/CD server build for release in ~25m
For local development, use in-place compilation if possible. In-place compilation allows the option for incremental compilation, where only modified files are recompiled by default.
Anecdotal performance when using an in-place compilation for local development,
- first compilation in-place in ~40m
- second compilation with no files changed in ~10s
An incremental in-place compilation can result in some errors, such as if ABI in libraries breaks; a full recompilation (-c
option) will clear the issue. CI/CD builds should probably always perform a full compilation.
Update "JScript" files to C#/VB.NET
Neither Roslyn nor CodeDom apply for this legacy language, and appearance of such pages must run through some old compiler process which is much, much, slower.
Unfortunately, aspnet_compiler also lacks sufficient output generation to determine which part(s) of a site are taking the most time to compiler.
1The website I am dealing with currently has 189 compiled assemblies, 97 .INC and .ASP classic files, 1580 .ASPX files (mix of C# WebForms and 'script'), and 1221 .CSHTML files. Final .COMPILED file count in IIS is 5788. Performance improvements will likely vary based on a number of factors including types of files and dependency graphs.
Cliff notes on setting up Roslyn for aspnet_compiler:
Download the Rosyln CodeDom package (Microsoft.CodeDom.Providers.DotNetCompilerPlatform). This includes both the CodeDom and a build of Roslyn.
Ensure the Roslyn directory in the package ends up in bin/roslyn
, however such fits into the build system. Here there is a manual pre-build copy step. Likewise, the CodeDom bridge assembly must be in bin/
.
Update the system.codedom/compilers
section in the web.config file as shown in the example. If not using VB.NET, that compiler entry can be removed along with the VB-specific Roslyn analyzers. (Note that below I've set /langversion:7
for C#7 features, the highest set officially supported in any .NET Framework target.)
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs"
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
warningLevel="4" compilerOptions="/langversion:7 /nowarn:1659;1699;1701"/>
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb"
type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\"Web\" /optionInfer+"/>
</compilers>
</system.codedom>
Verifying that Roslyn is invoked correctly:
Kick off a site build. During compilation there should be many csc.exe
instances launched. However, these executable instances should not take up much memory. Instead, there should be another executable called VBCSCompiler.exe
that runs from the bin/roslyn
directory (Visual Studio may spawn it's own Roslyn compilation server as well). This executable is the compilation server that the csc.exe executables send requests to. Having the long-running compilation VBCSCompiler.exe
server model is essential for the largest performance benefits of Roslyn as it avoids the relatively costly continual respawning of the compiler itself. If the compilation server cannot be started, the csc.exe
processes will fallback to do the heavy compilations themselves.
<MvcBuildViews>true</MvcBuildViews>
? I know it serves a useful purpose, but you may not need it to be enabled always, and it really slows down the build. If present, try setting it tofalse
, this may help especially during the development cycle with frequent builds in order to run the application after a small change. – Tommyetommyrotfalse
locally. But it is undefined on PR/CI builds. – Ardellearden