Tabs performance

Hello @Team.

The tabs below take long seconds to load and they get even worse in production.


I want to discuss possible improvements. The demo shows some waiting times too, specially when selecting the "Employee" tab, probably because of the many columns of the grid.

The first thing I can think of is to upgrade my app to NET 5, because of the reported performance enhancements they did in the Blazor framework, although they might only impact the WASM applications. Second thing is rendering only the visible page on page load, and render the remaining pages in a background thread while keeping them hidden. The expander, for example, doesn't load its contents every time you expand it, but on page load.

The Tabs component by itself is rather simple and doesn't involve any performance overhead. You can check it for yourself. Also by default it renders only the selected tab.

The demo uses database access which inherently introduces a delay. Not to mention the way Blazor server works (highly dependable on latency). Here is how this demo behaves online (I am on a not-so good connection at the moment):

tabs
VS on localhost:

local-tabs

I think it is the content of the tabs and the implementation of the page that is causing the delay rather than the RadzenTabs component itself. You can try hiding various parts and commenting some of the codebehind to see if it makes a difference. Upgrading to .net 5 may also help but I won't expect a dramatic improvement if you page relies on a lot of database calls.

1 Like

A key feature in our app is performance. We use raw SQL for database queries, Dapper for mapping, SpanJson for serialization and dynamic arrays throughout the pages to reduce data overhead, so data manipulation is very fast.

Here is how the tabs perform in a localhost environment. There is no triggered code when switching between tabs, because all data is loaded on page load. Yet there is a considerable delay when moving through some of the tabs.

Tabs 2 and specifically 4 are particularly slower because of the multiple components rendered in the DataList template. Maybe this has nothing to do with the tabs themselves, but with the DataList component having to render many components in each template.

How does the DataList demo perform in localhost?

Hey @kim,

Try the new RenderMode="TabRenderMode.Client" as shown in our demo:

Unfortunately nothing really changed.

TabsPerformanceClient

Here's the last tab (slowest one) if it helps:

<RadzenTabsItem Style="height:100%" Text="@(Language.GetTextType(LanguageService.TextType.Documents))" Visible=@(PersonGroup.PersonDocumentsVisible)>
    <ChildContent>
        <RadzenDataList Data="@PersonDocuments" Style="max-height:100%;overflow-x:hidden;overflow-y:auto" TItem="EntityDocument" >
            <Template Context="personDocument">
                <div class="row align-items-center">
                    <div class="col-3 col-sm-3 col-md-3 col-lg-3 col-xl-3">
                        <RadzenLabel Text="@($"{(personDocument.Document.Name)}")" />
                    </div>
                    <div class="col-9 col-sm-9 col-md-9 col-lg-9 col-xl-9">
                        <div class="row align-items-center">
                            <RadzenMask @bind-Value="@(personDocument.Number)" Mask="@(personDocument.Document.Mask)" Pattern="@(personDocument.Document.Pattern)" Placeholder=@(Language.GetTextType(LanguageService.TextType.Number)) Style="width:22%" Visible="@(personDocument.Document.Number)" />
                            <RadzenCheckBox @bind-Value="@(personDocument.Conformity)" Style="margin-left:0.5rem;margin-right:0.5rem;width:3%" Visible="@(personDocument.Document.Conformity)" />
                            <RadzenLabel Style="width:12%" Text="@(Language.GetTextType(LanguageService.TextType.Conformed))" />
                            <RadzenDatePicker @bind-Value="@(personDocument.Expiration)" DateFormat="@(Language.GetDateFormat())" Placeholder=@(Language.GetTextType(LanguageService.TextType.Expiration)) Style="width:19%" Visible=@(personDocument.Document.Expiration) YearRange="2021:2099" />
                            <RadzenDatePicker @bind-Value="@(personDocument.Extension)" DateFormat="@(Language.GetDateFormat())" DateRender="@((args) => PersonDocumentExtensionDatePickerDateRender(args, personDocument))" Placeholder=@(Language.GetTextType(LanguageService.TextType.Extension)) Style="width:19%" Visible=@(personDocument.Document.ExtensionLimit != null) YearRange="2021:2099" />
                            <RadzenTextArea @bind-Value="@(personDocument.Observation)" Placeholder="@(Language.GetTextType(LanguageService.TextType.Observation))" Rows="1" Style="width:20%" Visible="@(personDocument.Document.Observation)" />
                        </div>
                    </div>
                </div>
            </Template>
        </RadzenDataList>
    </ChildContent>
</RadzenTabsItem>

Here's with 15 items in the "Documentos" tab, in localhost and with render mode set to client:

TabsPerformanceClientMany

The DataList is nothing more than a simple foreach over the current page of data. Not sure what else could cause the delay but we can troubleshoot a reproduction if you have any. I suspect it is the sheer number of components that need to initialise and the network latency which is an inherent Blazor server weakness. It would explain why the page performs a lot better on localhost.

