Optimizing Google AnnotatedTimeLine Updating
When I added a bunch of additional values to my web app, I noticed that the updating of the Google AnnotatedTimeLine was taking a lot longer than before. I mean it was pausing for a good 3 sec. before updating the view. I looked at the CPU usage, and it was all in the Flex component. It was bad. Very bad. So I knew I had to do something about it.
In the original version of the page, I had code that was run when the data was received and the graph redrawn. This was a standard hook for the AnnotatedTimeLine:
/** * This function is called when the ATL is done updating the graph * from the data and the "draw()" method. */ function graphReady() { // hide all the unchecked data sets for (var i = 0; i < portfolioChecks.length; ++i) { if (!portfolioChecks[i].checked) { updateBackgroundVisibility(portfolioNames[i], false); } } // ...finish up with more processing } /** * This is called to update the visibility of the named portfolio * to the provided state in the background graph. */ function updateBackgroundVisibility(name, state) { var colCnt = graphData.getNumberOfColumns(); // I have to find the column name in the table for (var i = 1; i < colCnt; ++i) { if (graphData.getColumnLabel(i) == name) { // the dataset number is one less than the column number if (state) { chart[bg].showDataColumns(i-1); } else { chart[bg].hideDataColumns(i-1); } } } }
When I originally built the code, I wanted to have something that allowed me to change the visibility of the data sets in the graph(s) either way, and that's really nice. But what I didn't expect was the performance penalty I would pay for such a design.
As it turns out, there's a form of the hideDataColumns() method on the ATL that allows the user to send an array of dataset indexes. I didn't know what to expect, but I thought it had to be better than this, so I recoded this as:
/** * This function is called when the ATL is done updating the graph * from the data and the "draw()" method. */ function graphReady() { // hide all the unchecked data sets var cols = []; for (var i = 0; i < portfolioChecks.length; ++i) { if (!portfolioChecks[i].checked) { cols.push(i-i); } } chart[bg].hideDataColumns(cols); // ...finish up with more processing }
Without the call to updateBackgroundVisibility(), I knew it'd be a little faster - no need to look up all the column headers, etc. But I didn't expect to see what I saw.
The resulting code took the refresh time from 2-3 sec. to under the blink of an eye. Really. It was a slight flicker, but that's about it. Amazing. In retrospect, it makes sense... I was individually hiding about 30 columns each update. That's a lot. To do it right, there had to be some type of lock, update, refresh/redraw, and then unlock. All that added up. Big time.
It's taught me a big lesson about the efficiency of Google's code: if in doubt, look for a better way, there's probably one there, or you can always ask the visualization team. I know I've learned my lesson.