Bug Smash: Never take anything for granted

I noticed yesterday that Davenport was running rather sluggishly if there were a lot of drawer tabs open on the top bar. That would be understandable if 'having a lot of tabs open' meant the app was having to do a lot of extra work, but it doesn't. Unlike, say, Chrome, where tabbed webpages can still be playing sound or downloading images or running scripts, tabs in Davenport are quite literally just a coloured rectangle with a piece of text on it. And in case you hadn't noticed, Davenport is pretty good at drawing coloured rectangles with text on them.

So I opened the profiler window, and this is what I saw:


 The height of the graph is the amount of time it's taking to prepare and update the screen, and the colours represent different activities like running scripts and drawing things. From left to right is the time the app has been running. See if you can spot the moment when I opened nine tabs.

As you can see, the Unity UI load jumped from virtually nothing to around 16 milliseconds. 16ms might not sound like a lot, but when you're trying to keep the app running at a smooth 60 frames per second, you only have 16 ms in which to do everything.

After a lot of turning things off and on and messing about, I discovered that it was all down to a single line of code:
spacer.transform.SetAsLastSibling();
If you've used Davenport, you'll know you can drag tabs to rearrange them. I use Unity's UI to manage this for me, and to that end there's an invisible 'spacer' element that spends most of its life on the right end of the tab bar. When you're dragging a tab, Davenport moves the spacer into position under the tab you're dragging, so that you can see where the tab will go when you drop it.

It turns out that simply telling the spacer to be the last thing on the tab bar each update was forcing Unity to do a massive amount of recalculation, including working out all over again how much text should be visible in each tab - even though the spacer was already the last thing on the tab bar and nothing was changing.

With that craziness dealt with, I confess the temptation to do a bit more profiling was too strong. I decided to do a quick pass over the note parsing (the bit where LISA checks to see what effect your most recent change has had on the formatting or timeline of your notes). Here, one of the most oft-repeated operations is looking to find a tag terminator: these are characters that are not allowed to be part of a tag (punctuation, spaces and so on) and are important for LISA's comprehension.

I had been storing all the tag terminator characters in a string, and running through that string each time I needed to check to see if what I'd reached was a tag terminator. Changing that to a look-up table (storing a 'true' or 'false' for each possible character to indicate whether it is a terminator) sped up note parsing by almost three times. And it wasn't exactly sluggish to begin with, so that gives me confidence in Davenport's ability to scale well with larger projects.

Right, that's enough playing around with optimisations for the day - back to working on my book!

Comments

Popular Posts