mapbox-gl-native icon indicating copy to clipboard operation
mapbox-gl-native copied to clipboard

iOS user tracking mode = follow leads to excessive CPU usage

Open danpat opened this issue 7 years ago • 28 comments

Platform: iOS Mapbox SDK version: 3.4.0-beta.4

Steps to trigger behavior

  1. Create a map
  2. Call mapView.setUserTrackingMode(.follow, animated: true)
  3. If simulating, feed in a GPX trace. If outside, walk around.
  4. Watch 1 thread use 100% CPU
  5. Swipe the map to cancel user tracking
  6. Watch the CPU usage return to baseline

The value of animated: does not seem to have an effect on CPU usage. true/false both lead to one pegged core.

screen shot 2016-11-29 at 23 54 03

Small video showing the CPU changes with tracking mode changes with the iOS simulator:

https://www.youtube.com/watch?v=_71Hoz_7neA&feature=youtu.be

Expected behavior

Animating the map to follow the user marker shouldn't consume an entire core.

Actual behavior

My phone gets really hot and the battery drains quickly. My wife gets angry that she can't call me because my phone is dead. I get lost in the wilderness because I no longer have maps.

danpat avatar Nov 30 '16 08:11 danpat

Note that #6060 and #7125 do not address this issue because the camera changes significantly on each location update.

/cc @incanus @bsudekum @willwhite

1ec5 avatar Nov 30 '16 08:11 1ec5

I’m moving this issue to v3.5.0, because a complete fix will require beta testing and possibly changes to mbgl. However, note that #7125 remains on the milestone, and #7724 has landed.

1ec5 avatar Jan 24 '17 22:01 1ec5

This issue has been automatically detected as stale because it has not had recent activity and will be archived. Thank you for your contributions.

stale[bot] avatar Nov 27 '18 00:11 stale[bot]

Hello, do you plan to include a fix for this issue in one of the next releases? Following user location with the SDK causes this CPU spikes also in 4.9

pappalar avatar Apr 15 '19 10:04 pappalar

I'd like to point out that the same issue in the Android SDK is caused by the heavy usage of the compass component.

this Might be related?

pappalar avatar Apr 17 '19 10:04 pappalar

I'm testing version 4.10.0 on an iPhone X iOS 12.2, seems that we have spikes but profiling with instruments some are related to the core libraries but goes down immediately. The larger spikes in this chart are me playing with zooming in/out.

cpuusage

@racer1988 could you please post a screenshot of what you are seeing.

fabian-guerra avatar Apr 23 '19 22:04 fabian-guerra

@fabian-guerra This is my graph when changing the map TrackingMode

Screenshot 2019-04-24 at 10 15 05

the issue seems to be identical to the one that the reported initially raised.

Unfortunately I can't profile on the device and go around to reproduce the same. If you know how to run a GPX simulation while profiling, I'd be happy to try that.

Just by profiling and tracking user location I can see the map spiking to 50% and most of the usage is due to MapBox rendering:

I guess following user location is triggering re-rendering on the map somehow in a heavy way.

The usage of the total CPU cores in Xcode is around 50%, where Thread 1 is stuck at 100%. Memory consumption goes to very high.

Doing the same exact test with Mapkit sets the thread around 7/8 %

Screenshot 2019-04-24 at 11 13 04

pappalar avatar Apr 24 '19 09:04 pappalar

@racer1988 which map's SDK version are you testing? What's the device?

fabian-guerra avatar Apr 24 '19 16:04 fabian-guerra

Tested with both 4.9 and 4.10 This graph comes from a iPhone X

the same is visible on the simulators if using a GPX track to reproduce the location

pappalar avatar Apr 24 '19 16:04 pappalar

Using 4.9.0

Again the spikes are me zooming in/out panning cpu2

@racer1988 do you have a test app we could use to track down the issue?

fabian-guerra avatar Apr 24 '19 18:04 fabian-guerra

I currently don’t have access to my laptop. I don’t have a existing app, I would need to create a new one to share. I could check next week.

If it can help, I am hosting the map in a child view controller that is then included in the main one. Not sure how this is relevant, but might have different layout passes than a direct use of the map

