dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
34.5k stars 9.75k forks source link

Blazor WebAssembly AOT compilation #5466

Closed danroth27 closed 2 years ago

danroth27 commented 6 years ago

Compile everything to WebAssembly

This work is going to be handled in phases. We're starting from:

Related .NET 6 users stories:

danroth27 commented 6 years ago

Follow up on status of mono-wasm

danieldegtyarev commented 6 years ago

Do you know an issue where we can track progress on this?

danieldegtyarev commented 6 years ago

Current interpreter mode is very slow. https://dotnetfiddle.net/JUpsRT

.NET 300 ms WASM: 13155 ms

danroth27 commented 6 years ago

Yup, we think this more a result of nothing being optimized yet than an indication that we should move to AoT at this point, but we'll know more as the IL interpreter and AoT support matures.

stefan-schweiger commented 6 years ago

@danroth27 From what I can tell the performance difference between the Mono IL interpreter and the current mono.wasm version is about 5-10x. Overall mono.wasm is about 50-80x and the IL interpreter about 10x slower than a native .NET Core console application.

So the interpreter is currently still not very fast overall and WebAssembly adds even more overhead on top of that.

I would assume that AOT probably still has better chances of speeding things up, but you are right that it's most likely too soon to rule out the interpreter, as most web applications don't even have such high performance demands and will most likely be fine with an interpreted version.

csnewman commented 6 years ago

AOT being more efficient doesn't only matter to intensive applications. It also matters for mobile and other low end platforms, especially when you consider battery usage.

TheFanatr commented 5 years ago

Is there currently any feasible way to enable AoT compilation for Blazor projects? I keep reading blog posts that claim Blazor already has an AoT mode; if this is true, could someone please point to an article explaining how to enable it?

stefan-schweiger commented 5 years ago

@TheFanatr the AOT compilation is dependent on mono supporting this feature. As far as I can tell the last update on the topic was from @migueldeicaza in the Blazor Gitter.

Miguel de Icaza (2018-06-08 19:01): We are working on it. What happened is this: The interpreter is using “emscripten” that has a fairly complete libc that we can rely on. And our AOT engine was built on pure LLVM, which requires from us to write the full libc. The latter is the ideal place, because we get native linkers and immedite llvm support and so on. But woudl send us down the path of having to write that libc. So for the short term, we are moving to emscripten the AOT compiler, make sure that we keep the compatibility. The downside though is that the emscripten tooling is older, so many of the better pieces like the LLVM linker are not available. So we are working through those things. Basically, we had something done, but we had to start from scratch to work with what we have without forving us to write and emulate everything on our own. We could have attempted to blend those two, but that is also a major effort. So we think we can do emscripten for now via some artful hacks here and there.

So in short, they are working on it, but there isn't a good option to do this with the current public builds.

stavroskasidis commented 5 years ago

Some pretty great progress has just been reported in CoreRT !!

https://github.com/dotnet/corert/issues/4659#issuecomment-425690500

plasticalligator commented 5 years ago

anyone have an idea on what's blocking this? i might be willing to put in a few trillion manhours to see AOT happen sooner than later now that clientside blazor has been committed to by the powers that be at Microsoft. AOT is going to be really important if we want to develop ( excellent ) PWAs with Blazor.

danroth27 commented 5 years ago

@honkmother AoT is being worked on by the mono.wasm folks in the https://github.com/mono/mono repo. I'm sure they would be thrilled to have your help! This issue tracks consuming their work once it is ready.

plasticalligator commented 4 years ago

I think I have a somewhat related question but it isn't well thought out and badly worded- I've been experimenting with ILRepack and Blazor, and managed to package most of the dlls together into a single monolithic blob. Do you guys intend to at any point package the common libraries together as a single dll?

danroth27 commented 4 years ago

@honkmother We don't currently have any concrete plans to do so, but we'd be interested to hear the results of your experimentation.

plasticalligator commented 4 years ago

I was able to merge most of them together by just using ILRepack on the /dist/bin/ output, and including System.Core.dll. Startup times were improved after combining most of the libraries, but it introduced a lot of head-scratching bugs that I have no idea how to solve; and of course you're losing out on the ability to rely on caching for updated pieces of code without having to download the entire blob again.

legistek commented 4 years ago

So have there been any developments on this or at least an ETA? Server-side is working quite well but client-side WASM performance through the interpreter is still unacceptable as soon as the DOM gets reasonably complex.

plasticalligator commented 4 years ago

I don't think so, the AOT on the mono repo still seems to be a WIP; I heard we'll have it by Q1 of 2020 but I don't know if that's for certain; which is sad because I have a very nice PWA set up with client-side but it has some performance issues that AOT would probably alleviate without needing dirty hacks.

