- Verify that .NET 6.0 is installed. To do this, open a command prompt and run the command
dotnet --info
-
If you have Preview version of .NET 6 then uninstall it and install stable one.
-
Install the .NET WebAssembly build tools. Run the following command from an elevated command prompt (Administrative mode).
dotnet workload install wasm-tools
- To enable WebAssembly AOT compilation in your Blazor WebAssembly project, add the following property to your project file.
<RunAOTCompilation>true</RunAOTCompilation>
- Verify that your app uses correct packages.
- Create Publish Profile.
- Publish. It will take time.
Try to change publishing settings If it does not work.
Here is how to add and run published website.
To verify that your application is actually running in AOT, please open the browser developer tools (F12), go to the Network tab, force refresh (Ctrl + F5) and check the size of the dotnet.wasm file. It is supposed to be several times bigger when running in AOT (non AOT wasm size is < 3MB). If that's not the case then most probably it is not running in AOT.
The concept of "AOT" exists in 2 contexts: desktop/mobile
applications (CoreRT, NativeAOT, etc.) and Blazor WebAssembly
. The 2 work in a very different way. Here is the explanation how both work.
- Without AOT:
VS will compile the C# to IL (Intermediate Language) (.DLLs) When app is launched, the JIT ("Just-In-Time" compilation) (which comes from .NET Framework runtime, or NET Core...) will convert IL into binary executable code.
- With AOT:
VS will compile the C# to binary executable code.
=> No much difference in performance during application execution (on Windows) between non-AOT and AOT because JIT will compile to binary code on Windows when the application starts.
- Without AOT:
VS will compile the C# to IL (Intermediate Language) (.DLLs). There is no JIT in the browser. "dotnet.wasm" is the only binary executable that gets downloaded. "dotnet.wasm" loads the IL of the DLLs and "interprets" them. Negative impact on performance because interpretion layer.
- With AOT:
VS will compile the C# to binary executable code (files are bigger than IL).
=> Much better performance with AOT during application execution. As far as initial loading is concerned, AOT needs to download bigger files, so initial load is slower, but then the files are cached by the browser, so when coming back go the app, initial time is the same as non-AOT.
Note: when C# reflection is used in the application, or "dynamic" types, the execution will temporaroly fall back to "interpreted" mode, which means that the runtime performance of AOT will be nearly the same as non-AOT. We are working to remove all those types, so as to really benefit from AOT.
We recommend clients to already publish the application in AOT mode so that you can see the current state with AOT. Please keep in mind that further AOT performance improvement will come as we remove more types and we make further optimizations, to leverage AOT and not fall back to "interpreted" mode too often.
For AOT to work you need to publish your app (not merely starting it with Visual Studio). You can publish it to a local folder and point IIS to it (make sure to select "self-contained" in the publishing options).
In some cases publish fails with an error similar to following.
Exit code: -1073741571
C:\Program Files\dotnet\packs\Microsoft.NET.Runtime.WebAssembly.Sdk\6.0.0-preview.5.21301.5\Sdk\WasmApp.targets(145,5): Error : Precompiling failed for
This error happens when mono-aot-cross.exe tries to convert llvm method which is very big and most probably it will be one of InitializeComponent functions.
One of the techniques to find how many presumably big functions exist in the project would be to search .xaml.g.cs generated files that are bigger than 1MB. The solution can be to split InitializeComponent method to several methods, exclude .xaml file and include generated .xaml.g.cs one one.
To find out which exact function causes the issue follow the steps.
- Set MSBuild output verbosity to
Diagnostic
level
Tools -> Options -> Build And Run - Publish the project again and wait until it fails.
- Find the last execution of mono-aot-cross.exe and run it with cmd.exe adding -v (verbosity) option.
The command will look similar to this
"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.browser-wasm\6.0.0-preview.5.21301.5\Sdk\..\tools\mono-aot-cross.exe" -v --debug --llvm "--aot=no-opt,static,direct-icalls,deterministic,dwarfdebug,llvm-path=C:\Program Files\dotnet\packs\Microsoft.NET.Runtime.Emscripten.2.0.12.Sdk.win-x64\6.0.0-preview.5.21281.1\tools\bin\,llvmonly,interp,asmonly,llvm-outfile=MyLib.dll.bc" MyLib.dll
There is also MONO_PATH environment variable that need to be set before running the command. It will be shown in Visual Studio output as well.
If command succeeded you will see some statistic about converted methods etc. If it fails you will see the last method it was trying to convert and fails.
converting llvm method void MyApp.Views.MyEditServiceCodeDetailsView:InitializeComponent ()
It turns out that the bug is not directly related to the function size but the amount of lines that are adding objects to the List
var parents_8ce7371e02ef489e9893174ae5411181 = new global::System.Collections.Generic.List<global::System.Object>();
parents_8ce7371e02ef489e9893174ae5411181.Add(TextBlock_c0999e8200734224a58a6861f2e68cfb);
parents_8ce7371e02ef489e9893174ae5411181.Add(StackPanel_8465a697e0a94b7ca24107bb115330f6);
parents_8ce7371e02ef489e9893174ae5411181.Add(StackPanel_c7b62394168e4c7da9eabfbeb9d6a833);
Having many .Add lines in generated .g.cs code increases probability for mono-aot-cross.exe to fail.
As a workaround, OpenSilver compiler can be adapted to generate local function inside InitializeComponent() and add list items inside the function.
var parents_8ce7371e02ef489e9893174ae5411181 = new global::System.Collections.Generic.List<global::System.Object>();
void parents_8ce7371e02ef489e9893174ae5411181_func() {
parents_8ce7371e02ef489e9893174ae5411181.Add(TextBlock_c0999e8200734224a58a6861f2e68cfb);
parents_8ce7371e02ef489e9893174ae5411181.Add(StackPanel_8465a697e0a94b7ca24107bb115330f6);
parents_8ce7371e02ef489e9893174ae5411181.Add(StackPanel_c7b62394168e4c7da9eabfbeb9d6a833);
};
parents_8ce7371e02ef489e9893174ae5411181_func();
To achieve that we need 3 lines of code.
- Open
GeneratingCSharpCode.cs
file - Find the following line
while (elementForSearch != null && elementForSearch.Parent != null)`
- Add this line before while statement
stringBuilder.AppendLine("void " + nameForParentsCollection + "_func() {");
- Add this two lines after while statment
stringBuilder.AppendLine("};");
stringBuilder.AppendLine(nameForParentsCollection + "_func();");
After running published website the following error can appear in browser console.
That means some of MIME types are not served by IIS. To solve that go to MIME Types and add .dat and .blat types both application/octet-stream.
The concept of "AOT" exists in 2 contexts: desktop/mobile
applications (CoreRT, NativeAOT, etc.) and Blazor WebAssembly
. The 2 work in a very different way. Here is the explanation how both work.
- Without AOT:
VS will compile the C# to IL (Intermediate Language) (.DLLs) When app is launched, the JIT ("Just-In-Time" compilation) (which comes from .NET Framework runtime, or NET Core...) will convert IL into binary executable code.
- With AOT:
VS will compile the C# to binary executable code.
=> No much difference in performance during application execution (on Windows) between non-AOT and AOT because JIT will compile to binary code on Windows when the application starts.
- Without AOT:
VS will compile the C# to IL (Intermediate Language) (.DLLs). There is no JIT in the browser. "dotnet.wasm" is the only binary executable that gets downloaded. "dotnet.wasm" loads the IL of the DLLs and "interprets" them. Negative impact on performance because interpretion layer.
- With AOT:
VS will compile the C# to binary executable code (files are bigger than IL).
=> Much better performance with AOT during application execution. As far as initial loading is concerned, AOT needs to download bigger files, so initial load is slower, but then the files are cached by the browser, so when coming back go the app, initial time is the same as non-AOT.
Note: when C# reflection is used in the application, or "dynamic" types, the execution will temporaroly fall back to "interpreted" mode, which means that the runtime performance of AOT will be nearly the same as non-AOT. We are working to remove all those types, so as to really benefit from AOT.
We recommend clients to already publish the application in AOT mode so that you can see the current state with AOT. Please keep in mind that further AOT performance improvement will come as we remove more types and we make further optimizations, to leverage AOT and not fall back to "interpreted" mode too often.