BlazorT3_1200x303

Can you get a significant performance increase by writing your own Blazor or JavaScript code instead of using the Telerik DataGrid? Probably not.

When you go to create your UI, you have three options: Write it in JavaScript, write in Blazor, use a third-party component. Plainly, the third-party component (like the DataGrid in Telerik UI for Blazor) will give you more functionality with less effort… but what about performance?

My attitude toward improving performance, especially when talking about client-side code, may seem perverse: I’m not really interested in something “running faster” unless my user notices it. Unless a speed improvement elicits a, “Wow, that was faster!” from my user, I don’t care.

But that’s not to say that performance doesn’t matter to me: If I can get that “Wow!” experience, I want it (provided that I don’t have to write an enormous amount of code to get it). Over my last few posts, I’ve been looking at the functionality delivered by the Telerik UI for Blazor DataGrid. I think it’s fair to see what using that grid might be costing me in terms of performance.

In the following discussion, by the way, I ignored the time to download the Blazor infrastructure: I started the clock running some time after my code started executing. In these tests, I compared Blazor against “pure” JavaScript and jQuery. Had I been comparing Blazor against some other framework (e.g. React, Angular, or Vue), I would have considered download time.

My Scenario

Given the chargeback rate of a developer ((hourly rate + benefits + managements costs) x time on job), no one is going to attempt to build a component with as much functionality as the DataGrid in Telerik UI for Blazor—it’s more efficient to get a third-party component. However, you might consider building something that did “just this one thing.”

On that basis, I decided that the scenario driving my comparison would be: “What if I needed a page that just displayed a table of data? What technology will give me performance I would care about: Pure JavaScript, my own dynamic component, or the Telerik DataGrid?” It seemed to me obvious that the DataGrid was going to lose this race: Even stripped down to its basics, the grid is going to be doing (and setting up to do more) than the code I might write to generate a bunch of table tags. But, still, I wanted to know what the cost was going to be.

For this scenario, I ran three tests for each of the technologies: Time to initial display, time to redisplay the page if I used the browser’s refresh button, and time to redisplay if I used a button to recreate the display without re-fetching the data. In all cases, I ignored the time for the first run and only used timings from subsequent runs.

Set Up

You can probably skip this and go straight to the next section where I give my results. You’ll only be interested in this stuff if you question those results and want to challenge how I got to them.

To test the Telerik DataGrid, I added a barebones TelerikGrid to my Razor page. I turned off paging because I wasn’t going to implement paging in either of my other two cases, but I didn’t turn off any other options, either. Here’s the resulting markup:

<TelerikGrid Data="@MyData" Pageable="false">
  <GridColumns>
     <GridCheckboxColumn SelectAll="false" Title="Select" Width="70px" />
     <GridColumn Field="@(nameof(Employee.Id))" Width="120px" />
     <GridColumn Field="@(nameof(Employee.FullName))" Title="Employee Name" />
     <GridColumn Field="@(nameof(Employee.Department))" Title="Team" />
     <GridColumn Field="@(nameof(Employee.HireDate))" Title="Hire Date" />
  </GridColumns>
</TelerikGrid>

I retrieved my objects from a Web Service and loaded them into the collection that my grid was bound to in my component’s OnInitializedAsync method. Here’s that code:

async protected override Task OnInitializedAsync()
{
   HttpClient hc = new HttpClient();
   HttpResponseMessage hrm = await 
            hc.GetAsync("https://localhost:44380/api/EmployeeService");
   MyData = await hrm.Content.ReadFromJsonAsync<List<Employee>>();
   await base.OnInitializedAsync();
}

That took care of my grid test.

To test generating a table without the grid, I used a RenderFragment called from my .razor page to create my table. That code began and ended like this (I won’t include it all… it’s really boring):

RenderFragment CreateTable()
{
   RenderFragment table;

   table = b =>
   {
      b.OpenElement(1, "table");

      b.OpenElement(2, "thead");
      b.AddAttribute(2, "class", "k-grid-table");
      …code omitted…
      foreach (Employee emp in MyData)
      {
         b.OpenElement(3, "tr");
         b.AddAttribute(3, "class", "k-master-row");

         b.OpenElement(4, "td");
         b.AddContent(4, emp.Id);
         b.CloseElement();
         …code omitted…
         b.CloseElement();
      }
      b.CloseElement();
      b.CloseElement();
   };
   return table;
}

