Touchstart performance issues in Google Chrome

October 3, 2013

After hours of troubleshooting a weird problem with a javascript app that I’m building, I finally found the bug, which was totally unexpected. I don’t think I would’ve found it if I hadn’t started to checkout older commits so I could pin point when the problem started to occur.

But first, let me explain what had happened, the app that I’m building is very CPU intensive, before I started to optimize it, it took about 20 seconds to render the page, after I applied one work around for the rendering, I got the rendering time down to 3 seconds.

Once I had implemented most of the functionality I started to test it on various touch devices, obviously the app was loading slowly on those devices because of their limited CPU performance, I didn’t think much about it and concentrated on fixing positioning bugs.

After a few commits I was back on my computer and this was when I noticed that the rendering time had increased a tenfold. From 3 seconds, it now took almost 40 seconds to render the same thing, I hadn’t touched any of the rendering code, so I was quite dumbfounded when I tried to understand what had happened.

I did some profiling with the developer tools in the chrome browser, which was totally unhelpful, it was pointing at the same function that I had previously optimized with the for/settimeout loop. In desperation I started to do the most crazy optimizations that I’ve ever done, caching almost everything I could, but to no avail. I was able to shave off a few seconds, but the rendering still took about 35 seconds to complete.

Not seeing any improvements, I decided that this was the wrong way to approach the problem, I stashed my work and started to checkout older commits to systematically track down the offending code. After a few tries I finally found the culprit, but looking at the changes I found nothing suspicious that could effect the rendering, the only thing that I had done was some smaller CSS changes and added touchstart events to all click events.

Just for laughs I removed the touchstart events, and bam, back at sub 4 second renderings. I added back one touchstart after the other, whilst checking when the rendering started to deteriorate. Finally I could pin point where the problem occured, if an item has a touchstart eventlistener and you then check the offsets relative to its parent, you’ll take on a severe performance penalty in comparsion to a click event.

To demonstrate this problem, I created a similar situation, but removing all the extra surrounding logic and rendering, as well as rewriting everything in clean vanilla JS to ensure that the problem wasn’t with jQuery.

// Fetch all items with class name "item"
var items = document.querySelectorAll('.item');
var values = [];

// Add an event listener to every item
for(var i = 0; i < items.length; i++){
	items[i].addEventListener('click', function(){console.log('hi')});
}

// Record the start time
var start = new Date()
// Loop through all items and check the offsetTop property
for(var i = 0; i < items.length; i++){
	values.push( items[i].offsetTop )
}
// Notify user of total time spent in the loop
alert( 'Took a total of ' + ( new Date() - start ) + 'ms to loop through the item\'s offset' );

After creating a thousand randomly positioned li’s, I added a click event to each and every one of the items, and then proceeded with checking the offsetTop of each item, the time to loop through the items and read the offset property took about 10-30ms.

I then changed out the event to a touchstart event, and again looped through the items and checked the offset, the result? 140-180ms, a massive performance hit, and the only difference is the kind of event binded to the element.

I’m not qualified to say if this is a browser bug or not, but I really don’t believe that the type of events added to an element should alter the time it takes to check its position.

For the curious ones, I’ve created the two scenarios in jsfiddle, the first one using a click event and the second one using a touchstart event, remember to use chrome when trying it out (I’ve tested it on Google Chrome version 29).

Tags