In the meantime are some things you can do to alleviate the pain there, namely using virtual DOM and/or using RenderTreeBuilder directly so that you're not rendering everything at once and have some control over what's going on.

legistek commented 4 years ago

Well, I was also wondering if anything has changed in light of the announcement a few months ago about .NET 5. Interesting quote there:

The Blazor project is already using the Mono AOT. It will be one of the first projects to transition to .NET 5. We are using it as one of the scenarios to prove out this plan.

Do they know something we don't?

In the meantime are some things you can do to alleviate the pain there, namely using virtual DOM and/or using RenderTreeBuilder directly so that you're not rendering everything at once and have some control over what's going on.

Indeed. I am making an MVVM framework from scratch inspired by WPF and one of the first things I do is override ShouldRender to turn off Blazor's automatic render triggers and instead rely on property changes to tell components when to re-render. In fact one HUUUUGGGE performance improvement comes by updating styles through JS interop, rather than StateHasChanged, whenever possible.

Nonetheless, I'm having issues when large DOMs need to be generated up front - for example, a complex list or data grid. Server side is fine, but client side takes 5-10 seconds sometimes just to initially render.

What we really need is a VirtualizingPanel like in WPF. I have been thinking extensively about how this could be implemented in Blazor and JS interop, but a complete solution still eludes me. In WPF it works because the framework is responsible for measuring every visual element and reporting its size. Even with JS interop I'm not sure the same thing is possible, and I've yet to see a good generalized solution for this in any web framework.

plasticalligator commented 4 years ago

SyncFusion has some virtualization components, namely a list view - no idea how they work but I am using them. Might want to check them out.

There is also https://github.com/SamProf/BlazorVirtualScrolling which is worth looking at as well.

legistek commented 4 years ago

Oh yes I saw that. Glad to know it works well for you. It does have the significant limitations of needing a uniform item height and that the height be known in advance. But that could be an interim solution.

keithwill commented 4 years ago

Can someone tag this issue with the blazor-wasm tag? Its hard to find this issue amidst all of the server side (or hosting agnostic) Blazor issues.

And for those looking for an overview for the Mono WASM AOT work being done, I found this link: https://github.com/mono/mono/projects/11

rynowak commented 4 years ago

Your wish is my command 😆

legistek commented 4 years ago

Thanks that's very helpful!

Is it possible to get even the vaguest of estimates for when we might be able to start using AOT compiled WASM with Blazor, even experimentally? 6 months? A year? Has anyone on the Blazor team actually successfully gotten it working even in an ad-hoc fashion?

It's a little risky to start investing a lot of time in, or plan around, client-side Blazor when we still really don't know what the end product will look like. As good as server-side is we really need a reliable and performant client-side version for this to be viable.

Andrzej-W commented 4 years ago

I have posted this question here https://github.com/mono/mono/issues/10222 but got the answer that it is a wrong place. Reposting here:

It was announced that Blazor WASM will be released in May 2020. Can we assume that it will be native WASM application at this time? What is the correct answer?

  1. Yes.
  2. We will try, but we are not sure.
  3. No, it will be available in November 2020 with .NET 5.0.
  4. No, and we don't have any roadmap just yet.

For all Blazor fans two things are very important:

I'm a big fan of Blazor since the beginning. I know that Blazor server side has some advantages for some people but we are all waiting for real revolution - fast and reliable Blazor in the browser.

Thank you guys for your hard work!!!

freefrom-co-jp commented 4 years ago

@Andrzej-W This might be a bit misleading to anyone who skims through this and assumes that November 2020 is the canonical release.

Personally I have heard it is supposed to come out officially around Q1 of 2020.

Realistically there is nothing preventing us from implementing it into our build processes right now as far as I know beyond the bloated executable size and the fact it is not supported by Microsoft as of yet.

legistek commented 4 years ago

Realistically there is nothing preventing us from implementing it into our build processes right now as far as I know beyond the bloated executable size and the fact it is not supported by Microsoft as of yet.

Has anyone tried this? I think it's important for us to at least have a proof of concept so we can see how it performs compared to interpreted and compared to server-side. I know exe size is something the mono team is working on, and it is important, but app speed is king. (And compilation time is really irrelevant IMHO because we will be doing debugging server-side and only compile to native WASM for release. Compilation time for webpack can be pretty attrocious too).

danroth27 commented 4 years ago

At .NET Conf we announced that the first release of Blazor WebAssembly is planned for May 2020. For this first Blazor WebAssembly release we expect that the runtime will still be based on the IL interpreter that we are currently using.

