Friday, December 13, 2013

“DOJO does not always make it exactly like a desktop application”


Climbing … We’re practicing how to haul something heavy up a cliff.  It’s a rescue technique; critical to know when it’s needed but, thankfully, it almost never happens. The problem with this array of rope,  pulleys, carabiners and  barrel-shaped knots that give me leverage to lift 200 pounds is that for every foot I want to lift the weight, I have to pull three times that much rope.  It’s slow work!

There’s a great UI used in windows explorer.  The view flows great for the user: whatever is selected at the top is shown in a tree view on the left, whatever is selected in the left tree is displayed in the details list on the right.  It’s easy and it works in Windows because disk directory lookups are quick.   

 

DOJO is great for making web UIs that look like desktop applications, however for a DOJO UI, this three tiered UI requires careful design to make it perform well.   

One thing that really slows down a web UI multiple XHR requests that must be done sequentially.  For instance:    

 


 
In this case, the XHR requests cannot be done in parallel because the second and third requests depend upon the previous responses.  The web page TTI is greater than the sum of all three XHR times.  Usually, this kind of view always has a default selection for the top and left parts of the view so nothing is initially empty.  The default selection makes UI initialization the worst case  for this sequential XHR problem as in the table below:

 

Action

Number of sequential XHR requests

Select a new item in the left tree view

2

Select a new category at the top

2

Initialize the view

3
 

The initial page load can require three sequential round trips to the server.   

The view can still be used, but it requires some design thought.  Some options:

 
1)       Have no default selections.  This is not a great option because people are accustomed to Windows which does have initial selections.

2)      Choose default selections that are easy to calculate.  Some XHR requests are faster than others; choose defaults that use faster XHR requests.  If the XHR requests are fast enough in the worst-case then the UI will be OK.

3)      Calculate the default selections on the server and return all the data for the initial tree view in one XHR request.  This eliminates the extra network delays.

4)      Avoid this cascading view in web applications unless the XHR requests are all guaranteed to be lightning fast.   If the XHR has any chance of being slow because the UI will be over 3X as slow and the best option is to design a different UI.

 

 

Thursday, December 5, 2013

“GUI components often work but do not always scale”


Climbing … Ted looks bad; not injured but he’s flushed, drenched, and lactic acid buildup has made his forearms look like useless swollen tomatoes. In climbing they call it being pumped and it’s not good.  We are 400 feet up an “easy moderate” climb.

“I don’t understand”, he says, “I climb routes twice as difficult in the gym”

“Ted, in the Gym, the routes are only 25 feet high and you don’t carry gear”

“I need some endurance training”

 

A performance analyst says, “Ted, every time I load the detailed report page with a large database, the client CPU usage spikes unreasonably – it all seems to be in TableX.js”

Ted says “Yeah, TableX is a widget that I got from another division.”

“OK when can we get a fix?”

“Well, there was a reorg., so it’s not clear who owns it now.  Might be a while.”

Components and Frameworks are great; we couldn’t hit our deadlines without them. However, it’s not a given that all the code that we reuse will actually support the scalability our web site requires.  Also, there is a risk that while the components can scale, the way that our code uses the components causes them to fail under heavy load.  It can be difficult to get fixes to imbedded components and can be expensive to reengineer our use of the components.  What can be done?  We all test the components that we use, we code prototypes and proof-of-concepts, but how do we avoid unfixable scalability problems many months down the road?  

One solution is simple and practical; most programmers write test code to verify the functionality of components before imbedding them in a solution and it’s possible to tweak that code then quickly retest to reduce the risk of future scale problems.  First, we can think about exactly how we plan to use the components and write our test driver to mimic that usage pattern.  Second, we generate larger set of scaffolded data to mimic the absolute maximum scalability we expect for that component. We make our test drivers, prototypes or POCs mimic what our fully scaled product would do with the components and we watch for slowness, high resource usage on the server and the client.

Nobody likes surprise scalability problems late in development, especially if the problems are almost unfixable.  It’s worth a few hours early in the development schedule to test component scalability and reduce the risks.

Monday, December 2, 2013

