Drop the redundant canTransform in getShapeTransformCache - #3803
Open
agentperfopt wants to merge 1 commit into
Open
Drop the redundant canTransform in getShapeTransformCache#3803agentperfopt wants to merge 1 commit into
agentperfopt wants to merge 1 commit into
Conversation
getShapeTransformCache rebuilds on every octomap/sensor update and walks the TF tree twice per shape-bearing frame: canTransform first (a check), then lookupTransform (the actual compose). lookupTransform already composes the transform and throws tf2::TransformException if it's unavailable, which the function's existing try/catch already handles, so the canTransform check duplicates work lookupTransform does anyway. Drop it and call lookupTransform directly inside the existing try/catch. Cache contents are unchanged: lookupTransform returns the same transform whether or not canTransform was called first. One real behavioral difference worth being explicit about: canTransform(..., shape_transform_cache_lookup_wait_time_) also waits up to that duration (a ROS parameter, "<robot_description>_planning.shape_transform_cache_lookup_wait_time", defaulting to 0.05s) for a not-yet-available transform. Dropping it removes that wait. When a transform is available at target_time (the normal case), behavior is identical. If one is momentarily late, the current code waits up to the configured duration; this throws immediately, and the try/catch returns false for that update (the next sensor update tries again). The wait-time parameter itself becomes a no-op, since this was its only reader.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
getShapeTransformCacherebuilds on every octomap/sensor update and walks the TF tree twice per shape-bearing frame:canTransform(a check) thenlookupTransform(the actual compose).lookupTransformalready composes the transform and throwstf2::TransformExceptionif it's unavailable, which the function's existing try/catch already handles — so the check is redoing work the compose call does anyway. This drops it and callslookupTransformdirectly for the link, attached-body, and collision-body cases.Results
Real
tf2::BufferCore, cache build timed directly, us/call:Scales with both shape count and TF-tree depth (1.53x at depth 0 to 1.56x at depth 16), so it's a bigger win on robots with more collision shapes and deeper transform trees — which is also where the cache build is slowest to begin with.
Correctness
Cache output is bit-identical:
lookupTransformreturns the same transform regardless of whethercanTransformwas called first, so the checksum over all cached shape transforms matches exactly.Two behavioral differences worth being precise about rather than glossing over:
canTransform(..., shape_transform_cache_lookup_wait_time_)also waits up to that duration for a not-yet-available transform. That duration is a real, user-facing ROS parameter (<robot_description>_planning.shape_transform_cache_lookup_wait_time, defaulting to 0.05s) — dropping the call makes that parameter a no-op, since this was its only reader. Behavior is identical whenever the transform is already available attarget_time(the normal case); if one is momentarily late, the current code waits up to 50ms for it, this throws right away and the try/catch returnsfalsefor that update, and the next sensor update tries again.Something I noticed while making this change, not something the profiling data flagged: in the current code, each
canTransformguards only its own frame'sifblock, so if one link's transform isn't available, that link's shapes are skipped but every other link, attached body, and collision handle still gets processed normally. After this change, an unavailable transform for any one of them throws out of the whole function immediately, through the outer try/catch, skipping everything after it in the same call, not just the one shape that failed. Both still returnfalsefor that update either way, so the round-trip cadence is the same, but the current code's "skip just the one late frame, still fill in what's available" behavior becomes "skip everything remaining in this pass" here. I don't have a moveit2/tf2 build in this environment to characterize how often that actually matters in practice (it depends on how frequently individual frames go briefly stale relative to the whole batch), so flagging it precisely rather than asserting it's inconsequential.Checklist
tf2::BufferCoreFound by an automated tool that profiles merged PRs for headroom the PR itself didn't cover. The wait-time trade-off in point 1 was flagged by that tool's own analysis; point 2 is mine, from reading the control flow directly.