The .NET runtime team is working on ahead of time compilation support for compiling directly to WASM. This approach has the benefit of improving runtime performance, but at the expense of app size.

For the initial release of Blazor WebAssembly in May we are exploring running the IL interpreter in a mixed mode, where hot paths have been compiled to WASM but the rest of the app is .NET assemblies. This provides a nice compromise between runtime performance and app size. However, it's not clear yet if this will land in time for May.

Longer term, we want to provide an SDK that gives the app developer control which parts of their app are compiled directly to WASM. There isn't a roadmap yet for when such an SDK will be available.

@lewing @richlander

Andrzej-W commented 4 years ago

Thank you @danroth27 for explanation. Download size can be partially masked by server side prerendering - make sure this https://github.com/danroth27/ClientSideBlazorWithPrerendering will work in all future Blazor (pre)releases.

legistek commented 4 years ago

@danroth27 thanks for the update! One clarification question - does "the .NET runtime team" refer to the Mono team, CoreRT, .NET 5, or something else?

danroth27 commented 4 years ago

@legistek They are all one team now :smile:. The tech though is based on the Mono runtime.

legistek commented 4 years ago

Oh, right I forgot. ;D What I was getting at was whether mono/wasm/10222 was the right issue/repo for info on this topic.

danroth27 commented 4 years ago

@legistek Yup, all the .NET WASM working is currently happening in the mono repo.

legistek commented 4 years ago

I've built a pretty complex app running server side Blazor now, and it's working very well. (WASM through the IL interpreter, however, is so slow as to be unusable).

I'm really dying to know how it would run with compiled WASM.

