Monday, April 9, 2012

The importance of feedback...

Over the last few nights, I've been watching the screencast of the SolarUG meeting from March 29th which was pretty much dedicated to the Real Studio web framework. I was unable to attend in person, but was still curious about what had been discussed.

Somewhere in the middle of the meeting, a comment was made about how slow the WebListBox gets when you send lots of rows (like 1000 rows of 150 characters each). My immediate reaction was "well yeah, that's a lot of data," but the user seemed sure that the delay was after the data got to the browser, so I decided to take a second look.

The Diagnosis

All of the major browsers that we support (except IE7) have a built-in profiler (like the one in Real Studio) that lets you see how many times a method was called, how long each call took and the total time ( # of executions multiplied by call time), which can be incredibly useful in figuring out why something is running slowly. 

I created a simple web app which had a single listbox, and in WebPage1.Shown, I called Listbox1.AddRow 1000 times inserting a 190 character text sample. Running this app locally, creating and sending the rows was nearly instantaneous, but then the WebListBox took 2.83 seconds to draw. Yikes! Time to dig deeper.

The Bug

After a little experimentation, I discovered an issue with the rendering routine that goes something like this:

  1. Adding the first row refreshes the first row
  2. Adding the second row refreshes row 1 and 2
  3. Adding the third row refreshes row 1, 2 and 3
  4. …and so on…
If you do the math, the number of individual row refreshes is a little over 500,000. Several other methods are also called from this procedure, each adding to the severity of the bug.

The Fix

In Real Studio 2012r1, changing the contents of a WebListBox now employs a delayed refresh. Basically, if you send a bunch of commands to add or remove rows, the refresh is now delayed until the set is complete. The browser spends less time refreshing rows and gets the data to your end user faster!

…and the moral of the story is…

As far as I can tell, this bug was not in Feedback. In other words, no one has ever reported it. If you notice a web framework control that doesn't seem to perform as well as you expect it to, LET US KNOW! File a bug report in Feedback with an example and don't assume that it's that way just because it's a web app.

9 comments:

Unknown said...

You didn't see my case #20083 which already made it to 128th place on the top list?
Filled in February.

Brad Hutchings said...

Andre was MVP of that meeting. It's funny, because to put together the Word Finder the previous evening, I was loading about 1000 rows at one point, and didn't think performance was terrible. There might have been a second or second and half lag when filling the table, and it didn't get my attention.

Didn't this come up when talking about that online chatboard experiment? Repeated reloadings of many lines would get noticeable and annoying at a second or two pre reload.

Anyway, great catch Greg. Glad we could bring it to your attention.

Greg O'Lone said...

I didn't see #20083 until you requested they be merged this morning.

Didier said...

On delphi

Listbox.beginupdate
..addrow
Listbox.endupdate

This is better

Norman Palardy said...

The trouble with this style
Listbox.beginupdate
..addrow
Listbox.endupdate

is what if you forget to endupdate ?
It could be error prone

Unknown said...

Norman, that is the smallest problem.
I think the idea is great. Check the feedback case, where I collected a few ideas and a sample project:
feedback://showreport?report_id=20880

Greetings
Christian

Kiko Lobo said...

Why don't you also employ a mode that i can use to send a : table.isUpdating

and then a

table.endUpdating

so then after the endUpdating you can render the table... Maybe you can have a property that says : manual Refresh or Auto Refresh, in the second you employ a delay. In the first you rely on the user telling you when will updates occur and when it ended.. THis is useful if you need to do a bunch of changes in the table (deleting, adding moving around, etc).. You then just endUpdate... You can even employ animation support for the edits this way.

Kiko Lobo said...

Norman,

Its the same if you forgot to say : End if in an IF, you could have the compiler look for the end update pair, and advice the user there is no endupdate.

If the user does not employs the beginupdate, then you fall back to the delay.

Norman Palardy said...

"Because delphi has it is certainly not compelling in my mind"
I can actually think of a way to do "block updates" without introducing yet another thing a developer has to remember to do much

Basically what's needed is a way to defer drawing - which if you read Greg's post is exactly what the web framework will now do.

We just dont have a similar means on the desktop