-
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
Optimize and simplify tile retention logic #6995
Conversation
1bee1cb
to
45bbc09
Compare
I've deployed to production with the 3 raster PRs included, and I'm getting a large number of "Cannot read property 'clearFadeHold' of undefined" exceptions, because of source_cache.js. https://sentry.io/share/issue/0c5f7a31a923428ca24a43ad1705d14c/ |
@hyperknot this is likely a bug in #6995 — will investigate. |
This is #6995 :-) |
@hyperknot pushed a commit that might fix this, can you try it out? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice catch @mourner! This looks good to me – just a few nits with the comments.
I'm pretty sure native's update_renderables
algorithm doesn't have this accidentally quadratic problem because it doesn't use a findLoadedChildren
equivalent, but it might be worth refactoring that algorithm to match the structure of this code to improve readability and comparability in the future.
src/source/source_cache.js
Outdated
let found = false; | ||
|
||
_retainLoadedChildren( | ||
tiles: {[any]: OverscaledTileID}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can the any
be replaced with number
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also can the name of this argument be more descriptive like idealTiles
or something else to avoid confusion with this._tiles
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can the any be replaced with number here?
Yes, but I'd leave this for a separate PR, since improving types for all objects that use tile IDs as keys is a pretty big change. Currently it's either any
or string | number
throughout the code because when you iterate with for (const id in obj)
, id
is always a string, and converting it everywhere is cumbersome.
// loop through ancestors of the topmost loaded child to see if there's one that needed it | ||
let tileID = topmostLoadedID; | ||
while (tileID.overscaledZ > zoom) { | ||
tileID = tileID.scaledTo(tileID.overscaledZ - 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can you use topmostLoadedID
in the conditional and have tileID be a const?
while (topmostLoadedID.overscaledZ > zoom) {
const tileID = topmostLoadedID.scaledTo(topmostLoadedID.overscaledZ-1);
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, since tileID
loops through parents — changing to the above would do a different thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh yeah 🤦♀️ its a while loop
// If the drawRasterTile has never seen this tile, then | ||
// tile.fadeEndTime may be unset. In that case, or if | ||
// fadeEndTime is in the future, then this tile is still | ||
// fading in. Find tiles to cross-fade with it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we modify and keep this comment to explain the continue
condition below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I felt this would be redundant due to the comment after that. So one would say "skip tiles not loaded or already faded in", and another "do X with tiles that are loaded but still fading in".
src/source/source_cache.js
Outdated
} | ||
} | ||
|
||
// retain any loaded children of ideal riles up to maxCoveringZoom |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit : retain any loaded children of missing ideal riles up to maxCoveringZoom
src/source/source_cache.js
Outdated
} | ||
} | ||
// As we ascend up the tile pyramid of the ideal tile, we check whether the parent | ||
// tile has been previously requested (and errored in this case due to the previous conditional) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the due to the previous conditional
part of this comment no longer makes sense here – can change to and we can infer has errored or is loading because tiles with loaded data are skipped at the beginning of this block
or something
src/source/source_cache.js
Outdated
if (tile.hasData()) continue; | ||
|
||
// The tile we require is not yet loaded or does not exist. | ||
// We are now attempting to load a parent tile if children are not enough. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This second line comment can be deleted (parent tile loading doesn't start til L614 and is already commented down there
@mourner sure, I'll do a fresh build tomorrow and push it to production and wait a day to see if any errors are appearing. |
@hyperknot how's it going? |
The new build is perfect so far, not a single error related to tiles. |
src/source/source_cache.js
Outdated
@@ -324,59 +324,64 @@ class SourceCache extends Evented { | |||
} | |||
|
|||
/** | |||
* Recursively find children of the given tile (up to maxCoveringZoom) that are already loaded; | |||
* adds found tiles to retain object; returns true if any child is found. | |||
* Retain children of the given set of tiles (up to maxCoveringZoom) that are already loaded; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this mention the zoom
parameter too? It seems to be something like:
Retain children of the given set of tiles that are loaded and have a zoom between
zoom
(exclusive) andmaxCoveringZoom
(inclusive).
src/source/source_cache.js
Outdated
} | ||
} | ||
} | ||
return found; | ||
} | ||
|
||
/** | ||
* Find a loaded parent of the given tile (up to minCoveringZoom); | ||
* adds the found tile to retain object and returns the tile if found |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This clause no longer applies.
* clean up tile retention logic a bit * simplify retention logic further * simplify raster tile retaining logic * avoid delete when retaining child tiles * much faster children retention logic * simplify parent tile retention * address PR feedback * clear up source cache comments
* clean up tile retention logic a bit * simplify retention logic further * simplify raster tile retaining logic * avoid delete when retaining child tiles * much faster children retention logic * simplify parent tile retention * address PR feedback * clear up source cache comments
While investigating #6643, I noticed that our tile retention logic (and
_findLoadedChildren
use in particular) was accidentally quadratic. It didn't manifest with vector tiles because they're big and there are only 6–8 at the time, but with small raster tile layers, where there can be 40–80 tiles loading at the same time, it blows up.This PR brings down children retention CPU time on a test fly animation from the #6643 case down from 812ms to 37ms (from quadratic to linear). This somewhat reduces jank overall (maybe 20%), but there's still a lot of remaining jank which is almost fully dominated by XHR image requests — I'll explore whether we can do something about it in another PR.
Commits in the PR are self-contained so it's easier to review them one by one, and let's rebase-merge, not squash.
Launch Checklist
write tests for all new functionalityalready covereddocument any changes to public APIs