If we ignore download size or compile time just for the time being, is there any way of AOT compiling a Blazor app to WASM yet and trying it out? Over at the mono repo (https://github.com/mono/mono/issues/10222) people are posting some examples of AOT with other platforms like Uno, but I've yet to see a Blazor example let alone instructions on how to do it.

I realize the build process would be completely ad hoc at this point, but is it possible even in principle just so we can get even a rough sense of the performance difference? I like server side for debugging and demoing but I don't know if it's viable for a production deployment at scale. Thus I'm hesitant to do much more work on this project until I know that performance on AOT WASM will be good. I have to imagine there are a lot of people in the same boat.

legistek commented 4 years ago

Just to follow up, I wound up trying out the Uno WASM bootstrap using WSL (described here) and it really works quite well. This issue here is still marked as BLOCKED and while I know they're still working on reducing payload size that doesn't seem like it should be blocking work on an AOT build chain for Blazor, even if it is just Linux for now. Is that going on and if not what is the Blazor team waiting on from the Mono team before starting that?

Feroks commented 4 years ago

@legistek did you build your app or something else? How much better is the performance? Do you have some metrics to share? Asking out of curiousity.

legistek commented 4 years ago

I built a subset of my app and then just ran some performance metrics since I couldn't run the whole thing without Blazor bootstrapping.

For math (random number generation and simple arithmetic) I found AOT to be about 40x faster than interpreted.

What I was really interested in were things I knew would be inefficient like reflection and string manipulation. My app has a custom data binding framework similar to WPF so I tried setting up a complex data binding and changing the value to a random string 10,000 times. Here were the results:

Mono Interpreted IL: 2.49s Full AOT (Chrome): 0.702s Full AOT (Firefox): 0.5s Native: 0.067s

So basically we have a worst case scenario of AOT being about 4x as fast as interpreted IL, but a best case scenario of as much as 40x.

I think that's probably to be expected.

Unfortunately this still doesn't tell us how well Blazor AOT will do vs interpreted, but I'm a little pessimistic that it's going to be on the lower side (4-5x) because Blazor is presumably doing a lot of string manipulation to build the DOM, and also a sophisticated SPA will be doing a lot of JSON API calls (which of course use reflection and strings extensively). But really we can't be sure until we can actually try out a real Blazor app with AOT.

plasticalligator commented 4 years ago

I would imagine the performance will be remarkably improved in the near future as browser vendors start to take WebAssembly more seriously.

I think AOT merits a lot more investigation sooner than later because Blazor will likely live or die by the reputation of it's clientside implementation in WASM.

Projects like https://d07riv.github.io/diabloweb/ prove without a doubt that WebAssembly is more than capable of handling itself but I have yet to see an equally impressive proof of concept running on Mono+WASM.

partyelite commented 4 years ago

This issue is open for two years.. Is there any progress?

danroth27 commented 4 years ago

@partyelite Yes, there has been progress. There is an implementation of AoT compilation to WASM in the https://github.com/mono/mono repo and the runtime has been updated to support executing a mixture of .NET IL and compiled WASM files. However, the AoT support will not be ready for the upcoming May release. We will revisit shipping it for .NET 5.

legistek commented 4 years ago

I tried the AOT compilation option Dan is referring to and it works well with Uno.

What I'm still scratching my head about is why there isn't at least a PoC of it working with Blazor yet? I realize the toolchain is all still Linux and the output files are much larger than we ultimately want, but what is preventing making an example of a Blazor app working with AoT so that we can gauge performance and feasibility?

Herdo commented 4 years ago

I'm not sure if this is related, but on the Syncfusion repository there was a performance issue reported a while ago (https://github.com/syncfusion/blazor-samples/issues/50), said to be caused due to the slow performance of mono.

In my analysis, the performance issue drills down to calling js_to_wasm: grafik

Is this something that will be solved by this and the mono team? Or is this something unrelated?

legistek commented 4 years ago

@Herdo it might be related to this: https://github.com/dotnet/aspnetcore/issues/5617

Check your web console log to see if it's GCing excessively. This seems to be baked into the WASM Mono runtime and needs to be configurable IMO.

frankabbruzzese commented 4 years ago

@honkmother , Can you please share more Infos on how you succeeded in packaging Blazor DLLs with ILRepack? I used ILRepack.MSBuild.Task as detailed here. Packing succeeds but when I run the application I always get this error:

WASM: System.IO.FileNotFoundException: Could not load file or assembly 'netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies.

It appears that packaging breaks something, or at list prevents a correct bootstrap of the application.

kierepka commented 3 years ago

hmm AOT is in UNO. Can you C&P this solution?

ram16g commented 3 years ago

.Net 5 won't support AoT Compilation for Blazor?

legistek commented 3 years ago

It's crossed off the roadmap too.

I'd like to hear official comment before jumping to conclusions, but if true this is very bad news that not just affects Blazor but would force me to reevaluate .NET itself as a frontend platform.

There's been no reported activity in months on the Mono side of this either: https://github.com/mono/mono/issues/10222

Maybe it's time to cut losses and focus on CoreRT instead.

danroth27 commented 3 years ago

@ram16g @legistek We're working to improve Blazor WebAssembly runtime performance in .NET 5 (and beyond). AoT is still on our longer-term roadmap, but it's going to take longer to deliver than we have time for in .NET 5. The first step to AoT is to move Blazor WebAssembly to use the same core .NET libraries that are used by .NET Core, which should also help performance. That's the work that is happening right now. Then we need to work out how to make AoT work well with IL linking. At this point, we think we'll need until first quarter next year (early previews of .NET 6) to make the first previews of .NET AoT support for WebAssembly publicly available. But even without AoT, we still expect to deliver significant performance improvements for Blazor WebAssembly in .NET 5.

MichaelPeter commented 3 years ago

Hello @danroth27, the overall performance is right now not so much to my concern but what about the initial startup performance? It takes 2-3 seconds every page visit till the actual page loads, till the runtime is loaded the DLLs compiled etc. Will there be improvments without AOT? Or do we need to rely on prerendering?

danroth27 commented 3 years ago

Hi @MichaelPeter. The initial page load time is dominated by downloading the app and starting the runtime. AoT won't really help that. AoT is intended to improve runtime performance, not reduce the app download size. For JIT based runtimes AoT can improve startup performance because you avoid the need to JIT at runtime, but Blazor WebAssembly uses an IL interpreter based runtime without any JIT support.

In all likelihood, AoT will actually make the app larger to download, because .NET IL is a more compact format than its natively compiled representation. So using AoT will likely result in a tradeoff between speeding up runtime performance at the expense of increased download size. The current thinking is that we will make the AoT toolchain available to developers so that you can decide how much of your app you want to AoT and then the app will run in a mixed mode, where some of the app still runs as .NET IL, while the performance critical paths are precompiled to WebAssembly.

To improve the app starup performance we are looking at further improvements to the .NET IL linker and also doing work to the core framework libraries to make them more linkable. We also plan to look at startup performance of the runtime itself once it is downloaded.

MarkusRodler commented 3 years ago

@danroth27 Are there any issues that I can follow about the progress on the app startup performance? This is at the moment my biggest concern about blazor.

MichaelPeter commented 3 years ago

@danroth27 :+1: Thank you very much for the info, I mostly use Blazor in LAN envoirements where Download times should be almost zero and thought the Browser is caching the. Net DLLs anyway. But in a Startup Issue I would also be very interested.