-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
Improve LRU cache behavior #4210
Conversation
Actually I've thought about this a bit more and I think my line of reasoning is partially flawed. The current cache behavior is correct, considering its intent is to only cache tiles that are no longer rendered on the map. The part that's now incorrectly missing from my PR is removing tiles from the cache once they're rendered on the map again. In that case, an alternate proposal is to ensure that the I'll update this PR today :) |
I've updated this PR with the changes I mentioned! My thinking behind this code is that the LRU cache's API should be explicit and predictable. Having the |
3937a47
to
a821967
Compare
@chrisvoll thanks a lot for the PR Chris! We'll check this out. |
@mourner Just wanted to quickly follow up on this — I'm happy to help out in any way. Thanks! :) |
@chrisvoll I'm nervous about merging this PR. The tile retention logic in |
That is a good compromise @chrisvoll. I would be much more comfortable accepting that PR 😄 |
thanks for the feedback! I'll close this in favor of #4311 |
Background
A few days ago, we noticed some performance issues with raster tiles on our maps. We were seeing some cases where tiles were reloading when they should have already been cached. For instance, in this jsbin, a ring of tiles reloads pretty consistently when zooming in and then back out:
Problem
We dove into the code and noticed that the LRU tile cache was discarding tiles when it wasn't supposed to. Based on the commit history, it looks like it was once an MRU cache, and still has some of the behavior of an MRU cache, where
get
operations delete the key and its data.Normally this wouldn't have any significant consequences, but the cache is accessed more than once: here in
findLoadedParent
, and here inaddTile
. The first call infindLoadedParent
purges the tile from the cache, so the call inaddTile
came up empty-handed and led to the tile being reloaded.Solution
My PR changes the behavior of the cache to function like an LRU cache:
get
operations will now move the key to the top of theorder
stack instead of deleting it.This works pretty well, but it uncovers an unrelated issue that gives us a beautiful artistic impression of the map we want:(my incorrect cache changes in earlier commits contributed to this, but my analysis below about the cache size still seems accurate)What I've found is that the LRU cache size isn't always set to the correct value for raster tiles. In
SourceCache
,updateCacheSize
is sometimes given misleading data. In the jsbin above, for instance,transform
hastileSize: 512
(the dimensions of the retina-sized image tiles), but they're rendered at256px
. It may think that there is a 4x4 grid of tiles (16 total), for example, when it's actually 8x8 (64 total). Then you get a cache size of80
instead of320
.The cache runs into its limit pretty quickly, and starts discarding tiles that are still present in
SourceCache._tiles
. Their textures are then recycled so newer tiles can use them. The textures are overwritten, but the olderSourceCache._tiles
tiles still reference them, leading to the screenshot above.My PR doesn't fix the underlying issue (the incorrect cache size—I'm not sure how to approach this without potentially breaking something else), but
adds a change that prevents textures from being recycled if a tile is purged from the cache but remains in(I removed this in subsequent changes). Now the tiles are happily cached and zooming is smoother.SourceCache._tiles
Launch Checklist
This was my first big dive into the mapbox-gl-js code, so hopefully everything I mentioned here is accurate and makes sense! I'm more than happy to answer any questions or make any changes as needed.
Benchmarks