-
Notifications
You must be signed in to change notification settings - Fork 16
Implement Track<> copy-constructor and backing graph-copy infrastructure #205
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
Conversation
|
Thanks for the detailed description! |
|
Defining the So from an interface perspective, I think encapsulating it into I guess my preference would be to keep it as an "under-the-hood" detail, but if we want to prioritize the memory footprint then I can try to factor it out. |
|
I agree hiding complexity from the end user is a good thing.
What if Track owned a (mutable) CloneContext by reference (UniquePtr)? Then
it could be empty, with minimal payload for all normal use cases, but could
be instantiated at the beginning of the clone process (and cleared at the
end).
…On Tue, Aug 26, 2025 at 8:07 AM Ed Callaghan ***@***.***> wrote:
*edcallaghan* left a comment (KFTrack/KinKal#205)
<#205 (comment)>
Defining the CloneContext as a Track member was an interface decision. I
don't see a reason that passing in an external context wouldn't also work,
it just shifts more responsibility onto "users." As a Track member,
someone only needs to know about the CloneContext class if they try to
copy an implementation which doesn't have overloaded clones, and thus
needs to implement them. If the context is passed from outside, then anyone
who wants to make a copy needs to learn figure out what it is, instantiate
one, and not misuse it after the copy is completed, even though the
implementation is already complete.
So from an interface perspective, I think encapsulating it into Track' is
a good thing. Of course the price is that that extra payload gets lugged
around wherever the Track` goes. We could clear the map after the copy
completes, to keep it light, but because that has to happen after any
subclass-level copy functionality completes, it would be at the mercy of
subclass implementations.
I guess my preference would be to keep it as an "under-the-hood" detail,
but if we want to prioritize the memory footprint then I can try to factor
it out.
—
Reply to this email directly, view it on GitHub
<#205 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABAH572GWMIR5S4PKSIC4Y33PRZ27AVCNFSM6AAAAACEPXIWEGVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTEMRUGU3TIOJTGQ>
.
You are receiving this because you commented.Message ID:
***@***.***>
--
David Brown ***@***.***
Office Phone (510) 486-7261
Lawrence Berkeley National Lab
M/S 50R5008 (50-6026C) Berkeley, CA 94720
|
|
Latest push stores the context as a |
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.
Since CloneContext is exposed to every class except Track, the value of keeping it as a 'hidden' data member of Track seems marginal. I also worry about the state of the embeded CloneContext being indeterminate. Instead please consider having only clone functions (not copy constructors) all down the line, and require them all to have a CloneContext. The CloneContext lifetime can then be managed externally, which is the only place that makes sense. That seems more consistent with your overall design.
Trajectory/PiecewiseTrajectory.hh
Outdated
| auto rv = std::make_shared< PiecewiseTrajectory<KTRAJ> >(); | ||
| for (auto const& ptr : pieces_){ | ||
| auto piece = context.get(ptr); | ||
| rv->append(*piece); |
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.
append performs geometric operations and matches time ranges. Pushing cloned traj segments directly into pieces_ should produce a more literal copy.
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 has been adopted. I wasn't sure about the functionality inside append/prepend, but a quick test with a simpler copy scheme seems to work just as well.
brownd1978
left a comment
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.
A few more comments...
I've explored the road of avoiding a copy-constructor, and it doesn't lead somewhere clean. The simplest reason is that constructors are naturally accommodating of subclasses (i.e., a user doesn't actually call |
brownd1978
left a comment
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.
Thanks for making the context external, I believe that will improve long-term flexibility.
Please review the 'set*' functions, my experience has been these can be replaced by appropriate copy and operator = calls, resulting in cleaner (if slightly less efficient) code. If they are truely necessary, see if they can't be made private, as they always open the possibility of a user creating a self-inconsistent object.
I know this PR has been merged into 206, please make changes there.
Detector/ParameterHit.hh
Outdated
| unsigned nDOF() const override { return ncons_; } | ||
| Parameters const& constraintParameters() const { return params_; } | ||
| PMASK const& constraintMask() const { return pmask_; } | ||
| Weights pweight() const { return pweight_; }; |
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.
Is there a reason you duplicated constraintMask function?
These new accessors have cryptic names, can they be improved?
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 added these methods very mechanically and without much thinking --- the duplicate has been removed and the names of the others are now more descriptive.
Detector/ResidualHit.hh
Outdated
|
|
||
| template <class KTRAJ> class ResidualHit : public Hit<KTRAJ> { | ||
| public: | ||
| // clone op for reinstantiation |
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.
Is this comment still relevant?
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.
Commentary has been revised to be relevant and accurate.
Examples/StrawXing.hh
Outdated
| StrawXing(PCA const& pca, StrawMaterial const& smat); | ||
| virtual ~StrawXing() {} | ||
| // clone op for reinstantiation | ||
| // ejc TODO does typeof(tpca_) == ClosestApproach<> need a deeper clone? |
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.
Is this comment still relevant?
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.
Commentary has been revised to be relevant and accurate.
Fit/DomainWall.hh
Outdated
| auto const& prevWeight() const { return prevwt_; } | ||
| auto const& nextWeight() const { return nextwt_; } | ||
| auto const& fwdCovarianceRotation() const { return dpdpdb_; } | ||
| void setPrevPtr(DOMAINPTR const& ptr){ prev_ = ptr; } |
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 these set functions be replaced by simply constructing a new object from 2 Ptrs?
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.
A non-public constructor could maybe be worked out, but I've now reduced the access of these methods to private.
|
With one exception, newly-added public setter methods have been changed to private access. The exception is of course |
brownd1978
left a comment
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.
Thanks for making the updates, this looks good now.
This PR implements a copy-constructor for the
Track<>class template. Track objects contain references to stateful members in the form of "hits," "element crossings," and "effects," the types of which subclassKinKal-defined bases, and which may contain references to each other. The construction of an equivalent, but independent and consistentTrack<>thus requires reinstantiation of the full relationship graph among its members, the struture of which is defined by the subclass implementations. This is achieved via a recursive, memoized cloning scheme, which requires all track members to provide a reinstantiation (clone()) method, which should make use of a common reallocation record (CloneContext) to ensure that each graph node is only reallocated once; but this simple implementation does not accommodate reference cycles or e.g. explicit self-references.New classes:
CloneContext: Contains a hash table used to map addresses of to-be-copiedTrack<>-members to the addresses of their newly-allocated clones. Reallocation is done lazily, so that an attempt to lookup the address of an unreallocated object triggers its reallocation via itsclone()implementation, which may itself involve address lookups andclone()calls as necessary.New functionality:
Track<>class template now contains aCloneContextmember, which is used in an explicit copy-constructor to coordinate cloning of member objects. This context isprotectedso as to be available to subclasses ofTrack<>, which may need access to the reallocation-map to consistently clone members strictly of the subclass.Hit<>,ElementXing<>, andEffect<>class templates now each have defaultclone()methods which throw an exception indicating that explicit cloning must be implemented. No correct default implementation can be provided because arbitrary references in subclasses allow for arbitrary graph structures, for which no infrastructure exists to track. A default exception is preferable to, e.g., a pure-virtual method in that it allows users implementing subclasses to avoidclone()implementations unless they actually need to use them, which provides a lower barrier for adoption and prevents e.g. providing (incorrect) empty implementations just to satisfy the interface requirement, which would then later cause problems if a copy is eventually attempted.clone()methods are provided forKinKal-level objects, e.g.Domain,DomainWall, andParameterHit.clone()methods are provided for the exampleScintHit,SimpleWireHit, andStrawXingclass templates.PiecewiseTrajectory<>andResidualHit<>.