pappalar avatar Apr 24 '19 20:04 pappalar

@julianrex @fabian-guerra I stripped down my production code to a basic map and I am able to reproduce in 100% of the cases.

You can find the code and reproduce yourself from here: https://github.com/racer1988/mapboxIssue

Important Notes:

  • I did not put a token, so the map is not rendered. Even with that, the issue still occurs!
  • The spike also happens while zooming in the first time by starting to track the user location. (the spike then goes down when the camera goes idle)

How to reproduce:

  • Use CocoaPod to install MapBox (and PureLayout)
  • Run the app on a random simulator
  • Simulate the location in Xcode with a random GPX: I provided 2014-04-09_2527084_test-iphone-5s_export_xcode.gpx so you can use that one directly.
  • Click on the blue button on the map to cycle between: ModeFollow -> ModeFollowWithHeading -> ModeNone

you can see the same behaviour reported by me and @danpat :

Screenshot 2019-04-29 at 14 38 37

The TrackingMode button is the following one:

Screenshot 2019-04-29 at 14 30 01

pappalar avatar Apr 29 '19 12:04 pappalar

This is what I can see when clicking tracking for the first time and the zooming kicks in:

Screenshot 2019-04-29 at 14 49 47

EDIT:

You can reproduce while profiling using the Simulator debug location (eg: Freeway ride)

Screenshot 2019-04-29 at 14 54 00

pappalar avatar Apr 29 '19 12:04 pappalar

@racer1988 thanks so much for providing a test case!

julianrex avatar Apr 29 '19 12:04 julianrex

Hi, @racer1988. Thank you for the test app.

It seems that this is related to the gpx file update frequency. I looked into our logs (you also can, take a look at this config), and realized there were 4~5 updates per second. Playing with the file to update every second I got the following chart:

onesecond

You can modify the file changing the time as it's suggested here: https://stackoverflow.com/a/36416589/1919929

In case you need, we have an API that will let you simulate location updates: https://docs.mapbox.com/ios/api/maps/4.10.0/Location%20Updates.html and an example on how to implement it is in: https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/app/MBXCustomLocationViewController.m

fabian-guerra avatar Apr 29 '19 22:04 fabian-guerra

@fabian-guerra Even at 4-5hz, if there's no map being rendered, what on earth is eating up all the CPU time? Why are location updates so CPU intensive if there's nothing to render?

danpat avatar Apr 29 '19 22:04 danpat

@danpat I'm still researching this issue. Location management technology is cpu intensive. That being said don't know if Apple has some sort of optimizations for gpx files in MapKit. This chart is from our API that simulates a location manager with a route in SF, and userTrackingMode set to follow.

mapboxlm

fabian-guerra avatar Apr 29 '19 22:04 fabian-guerra

@fabian-guerra the same behaviour in spike of CPU happens even with a fixed location when the tracking is set to follow and the map is slowly zoomed from level 1 to the max zoom level.

There the issue looks the same, however the cause can't be the location updates, as there are none. To me it looks like something else is eating CPU time

pappalar avatar Apr 29 '19 22:04 pappalar

@racer1988 a fixed location in a gpx file?

fabian-guerra avatar Apr 29 '19 22:04 fabian-guerra

Without having any knowledge of this specific issue, one optimization that @LukasPaczos implemented on Android seems worth investigating on iOS — https://github.com/mapbox/mapbox-gl-native/pull/13678 — the idea being that we attempt to update the user location too frequently and for no real/visible benefit.

friedbunny avatar Apr 29 '19 23:04 friedbunny

@fabian-guerra 2 more points here for your investigation:

  1. No, no need for a GPX.

You can simulate the issue two ways:

  • On a simulator, launch the app, go to the location picker and choose a fixed one provided by apple. then switch to TrackingFollow (from None)
  • On a Device: launch the app and switch tracking mode to zoom to your location.

Even with this simple case, the CPU spike is the same (eats 100% Thread), the inspector shows the same issue (see image at the bottom)

This is a simple case of a user wanting to zoom to his location. Even in this case, the camera updates are eating the full Thread, without anything really rendered

  1. The CPU is not going to 100% if the Location is updated without modifying the viewport even with the fast GPX file.

To simulate, start the GPX file, start following the user, then slighly pan away, to fallback to tracking None.

When the location annotation is in the viewPort, but the tracking is set to NONE, the CPU is at 0% and only didUpdateUserLocation is printed. (same if location is outside the viewport)

as soon as you set the mode to Follow then the CPU start spiking due to the region updates:

didUpdateUserLocation
regionDidChangeAnimated
regionWillChangeAnimated
didUpdateUserLocation
regionDidChangeAnimated

This point IMHO that the issue is not with the location updates, but with the update of the region in the viewPort

Screenshot 2019-04-29 at 14 49 47

pappalar avatar Apr 29 '19 23:04 pappalar

@racer1988 I did tried with a single coordinate gpx file or fixed location in the simulator. Although I see a spike and I think is normal because the map zooms in, after that it goes to zero. I'm testing the map with the token set.

In my comment https://github.com/mapbox/mapbox-gl-native/issues/7236#issuecomment-487770575 I posted how using our custom api to simulate updates it's almost unnoticeable.

Thanks for the info I will continue researching. Considering as well if we can try what it was implemented on Android as described in this comment https://github.com/mapbox/mapbox-gl-native/issues/7236#issuecomment-487773663

fabian-guerra avatar Apr 29 '19 23:04 fabian-guerra

The intensive CPU usage is due to two main factors:

Location update frequency: Xcode does not allow set a frequency update for gpx files, this can be somehow achieved by adding a timestamp to each location entry per https://stackoverflow.com/a/36416589/1919929. This behavior may be reproducible in an app using a combination of distanceFilter and desiredAccuracy in the default MGLLocationManager, but as is known getting more frequent updates drain the battery, and require more calculations (major cpu usage).

Rendering frequency: As is already pointed in this comment https://github.com/mapbox/mapbox-gl-native/issues/7236#issuecomment-487566985 updateFromDisplayLink calls our Core GL rendering stack. This stack performs a myriad of calculations leading to the "excessive" cpu usage (even tho you don't provide a token to download the tiles, the calculations are triggered anyways). Although #7125 and #7724 landed a while ago, both are platform level optimizations. It would require a significant change to our render engine to account for a render "cache" mechanism that leads to .

A workaround is change the location update frequency to let the cpu finish the job.

In this pr https://github.com/mapbox/mapbox-gl-native/pull/14577 I'm exploring another platform level optimization. In the pr I added a point based threshold that will move the camera thus triggering the render engine once the userAnnotationView.center property crossed it.

This is how it looks with a 20point threshold, and using the gpx file. While this optimization seems viable rises questions on if it's worth a lag feeling for camera updates, and how it will behave for followWithCourse.

threshold

In conclusion I suggest change the location update frequency.

fabian-guerra avatar May 02 '19 23:05 fabian-guerra

Has there been any progress on this? We're running into this with a turn by turn issue (using react-native-mapbox-gl)

dorthwein avatar Jul 18 '19 15:07 dorthwein

bump to this. not only user tracking is affected, but [source setShape: shape]; too

noway avatar Feb 10 '20 11:02 noway

Also experiencing crash logs due to excessive cpu usage during turn-by-turn navigation:

Event:           cpu usage
Action taken:    Process killed
CPU:             48 seconds cpu time over 59 seconds (81% cpu average), exceeding limit of 80% cpu over 60 seconds
CPU limit:       48s
Limit duration:  60s
CPU used:        48s
Duration:        7.03s
Steps:           8

datwelk avatar Mar 30 '20 09:03 datwelk

any updated on this?

juliopiubello avatar Feb 10 '21 15:02 juliopiubello

I'm seeing this in a React Native app too, but interestingly, I'm also seeing the exact same behaviour in a pure SwiftUI app. On a simulator running either app, as soon as I set tracking mode to follow, the CPU goes through the roof. I can't reproduce the issue on my iPhone XR so perhaps this is a simulator-only problem? Certainly wouldn't be the first 😤

developius avatar Feb 26 '21 01:02 developius