Fer
Fer

Reputation: 4196

Mysterious severe performance issue on mobile Safari for just one web page

I have a very large (as in feature-rich) responsive website. It consists of over 150 different UI pages, and so far both rendering and performance on mobile are fine (I'm using an iPhone5 to test, and occasionally other devices).

Except for one page, which I am coding now. Here's the temporary dev URL:

http://www.jungledragon.org/apps/jd3/daylight

On Mobile Safari, this page performance extremely poorly: - It takes several seconds to load, much slower than all other pages - Once loaded, a touch scroll can take 5-10 secs to do anything - Mobile Safari as a whole becomes non responding or close to it

I'm trying to troubleshoot the root cause of the issue, but no luck so far. I cannot reproduce this on any desktop browser using a small viewport, not even on desktop Safari. On the desktop, I've inspected several web debuggers to check for any long-running processes, but found none.

Some explanation on what the page does:

  1. It will try to detect your current location (using alerts I discovered this takes little time)
  2. Based on your current location and the current date, it will calculate the sun times for the day. This too is nearly instant
  3. Based on the suntimes, it will dynamically generate a table, and then finally show it on screen

Here's the what I am seeing in detail on mobile Safari:

  1. The server response is fine, the page loads quickly and shows the site header soon
  2. Next, the content body is blank and stays blank for several seconds (which I cannot explain)
  3. Finally, the suntimes table renders.
  4. This completes the page, yet as of this point, the page as well as the browser are extremely sluggish, scrolling takes forever, and Safari controls are nearly irresponsive. It looks and feels as if the browser can crash any moment.

Based on my research so far, and given fine performance in all other pages on the site, I'm totally in the dark on what causes this.

Edit: Using BrowserStack I did some more tests:

So I'm not seeing the issue on any desktop browser, and on no mobile device except for the iPhone 5 (iOS7).

Edit2: adding more findings and explanation based on comments received:

The issue does not seem animation-related. For this I have a number of proof points. A simple proof point is the page does not do any visual rendering that is much different from any of the other 100+ pages on the site which have no performance issue.

The 2nd proof point can be explained by understanding what is going on in this specific page. What happens is this:

  1. The system will detect the current user's time and location. For now assume that the user actually allows location sharing. Using a simple alert, I've been able to proof that location detection is not the bottleneck.

  2. Based on the user's time and location, the daylight periods are calculated. This is done by using the Suncalc JS library (https://github.com/mourner/suncalc).

  3. The Suncalc library returns an array of daylight periods for the given date and location. I render that array as a table with colored background rows. That is all.

Rendering a table with 12 rows and different background colors is not likely to cause such enormous issues. My theory therefore lies in step 2 being the root cause. The Suncalc library has a lot of advanced math in it. I am thinking (without evidence yet) that either my mobile processor is horrible at those kind of operations, and/or the specific calculation for some reason cause a peak in memory usage (or even a leak).

As an additional proof point: once the page is loaded on mobile, use the right arrow next to the date to navigate to "tomorrow". Again you will see the extremely bad performance. During that step, there is no network activity, no location detection, nothing, just calculations and some very simple rendering. This validates my theory that perhaps the issue lies in the calculation.

Upvotes: 1

Views: 542

Answers (4)

brokethebuildagain
brokethebuildagain

Reputation: 2191

So I've played with this some more, and ran the "Timeline" feature on Chrome (load this file into your chrome timeline tool: https://www.dropbox.com/s/2vpl6z1ntuk3aqj/TimelineRawData-20140328T105820.json), and it looks like this might be your main problem.

Your scripts and libs (including loading Google Maps and jQuery) are getting evaluated AFTER parsing the HTML and running Google Analytics because they are at the bottom of the body, not head. Unless you have a very good reason to do that, I would recommend moving those to the head.

There seems to be a separate problem with scrolling, but perhaps it will be resolved by this change.

Upvotes: 0

brokethebuildagain
brokethebuildagain

Reputation: 2191

Just ran this through Chrome remote debugger (https://developers.google.com/chrome-developer-tools/docs/remote-debugging) on my S3, and it looks like Modernizr's cancelZoom function (showing up in jd3_0006.js) is getting called recursively too many times or by too broad a selector. I've uploaded the profiles into dropbox: https://www.dropbox.com/s/kubxk44smm6qqkx/jungledragon_debug..zip

You can import them into Chrome's debugger on the "Profiles" tab.

Upvotes: 1

David H. Bennett
David H. Bennett

Reputation: 1842

Sadly, it looks like native Javascript profilers on that platform are non-existent. You may also want to try the Javascript Microtime function referenced in this answer. You will need to seed your script with calls at points where you think the bottleneck might be.

Upvotes: 1

David H. Bennett
David H. Bennett

Reputation: 1842

I believe your performance problem centers around the use of navigator.geolocation.getCurrentPosition() in your runMap() function

if (urlDate != null) {
    urlPos(latitude,longitude);
} else {
    if (navigator.geolocation) {
        $(".img-loading").show(100);
        navigator.geolocation.getCurrentPosition(successPos, errorPos{maximumAge:600000,timeout:10000});
    } else {
        errorPos('');
    }
} 

Consider using watchPosition() instead with a callback which will not halt processing of the script thread. You can cancel the watchPostion() update by using clearWatch()

Upvotes: 0

Related Questions