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.