“You can have two for the same price as one, but you may not like either of them” …


Climbing … Tied to the other end of my rope, and 120 feet above me, Jim is standing on a ledge rigging a belay with the rope to catch me if I fall when I climb.  He’s fiddling.  Fiddling makes me nervous.   

He clips a piece of gear into the rope and stares at it; it sparkles as he flips it over, frowns then starts re-rigging again.    He just bought a new belay device (a widget), and with his busy schedule has not spent time practicing with his new toy.  I know that Jim already owns 4 older equivalent devices and he does know how to use them.  Some climbers just like buying new gear; it’s good for the economy; not so good for rising healthcare costs.  

Finally, he shrugs.  He either figured it out or gave up.  

He yells, “Climb when ready, Dave”.
 

UI Design and Climbing are similar in that there are many of cool widgets and techniques to play with.  It’s temping to add more widgets even if what we already have does the job.   Sometimes, having more really is better, but it always carries practical risks for performance because performance teams are rarely staffed to optimize the extra code.  

For example, a team writing a program to manage a customer profiles might have two basic UI options for an Update Customer Profile function:

1)       Where the user selects a Profile from a list, then selects “Edit”.
2)      Where the user opens a Profile Editor and uses a drop-down list to choose the Profile to edit.

Customers are polled: “do you like option 1 or 2?”  Results: 50% prefer option 1, 50% option 2.  In the absence of an electoral college, the easy UI design solution is to do both.  

While the functions are conceptually identical, the implementations are different.  It’s the separate sets of code that leads to performance issues:  the performance analysts are too overtaxed (who isn’t) to fully test and optimize both sets of code, so they are forced into one of two bad choices:

1)       To fully optimize one of the two options and probably make half of the customers angry.
2)      To partially optimize both and possibly make all of the customers angry. 

There is a third choice; the UI Designer has the ability to remove this extra risk of poor performance.  It goes like this: “design the UI with option 1 or with option 2 but refuse to design both.”   What makes this so often the best choice independent of their voting habits for GUI options, most user’s really want the UI be solid and FAST.

 
For best performance a UI will be nearly orthogonal – every function is done with only one set of code.  The Performance team can focus on that one bit of optimization giving it the best possible performance – the users get one FAST way to do anything.

Tuesday, November 19, 2013

Change is for the best when everything changes...

 … Thunderclouds came from the north so we first saw them overhead. Three of us were half way up a high south facing dome which earlier looked like a fun climb, but now looked like a giant lightning rod.  It was time to exit fast by sliding, one at a time, down the rope to ground.
 As the junior climber, I went first.
 Halfway down, as the rain started in earnest my partners tried to yell at me over the rain. Without hearing, I saw their angry expressions and arm waving to mean: "Move it buddy, or else!".   My habitual, safe, slow rappelling was leaving my team exposed above.
The environment had changed but I had not adjusted to the new parameters.  I went faster!

Many Java Applications Merged:

This first post is about an application design change, and an important further optimization that was  discovered sometime later.  The application began as a set of many separate Java processes with a very slick Messaging library for communication among the processes. 

After a couple of years, the hardware and JRE had advanced enough to allow all of the processes to be combined into one.  It made sense: the single merged application started faster, and used less system resources.  The functional speed of the application was better, not the expected speed boost but better; it was time to ship the product and move on.

A couple of releases later during a debug session for an unrelated issue, a javacore showed a callstack where one component was sending a message to another component in the same JVM.  At the top of the callstack, the Messaging library was preparing for a send() by serializing the message.  Code inspection showed that the Messaging library marshalled and demarshalled every message, even when the source and destination were the same JVM.  The same marshall/demarshall also happened on every message response.

The improvement was straightforward and easy:

  1. Ensure that message objects were never modified after they were sent.
  2. Skip the marshall/demarshall when the source and destination components were in the same JVM.

The benefits were significant and immediately noticeable across every function in the product in throughput and in decreased CPU usage from GC.

During the restructure, the design team had thought about the Message library but only to verify the function: that the library could handle same-JVM messages.  It was easy to miss the idea that the Message library could actually be improved as a result of the overall design change.