Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

investigate excessive memory usage while zooming #24

Closed
greensopinion opened this issue Jan 15, 2022 · 10 comments
Closed

investigate excessive memory usage while zooming #24

greensopinion opened this issue Jan 15, 2022 · 10 comments

Comments

@greensopinion
Copy link
Owner

Memory usage can spike occasionally while zooming. Sometimes it's severe enough for iOS to terminate the app.

I can reproduce this on the example app by zooming in/out in an area such as Los Angeles or San Francisco where there is a lot of detail on the map.

@greensopinion
Copy link
Owner Author

here's what I've found so far:

Vector tiles, when decoded from protobuf, consume a lot of memory, easily exceeding 1GB.
There are three GC root paths that retain these decoded tiles:

  1. the vector tile cache
  2. tiles being displayed on the map (corresponding to map widgets)
  3. tiles that are being processed (e.g. in an isolate)

During zooming the problem is exacerbated, because there are more tiles on the map, and if you zoom quickly enough you can have processing on isolates that is no longer needed due to tiles having been requested but not completed processing. The queue of work can easily get over 50.

My plan to address it:

  1. eliminate the vector tile cache completely, and in its place add a byte cache. Each tile will consume roughly 40kb, meaning that we can have ~250 tiles cached in a 10MB memory space
  2. add cancellation to processing so that obsolete jobs can be discarded quickly, freeing up related memory and eliminating unnecessary processing
  3. introduce LIFO to the execution queue, increasing the chance of having obsolete processing tasks and improving task completion latency when multiple jobs are queued
  4. consider reacting to memory pressure more aggressively by releasing vector tile data for map tiles that are visually displayed

greensopinion added a commit that referenced this issue Jan 16, 2022
Reduce memory overhead by eliminating decoded
vector tile cache.
Introduced a byte cache of vector tile data.

issue: #24
greensopinion added a commit that referenced this issue Jan 16, 2022
execute tasks in LIFO order to improve
responsiveness when a queue starts to
grow. This also incrases the probability
that some tasks become obsolete before
they are even started.

issue: #24
greensopinion added a commit that referenced this issue Jan 16, 2022
Introduce executor jobs to facilitate
additional job parameters.

issue: #24
greensopinion added a commit that referenced this issue Jan 16, 2022
Introduce executor support for job cancellation
as an optional job parameter.

issue: #24
greensopinion added a commit that referenced this issue Jan 16, 2022
eliminate unnecessary processing by cancelling
tasks that are no longer needed. This is useful
when a user interacts with the map quickly,
since tiles can be disposed before processing
completes.

issue: #24
greensopinion added a commit that referenced this issue Jan 16, 2022
make debugging more comparable to release mode
by using an isolate to process jobs.

This helps to expose issues such as job queueing.

issue: #24
greensopinion added a commit that referenced this issue Jan 16, 2022
Disable isolates since they exacerbate the
memory growth problem. Add QueueExecutor
which adds deduplication to scheduled
jobs.

issue: #24
greensopinion added a commit that referenced this issue Jan 30, 2022
eliminate use of streams, which need to be closed.
instead, provide a list of futures.

Streams may have been the source of the memory
issue that caused RSS growth.

issue #24
@greensopinion
Copy link
Owner Author

Experiment on flutter-vector-map-tiles/tree/memory-investigation

learning so far:

  • RSS usage seems to be the issue, since overall app memory usage is small, but RSS can grow to 0.8GB and higher
  • enabling background rendering can contribute to greater RSS usage
  • rarely I'll see a huge (>600MB) jump in Raster Picture memory
  • tap-to-zoom to a high zoom can reproduce the crash
  • the changes on the above branch appear to reduce likelihood of crashes

@greensopinion
Copy link
Owner Author

I've managed to find a way to reliably reproduce the issue, and have found that the raster cache grows suddenly when panning over the Vancouver subway at a high zoom (14-16)

memory-usage

Memory jumps when rendering one (or both) of the following:

flutter: painting z=14,x=2588,y=5606 at 16.0
flutter: painting z=14,x=2588,y=5606 at 17.05568382090019

More investigation is needed to determine the root cause.

@greensopinion
Copy link
Owner Author

Further experimentation has lead me to observe that Raster Picture memory usage remains nominal as long as the map zoom is not far beyond the tile zoom.

In my example, map tile data is limited to a maximum zoom of 14, whereas I have the maximum zoom of the map set to 20. The map layer will allow zooming beyond the maximum zoom of the tile data by rendering tile size 14 at the map zoom size (in this case, up to 20).

When I have the map zoomed to level 14, I don't see any Raster Picture memory usage on the memory timeline. Following is a zoom level to observed RasterPicture memory usage:

Zoom Maximum Observed Raster Picture Memory Usage
14 0
15 130MB
16 280MB
17 1.1GB

Memory usage varies depending on the tile being rendered, with some tiles needing much less than observed.

My current theory is that Canvas must allocate memory to render details outside of the visual area, and that the amount of memory used corresponds both to the size of the canvas and to the level of detail in the tile.

Increasing the difference between the tile zoom and the map zoom causes increased memory usage, so should be limited to devices that can supply the necessary memory.

A viable workaround is to limit the maximum zoom of the map to the same as the maximum available tile size, or within one level higher.

Recommendation: For maps that display on mobile devices, with a map tile with maximum zoom of 14, limit the map maximum zoom to 15.

@amenk
Copy link
Contributor

amenk commented Jun 19, 2022

My current theory is that Canvas must allocate memory to render details outside of the visual area, and that the amount of
memory used corresponds both to the size of the canvas and to the level of detail in the tile.

Can the canvas be restricted somehow to not allocate memory outside the drawing area?
At a first glance, it sounds strange, that rendering at high zoom levels consumes so much memory - because the details are usually much less then in a low zoom level.

I also saw the the renderMode setting was removed, especially "raster". A bit above raster cache is mentioned. Is this still an issue? (currently I am experimenting with maximumZoomDifference: 4)

@greensopinion
Copy link
Owner Author

Can the canvas be restricted somehow to not allocate memory outside the drawing area?

Not that I'm aware of. Feel free to open a ticket with the flutter team, perhaps this is something that they could fix.

The raster cache has been removed completely so it should no longer be an issue.
In my testing having a zoom difference greater than the default caused issues, but I left it configurable since I didn't want to presume the use-case (e.g. device memory, tile source)

@amenk
Copy link
Contributor

amenk commented Jun 19, 2022

Thanks, unfortunately I don't have the background knowledge to open a ticket with the flutter team.

Thanks for the impressive work on this module.

@greensopinion
Copy link
Owner Author

greensopinion commented Jun 20, 2022 via email

@amenk
Copy link
Contributor

amenk commented Jun 20, 2022

@greensopinion I believe the standard openmaptiles only support zoom level 14 and in our app we want to be able select positions with a higher accuracy (zoom 18)

I am wondering if this canvas problem also effects performance in other regards.
We were trying also the mapbox GL flutter package and it feel much faster (also with OSM Liberty). But maybe that's something for another thread.

@greensopinion
Copy link
Owner Author

I believe the standard openmaptiles only support zoom level 14 and in our app we want to be able select positions with a higher accuracy (zoom 18)

I'm using zoom 14 tiles at zoom 16 without issue. To go higher, I recommend looking at another source of tiles. For example, Mapbox goes to zoom level 22.

Performance of native and web-based map libraries is much better. This library has a long ways to go to get rid of jank fully. Performance is much better without memory pressure, so avoiding over-zooming will help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants