mapbox / mapbox-gl-native

Interactive, thoroughly customizable maps in native Android, iOS, macOS, Node.js, and Qt applications, powered by vector tiles and OpenGL
https://mapbox.com/mobile
Other
4.36k stars 1.33k forks source link

iOS user tracking mode = follow leads to excessive CPU usage #7236

Open danpat opened 7 years ago

danpat commented 7 years ago

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.

1ec5 commented 7 years ago

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

/cc @incanus @bsudekum @willwhite

1ec5 commented 7 years ago

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.

stale[bot] commented 5 years ago

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

pappalar commented 5 years ago

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 commented 5 years ago

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?

fabian-guerra commented 5 years ago

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.

pappalar commented 5 years ago

@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

fabian-guerra commented 5 years ago

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

pappalar commented 5 years ago

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

fabian-guerra commented 5 years ago

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?

pappalar commented 5 years ago

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 commented 5 years ago

@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:

How to reproduce:

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 commented 5 years ago

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

julianrex commented 5 years ago

@racer1988 thanks so much for providing a test case!

fabian-guerra commented 5 years ago

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

danpat commented 5 years ago

@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?

fabian-guerra commented 5 years ago

@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

pappalar commented 5 years ago

@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

fabian-guerra commented 5 years ago

@racer1988 a fixed location in a gpx file?

friedbunny commented 5 years ago

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.

pappalar commented 5 years ago

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

1) No, no need for a GPX.

You can simulate the issue two ways:

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

2) 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

fabian-guerra commented 5 years ago

@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 commented 5 years ago

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.

dorthwein commented 5 years ago

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

noway commented 4 years ago

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

datwelk commented 4 years ago

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
juliopiubello commented 3 years ago

any updated on this?

developius commented 3 years ago

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 😤