For the initial display test, I started my timer right before I created the HttpClient and captured my “end time” in the page’s AfterRender event. To test redisplaying the data with the grid, I set the collection driving my grid to an empty list, started my clock, and then set the grid’s collection back to the data. For the “redisplaying the data” test, I just started the clock at the top of the method that returned the RenderFragment. I did all my tests using a Release build.

To generate the table in JavaScript, for the initial display I used code like this test to grab my objects out of a Web Service and wrap them in a table (again, I won’t include all of the tedious code):

function GetDataAndBuildTable() {
   $.getJSON("https://localhost:44380/api/EmployeeService", buildTable);
}
 
function buildTable(emps) {
   let empTable = $("<table class='k-grid-table'/>")
   buildHeader(empTable);

   let empBody = $("<tbody>")
   emps.forEach(function (emp) {
   buildRow(emp, empBody)
            empTable.append(empBody);
   });
    empTable.append(empBody);
    $("#empDiv").append(empTable);
});
…functions omitted…
function buildCell(prp, emp, row) {
   let empCell = $("<td>" + emp[prp] + "</td>").appendTo(row);
   row.append(empCell);
}

For my JavaScript initial display test, I started my clock running when I called the Web Service. I captured my “end time” after I appended my table element to the div tag on the page. For redisplaying without re-fetching, I just drove my code from the array I’d already fetched.

Taking my end time when I appended the table element may give my JavaScript test an advantage by ignoring render time… but I don’t really care. Obviously, these measurements are crude at best, but I’m only interested in gross differences. I don’t care about the difference between two tests unless there’s at least a quarter of a second difference because I don’t believe users can detect anything smaller.

The Results

The JavaScript code took between 25 and 30 milliseconds to get to the initial display, as did refreshing the browser. Rebuilding the table without re-fetching the data took between 9 and 30 milliseconds (i.e., sometimes it was faster than the initial display).

The DataGrid took between 250 and 280 milliseconds for the initial display. It got faster when I refreshed the browser: between 210 and 230 milliseconds. Refreshing the grid without re-fetching was faster, between 150 and 220 milliseconds. Compared to JavaScript, you could generalize and say things took as much as 200 milliseconds longer (or as little as 120 milliseconds longer).

Building the table in Blazor by using a RenderFragment took between 60 to 100 milliseconds for the initial display. Not as fast as the JavaScript (which was 30 to 40 milliseconds faster). Refreshing the browser took about the same time. Re-executing the code to redisplay the page, on the other hand, was faster than the JavaScript: between 5 to 10 milliseconds.

I first ran all these tests in Chrome, by the way. Obviously, switching to another browser would get different results because I’d be using different JavaScript and WebAssembly engines. Having said that, I did repeat the tests in Internet Edge and not much changed. The only significant difference was that the Telerik DataGrid took longer to get to the initial display in Edge by about 200 milliseconds, a difference approaching, but still within, my “I don’t care” boundary.

While not strictly relevant, I also measured the time to make my Ajax call and parse the resulting JSON. In JavaScript, I measured the time from calling getJSON to the start of the callback function invoked by in getJSON call: that came to between 15 and 20 milliseconds. For Blazor, I measured the time from creating the HttpClient object to just after loading the List that held my data: that turned out to be between 210 and 260 milliseconds. Again, just short of my “I don’t care mark.”

Conclusions

I came to two and a half conclusions.

First conclusion: I’m trying desperately to care. The largest difference I could get was a fifth of a second. The differences were so small that, effectively, all the technologies resulted in an instantaneous display (ignoring, as I said at the start, the time to download the Blazor infrastructure).

One-half conclusion: Considering the time it takes to make a Web Service call in Blazor, you should structure your application to only make one big call at a time, rather than make a series of smaller calls (but that’s just good data management: make as few trips to your data server as possible at any one time).

Second: If you are going to worry about performance and intend to build it all yourself… well, based on these tests (and assuming performance is all that matters to you), then you should do it in Blazor. Which I thought was a pretty cool thing to discover.

But if we’re going to talk about performance, we also need to also talk about efficiency. Displaying the table using the Telerik DataGrid took about 4 lines of code; the JavaScript version took about 40 lines; the Blazor version, about 50 lines. My time is better spent leveraging the DataGrid even if I’m doing the simplest task possible: Just displaying a table. And, of course, you’re never going to be “just displaying a table.”


Peter Vogel
About the Author

Peter Vogel

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter also writes courses and teaches for Learning Tree International.

Related Posts

Comments

Comments are disabled in preview mode.