Battle plan:
After a very extensive investigation, here are things I think we should do to complete this for 5.0:
on
. This is ultra-trivial and gives a 2-3% gain on the PlainTable/ComplexTable benchmark. https://github.com/dotnet/aspnetcore/issues/24465IsFixed
when cascading values to large numbers of receivers.@attributes
is relatively expensiveSetParameterAsync
, and how to do it efficiently.<Virtualize>
The specific optimizations I've listed above aren't the only ones I'm aware of. I've tried many things based on the profiling data. These optimizations are the ones that have the biggest impact for the least cost.
Would it be possible to create "native" WebAssembly benchmarks to test Blazor against? Just to be able to decide whether any given performance related issues are present because of WebAssembly (/JS interop DOM manipulation) or because of the Blazor runtime?
The performance is a huge issue. I was expecting "close to native" speeds from .net wasm but in my experience it's far from it. I'm working on an application that heavily relies on cryptography, and the performance different from .net core console and blazor wasm is staggering.
For example, one of the nuget packages I use is SRPDotNet. I install the nuget package, copy paste the code from their example, and run it on each platform. The time to run the sample on each is
.NET Core Console
0.0784997 seconds
Blazor WebAssembly 3.2.0
10.45238 seconds
Blazor is over 133x slower! Unfortunately for me, this is the user sign in process for my application, and that length of time to sign in is unacceptable. This was also tested on my powerful desktop PC, I imagine it would be much worse on a mobile device.
I also considered that the specific nuget package I was using wasn't very optimized and tried another implementation with the same poor results in blazor.
If someone on the blazor team wants to try this out for themselves install the SRPDotNet nuget pacakge and run this code.
```C#
Console.WriteLine("Starting SRP");
var sw = Stopwatch.StartNew();
var username = "johndoe";
var password = "password";
var hash = SHA256.Create();
var parameter = new Bit2048();
var srp = new SecureRemoteProtocol(hash, parameter);
var privateKey = SecureRemoteProtocol.GetRandomNumber().ToBytes();
var serverKey = SecureRemoteProtocol.GetRandomNumber().ToBytes();
var verificationKey1 = srp.CreateVerificationKey(username, password);
var user1 = new SRPUser(username, password, hash, parameter);
var a = user1.GetEphemeralSecret();
var authentication1 = user1.StartAuthentication();
var svr1 = new SRPVerifier(hash, parameter, verificationKey1, authentication1.PublicKey);
var b = svr1.GetEphemeralSecret();
var challenge1 = svr1.GetChallenge();
var session1 = user1.ProcessChallenge(challenge1);
var hamk = svr1.VerifiySession(session1);
sw.Stop();
Console.WriteLine($"SRP took {sw.Elapsed.TotalSeconds} seconds");
```
@shawnshaddock Blazor WebAssembly runs using an IL interpreter. For the 3.2 release, it's about 10-15x slower than .NET Core for the typical UI workloads that we expect Blazor users to be writing. We expect improvements in this number as part of .NET 5. This issue is specifically focused at investigating performance improvements in Blazor's WebAssembly implementation for 5.0.
https://github.com/mono/mono is currently where the .NET WebAssembly team is tracking WASM specific issues. Since your issue pertains to the underlying platform, I'll recommend filing an issue there.
@yugabe When I have critical performance requirement and default blazor is slow I generate result html by myself dinamically, sent it to js using fast low level mono api and update dom from js.
It helps because it shows much faster render, about 10x faster depending on case.
I think it can be good for comparison too, until this way renders faster than blazor we have slow performance compared to js not wasm.
I think we can't really compare against wasm because wasm can't manipulate dom without js for now.
If you wonder how this technique works please check this video
@Lupusa87 I understand, but I feel it to be obvious that in the long run you have to have an out-of-the-box performant framework instead of a good enough manual workaround. It kind of defeats the purpose of Blazor itself to build a whole component in HTML and send the whole diff to JavaScript (you are overwriting a whole DOM subtree without considering which elements to keep, if I understood correctly). It is a good workaround, but it won't fix Blazor WASM on mobile. Have you tried using the @key
attribute to speed up rendering though?
Now, Blazor WebAssembly performance is not quite good on mobile devices. Mostly just what you said: DOM manipulation is slow because there is no way to manipulate the DOM without using JS interop. If I know correctly, direct DOM manipulation is on the roadmap for WASM vNext. If someone is competent enough to help out the WASM guys a little, I'm sure it will be greatly appreciated to speed up the delivery of that feature.
@yugabe I use this technique only in critical parts and when it makes sense (when whole most part of dom is changing and no sense to analize changes which makes blazor slower) and shows better performance.
I do not like to do extra work and override blazor intended behavior but I am forced to do so in some cases, sure it is fast workaround and not solution but it works for me for now.
@SteveSandersonMS
I think blazor component should have option to re-render from scratch meaning not to do diffing, just remove old dom fragment and insert new one, it will save some time.
When blazor developer knows that some component on update has major changes he/she can turn this mode on.
Not sure if it is good idea but seems to be from my perspective.
something like this statehaschanged(true), statehaschanged can have bool parameter with default value false and if true is provided component will re-render full dom skipping diff calculations.
@Lupusa87 Now I understand your point. What you outlined just now could help in scenarios where a full component rerender could be preferred over diffing. This sounds a good compromise if no performance gains are available otherwise, a sort of band-aid, if you like. I feel it should be an advanced only scenario though, as the vast majority of the time you shouldn't have to worry about performance so much. Like now, when you are developing, say, an ASP.NET Core backend, you are going to have about 1% of the code being performance-critical, and 99% of the time "it just works". I feel something like this should be a baseline. Now, unfortunately, performance is sub-par, but the framework and foundation are very solid.
That's why I would recommend writing test applications using different alternative frameworks (Angular, React, plain JS, plain WASM) and compare performance to Blazor to find bottlenecks and possible optimization opportunities. I think that's the same method the ASP.NET Core backend team used to compare to NodeJS (but I'm just guessing).
Should the full component rerender be put into a separate issue?
Yes it is for advanced scenario and can be used in 1% of cases but it does not reduce it's importance.
Any framework's strengt is measured fow it can handle advanced scenarios, trivial ones can do everyone :)
Re dedicated issue I am not sure it makes sense bucause as I remember I am not saying this first time and probably the idea will be declined again with some right arguments which are hidden to my eyes.
For what Lupusa is saying, why not have a way to flag a component as render always with no diff built into the framework? Then for anything where performance is key, that attribute could be applied to force full render and replacement without doing any diffing? Or is this not really possible?
@radderz Perhaps it would be best not to get too deep in the details of this one particular scenario here, since this issue is meant to be tracking the whole range of perf-related work.
If you have a specific scenario that you want to optimize, could you please post a new separate issue rather than on this thread, giving an example of the code that you're trying to make faster, and what data you have that suggests that the problem is something specific (e.g., diffing)? Thanks very much!
@radderz @SteveSandersonMS There is a proposal (with unclear fate...) of having ability to provide control logic when to render at builder-API-level here: #21915 (further comment: https://github.com/dotnet/aspnetcore/issues/13610#issuecomment-631699616)
Most helpful comment
Would it be possible to create "native" WebAssembly benchmarks to test Blazor against? Just to be able to decide whether any given performance related issues are present because of WebAssembly (/JS interop DOM manipulation) or because of the Blazor runtime?