Patrick Szalapski
Patrick Szalapski

Reputation: 9439

Blazor WebAssembly deserializing is much slower than even the download? What is wrong?

In my Blazor app, I have a component that has a method like this. (I've replaced GetFromJsonAsync call with code from inside it, to narrow down the slow part.)

  private async Task GetData()
  {
      IsLoading = true;
      string url = $".../api/v1/Foo";  // will return a 1.5 MB JSON array
      var client = clientFactory.CreateClient("MyNamedClient");

      Console.WriteLine($"starting");

      List<Foo> results;

      Task<HttpResponseMessage> taskResponse = client.GetAsync(url, HttpCompletionOption.ResponseContentRead, default);

      var sw = Stopwatch.StartNew();
      using (HttpResponseMessage response = await taskResponse)
      {
        
        response.EnsureSuccessStatusCode();
        var content = response.Content!;

        if (content == null)
        {
          throw new ArgumentNullException(nameof(content));
        }
        
        string contentString = await content.ReadAsStringAsync();

        sw.Stop();
        Console.WriteLine($"Read string: {sw.Elapsed}");
        sw.Restart();

        results = System.Text.Json.JsonSerializer.Deserialize<List<Foo>>(contentString);

      }

      sw.Stop();
      Console.WriteLine($"Deserialize: {sw.Elapsed}");
      
      StateHasChanged();
      IsLoading = false;

My download of 1.5 MB takes 1-6 seconds, but the rest of the operation (during which the UI is blocked) takes 10-30 seconds. Is this just slow deserialization in ReadFromJsonAsync (which calls System.Text.Json.JsonSerializer.Deserialize internally), or is there something else going on here? How can I improve the efficiency of getting this large set of data (though it isn't all that big, I think!)

I have commented out anything bound to Results to simplify, and instead I just have an indicator bound to IsLoading. This tells me there's no problem in updating the DOM or rendering.

When I attempt the same set of code in an automated integration test, it only takes 3 seconds or so. Is WebAssembly really that slow at deserializing? If so, is the only solution to retrieve very small data sets everywhere on my site? This doesn't seem right to me.

Here's the resulting browser console log from running the above code:

VM1131:1 Fetch finished loading: GET "https://localhost:5001/api/v1/Foo".
read string 00:00:05.5464300
Deserialize: 00:00:15.4109950
L: GC_MAJOR_SWEEP: major size: 3232K in use: 28547K
L: GC_MAJOR: (LOS overflow) time 18.49ms, stw 18.50ms los size: 2048K in use: 187K
L: GC_MINOR: (LOS overflow) time 0.33ms, stw 0.37ms promoted 0K major size: 3232K in use: 2014K los size: 2048K in use: 187K

For what it's worth, here's the Chrome performance graph. The green is the download and the orange is "perform microtasks", which I assume means WebAssembly work.

enter image description here

Upvotes: 3

Views: 2876

Answers (1)

Henk Holterman
Henk Holterman

Reputation: 273244

or is there something else going on here?

It looks that way. You didn't state what hardware you use but on my Desktop (i7) I can download in < 100 ms (Localhost) and Deserialization takes < 4 seconds. Not much difference between Debug and Release mode or between Browsers.

So, 5 seconds to just download from localhost is already very strange. Something is wrong in your setup.

An MCVE:

  • Start with the Wasm Hosted template
  • Make the controller return ~17000 items (was 5) to get ~ 1.5 MB
  • Put your code in the FetchData OnInit(), replace Foo with WeatherForecast
  • Don't try to render them all.

See what results you get for this.

Upvotes: 1

Related Questions