It appears that the problem still exists with the latest version (Radzen.Blazor 3.18.6) on .NET 6.

My observation confirms that it is related to network latency or bandwidth - it has acceptable performance when both the server and the browser are local or on the same LAN; switching tabs slows to an unacceptable level or even completely stops working when the datagrid becomes large and there is VPN between the browser and the server.

It is interesting to note that the initial rendering of the first tab when the page is rendered is very fast, should we expect switching to different tabs to act at the same speed?

DataGrid in other components such as Accordion appears to have the similar issue.

@korchev Below is the test page I used to test the performance. Tab switching becomes very slow at count of 200 and is completely broken (the entire page is greyed out and the browser displays "Attempting to reconnect to the server" and finally "Reconnection failed. Try reloading the page if you're unable to reconnect." after retries are exhausted) with a count over 300.

@page "/benchmark/{count:int}"

<div>
    <RadzenTabs>
        <Tabs>
            <RadzenTabsItem Text="One">
                <RadzenDataGrid AllowColumnResize="false" AllowVirtualization="false" Data="@_employees" TItem="Employee">
                    <Columns>
                        <RadzenDataGridColumn TItem="Employee" Property="Id" Title="Id" />
                        <RadzenDataGridColumn TItem="Employee" Property="Name" Title="Name"/>
                        <RadzenDataGridColumn TItem="Employee" Property="Age" Title="Age" />
                        <RadzenDataGridColumn TItem="Employee" Property="Gender" Title="Gender">
                            <Template Context="data">
                                <RadzenLink Path="@data.Gender" Text="@data.Gender"/>
                            </Template>
                        </RadzenDataGridColumn>
                    </Columns>
                </RadzenDataGrid>
            </RadzenTabsItem>
                        
            <RadzenTabsItem Text="Two">
                <RadzenDataGrid AllowColumnResize="false" AllowVirtualization="false" Data="@_employees" TItem="Employee">
                    <Columns>
                        <RadzenDataGridColumn TItem="Employee" Property="Id" Title="Id" />
                        <RadzenDataGridColumn TItem="Employee" Property="Name" Title="Name"/>
                        <RadzenDataGridColumn TItem="Employee" Property="Age" Title="Age" />
                        <RadzenDataGridColumn TItem="Employee" Property="Gender" Title="Gender">
                            <Template Context="data">
                                <RadzenLink Path="@data.Gender" Text="@data.Gender"/>
                            </Template>
                        </RadzenDataGridColumn>
                    </Columns>
                </RadzenDataGrid>
            </RadzenTabsItem>
        </Tabs>
    </RadzenTabs>
</div>

@code
{
    [Parameter]
    public int Count { get; set; }

    class Employee
    {
        public int Id { get; set; }
        public int Age { get; set; }
        public string Name { get; set; }
        public string Gender { get; set; }
    }

    private Employee[] _employees;

    protected override void OnParametersSet()
    {
        var random = new Random();

        _employees = new Employee[Count];

        for (var i = 0; i < Count; i++)
        {
            _employees[i] = new Employee()
            {
                Id = i,
                Age = random.Next(20, 80),
                Name = "TBD",
                Gender = (i % 2) == 1 ? "Male" : "Female",
            };
        }
    }
}

We haven't done anything as quite frankly we don't know what could be done to mitigate the network latency.

I tested your page with 300 items and here is how it worked:

localhost:

localhost

Switching tabs on localhost is instant.

remote:
https://ec30-94-155-71-204.ngrok.io/datagrid

remote

Switching tabs on remote took a few seconds but is still usable and does not break connection.

A possible suggestion is to try <RadzenTabs RenderMode="TabRenderMode.Client">. This will render all tabs server side (which you say was faster) and then switch them client-side via JavaScript which won't make any roundtrips to the server.

Thanks for the quick response @korchev .RenderMode="TabRenderMode.Client" does not help my case though.

I notice that your test page with 300 rows performs similar to my environment with 100 rows over VPN (has delay but is able to switch). Can you increase the number of rows (or add the page parameter so that tester can change the row count as needed) to a larger number (1000 or 2000)?

The more you increase the count, the more HTML will be transferred through the web socket. Bigger content -> slower performance. The only option in my opinion is to enable DataGrid virtualization during scrolling.

I tried turning on virtualization (AllowVirtualization="true") on DataGrid, but to my surprise the rendering speed appears to be better with virtualization off.

When virtualization is turned on, it renders the first page very quick, but as you scroll down the grid, it could not keep up with the scroll (I was going very gently); on the other hand with virtualization off scrolling has not problem.

Like I mentioned earlier, rendering datagrid on the first tab when the page is loaded is not an issue (which suggests virtualization probably will not help much), the problem is with switching tabs.

This is an interesting finding - I could not get tabs working with 300 rows in the datagrid over VPN, but that was when the Web server uses HttpSys hosting. I can now get 1000 rows working using Kestrel hosting under the same network condition; there is still delay in tab switching but the browser no longer gives up.