reduce computation time for adaptive_vjp_spacing via caching for GeometryGroup#3216
reduce computation time for adaptive_vjp_spacing via caching for GeometryGroup#3216groberts-flex wants to merge 1 commit intodevelopfrom
Conversation
79df115 to
ee194a7
Compare
marcorudolphflex
left a comment
There was a problem hiding this comment.
Good to have that! How many impact do we observe from that caching?
yaugenst-flex
left a comment
There was a problem hiding this comment.
Thanks @groberts-flex LGTM! Agree with @marcorudolphflex's comment that this might be nicer with a context manager, alternatively a try/finally to ensure things are cleaned up.
Only nit would be that it might be good to add a unit test to ensure that cached vs uncached adaptive_vjp_spacing results are identical. Helps catch future regressions.
It is really beneficial in geometry groups with a lot of geometries. For example, in the performance profiling test, I have a 35x35 array of geometries in a geometry group. Without caching, the |
great point, I'll definitely add a test! |
0ea2025 to
d5c9764
Compare
…aching for GeometryGroup
d5c9764 to
c2191cb
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
|
|
||
| cache_min_spacing_from_permittivity = DerivativeInfo.cache_min_spacing_from_permittivity | ||
| min_spacing_from_permittivity = DerivativeInfo.min_spacing_from_permittivity | ||
| wavelength_min = property(lambda self: DerivativeInfo.wavelength_min.fget(self)) |
There was a problem hiding this comment.
Test mock doesn't propagate cache in updated_copy
Low Severity
The SimpleDerivativeInfo.updated_copy method does not include cached_min_spacing_from_permittivity in the data dictionary being copied. The real DerivativeInfo.updated_copy() uses dataclasses.replace() which copies all fields automatically. This means the caching mechanism added in this PR won't work correctly for this test mock - when GeometryGroup._compute_derivatives creates copies via updated_copy() inside the caching context, the cached value won't propagate to child instances.
Diff CoverageDiff: origin/develop...HEAD, staged and unstaged changes
Summary
tidy3d/components/geometry/base.pyLines 3603-3611 3603
3604 vjp_dict_geo = geo._compute_derivatives(geo_info)
3605
3606 if len(vjp_dict_geo) != 1:
! 3607 raise AssertionError("Got multiple gradients for single geometry field.")
3608
3609 grad_vjps[field_path] = vjp_dict_geo.popitem()[1]
3610
3611 return grad_vjps |


adapative_vjp_spacingtakes longer to run now that we use the fulleps_datainside ofDerivativeInfo. For geometry groups this was running for every geometry and adding a significant overhead. Just caching the property didn't work because thederivative_infovariable is copied and updated with new derivative paths inGeometryGroup. Instead, we allow the variable to be cached inside ofDerivativeInfo.Note
Low Risk
Performance-only refactor with scoped caching and test coverage; main risk is subtle cache leakage or incorrect reuse, which is explicitly checked and cleared.
Overview
Speeds up autograd shape-gradient evaluation for
GeometryGroupby caching the permittivity-derived spacing used byDerivativeInfo.adaptive_vjp_spacing, avoiding recomputation for each child geometry.This refactors the spacing calculation into a new
DerivativeInfo.min_spacing_from_permittivityproperty plus a scopedcache_min_spacing_from_permittivity()context manager, and wrapsGeometryGroup._compute_derivatives()so all child derivative computations share the cached value. Tests are updated/added to ensure caching doesn’t change VJP results and that cached state is cleared after use; the changelog notes the performance improvement.Written by Cursor Bugbot for commit c2191cb. This will update automatically on new commits. Configure here.
Greptile Overview
Greptile Summary
This PR optimizes the performance of
adaptive_vjp_spacingforGeometryGroupby implementing a caching mechanism for the permittivity-based spacing calculation.Key Changes:
@cached_propertymethodmin_spacing_from_permittivityinDerivativeInfocached_min_spacing_from_permittivityfield to enable explicit caching that survivesdataclasses.replace()operationscache_min_spacing_from_permittivity()anduncache_min_spacing_from_permittivity()to control cache lifecycleGeometryGroup._compute_derivatives()to cache the value once before the loop and uncache after, so all child geometries share the same cached valueImplementation Details:
The caching approach cleverly handles the fact that
GeometryGroupcreates copies ofDerivativeInfofor each child geometry usingupdated_copy()(which usesdataclasses.replace()). Since@cached_propertystores its cache in the instance's__dict__, it doesn't survive acrossreplace()operations. The solution is to store the computed value in an explicit field (cached_min_spacing_from_permittivity) which DOES get copied duringreplace(). The@cached_propertydecorator checks this field first, so copied instances use the pre-computed value without recalculation.Performance Impact:
Previously,
adaptive_vjp_spacingwould compute the permittivity-based spacing for every geometry in aGeometryGroup, even though all geometries share the sameeps_data. With this change, it's computed once and reused across all geometries in the group.Confidence Score: 4/5
@cached_propertyanddataclasses.replace(). The confidence is 4 (not 5) because this is a subtle performance optimization involving cached properties and dataclass copies, which could benefit from additional testing to verify the performance improvement and ensure no edge cases were missed.Important Files Changed
adaptive_vjp_spacingcaching inGeometryGroupadaptive_vjp_spacingto cache permittivity-based spacing via newcached_min_spacing_from_permittivityfield and@cached_propertydecoratorGeometryGroup._compute_derivativesto enable cache sharing across geometriesSequence Diagram
sequenceDiagram participant GG as GeometryGroup participant DI as DerivativeInfo (original) participant DI_copy as DerivativeInfo (copy) participant G1 as Geometry 1 participant G2 as Geometry 2 Note over GG: _compute_derivatives called GG->>DI: cache_min_spacing_from_permittivity() activate DI DI->>DI: Access min_spacing_from_permittivity (@cached_property) DI->>DI: Compute spacing from eps_data DI->>DI: Store in cached_min_spacing_from_permittivity field deactivate DI loop For each geometry in GeometryGroup GG->>DI: updated_copy(paths, bounds, ...) DI-->>DI_copy: Create shallow copy via dataclasses.replace() Note over DI_copy: cached_min_spacing_from_permittivity<br/>is copied to new instance GG->>G1: _compute_derivatives(DI_copy) activate G1 G1->>DI_copy: adaptive_vjp_spacing() DI_copy->>DI_copy: Access min_spacing_from_permittivity Note over DI_copy: Check cached_min_spacing_from_permittivity<br/>Return cached value immediately DI_copy-->>G1: Return cached spacing G1-->>GG: Return gradient deactivate G1 end GG->>DI: uncache_min_spacing_from_permittivity() Note over DI: Set cached_min_spacing_from_permittivity = None Note over GG: All geometries used cached value,<br/>avoiding redundant computation