Manages value interdependencies for a particular value or set of values in a Context.
A DependencyTracker ("tracker" for short) provides notifications of changes to the managed value to downstream subscribers, and may invalidate an associated cache entry. The "managed value" can be a source like time or state, or a cached computation. A particular tracker is selected using a DependencyTicket ("ticket") which provides very fast access to the tracker. The ticket is used by both the System and Context as a way to identify dependencies, while trackers exist only in the Context.
Each DependencyTracker manages dependencies for a value, or group of related values, upon which some downstream computations may depend, and maintains lists of downstream dependents (subscribers) and upstream prerequisites. An optional CacheEntryValue may be registered with a tracker in which case the tracker will mark the cache value out of date when one of its prerequisites has changed.
A single DependencyTracker can represent interdependencies within its subcontext, and to and from other subcontexts within the same containing Context tree. Trackers are always owned by a DependencyGraph that is part of a particular subcontext, and should always be created through methods of DependencyGraph; don't construct them directly yourself.
DependencyTracker objects within a Context are nodes in a directed acylic graph formed by "is-prerequisite-of" edges leading from source values (like time, state, parameters, and input ports) to dependent cached computations and output ports. A DependencyTracker maintains lists of both its downstream subscribers and its upstream prerequisites. The entries in both lists are pointers to other DependencyTrackers. That requires special handling when cloning a Context, since the internal pointers to the DependencyTracker objects in the source must be replaced by their corresponding pointers in the copy.
DependencyTrackers may simply group upstream values, without representing a new value or computation. For example, the three continuous state subgroups q, v, and z are each associated with their own DependencyTracker. There is also a tracker that monitors changes to any variable within the entire collection of continuous variables xc≜{q,v,z}
; that tracker subscribes to the three individual trackers. Similarly, individual discrete variable groups dᵢ collectively determine the discrete state xd≜{dᵢ}
, individual abstract state variables aᵢ determine the abstract state xa≜{aᵢ}
, and the full state is x≜{xc,xd,xa}
. Here is a graph showing time and state trackers and some hypothetical cache entry trackers.
(q)--------➙(position kinematics) ➘ (v)--➙(xc)--- (time)----➙(xc_dot) (z)--➚ ╲ ➚ ➘ ╱ (d₀)--➙(xd)---➙(x)---------- (d₁)--➚ ➚ ╱ (a₀)--➙(xa)--- (a₁)--➚
The parenthesized nodes are DependencyTrackers for the indicated values, and a directed edge (a)->(b)
can be read as "a is-prerequisite-of b" or "a determines b". The graph also maintains reverse-direction edges (not shown). A reversed edge (a)<-(b)
could be read as "b subscribes-to a" or "b depends-on a".)
These grouped trackers simplify dependency specification for quantities that depend on many sources, which is very common. For example, they allow a user to express a dependence on "all the inputs" without actually having to know how many inputs there are, which might change over time. Grouped trackers also serve to reduce the total number of edges in the dependency graph, providing faster invalidation. For example, if there are 10 computations dependent on q, v, and z (which frequently change together) we would have 30 edges. Introducing (xc) reduces that to 13 edges.
Downstream computations may subscribe to any of the individual or grouped nodes.
#include <drake/systems/framework/dependency_tracker.h>
Public Types | |
using | PointerMap = std::unordered_map< const DependencyTracker *, const DependencyTracker * > |
(Internal use only) More... | |
Public Member Functions | |
const std::string & | description () const |
Returns the human-readable description for this tracker. More... | |
std::string | GetPathDescription () const |
Returns the description, preceded by the full pathname of the subsystem associated with the owning subcontext. More... | |
DependencyTicket | ticket () const |
Returns the DependencyTicket for this DependencyTracker in its containing DependencyGraph. More... | |
void | set_cache_entry_value (CacheEntryValue *cache_value) |
(Internal use only) Sets the cache entry value to be marked out-of-date when this tracker's prerequisites change. More... | |
const CacheEntryValue * | cache_entry_value () const |
(Internal use only) Returns a pointer to the CacheEntryValue if this tracker is a cache entry tracker, otherwise nullptr. More... | |
void | suppress_notifications () |
(Internal use only) Instructs this tracker to suppress notifications to downstream subscribers. More... | |
bool | notifications_are_suppressed () const |
Returns true if suppress_notifications() has been called on this tracker. More... | |
void | NoteValueChange (int64_t change_event) const |
Notifies this DependencyTracker that its managed value was directly modified or made available for mutable access. More... | |
Does not allow copy, move, or assignment | |
DependencyTracker (const DependencyTracker &)=delete | |
DependencyTracker & | operator= (const DependencyTracker &)=delete |
DependencyTracker (DependencyTracker &&)=delete | |
DependencyTracker & | operator= (DependencyTracker &&)=delete |
Prerequisites and subscribers | |
These methods deal with dependencies associated with this tracker. | |
void | SubscribeToPrerequisite (DependencyTracker *prerequisite) |
Subscribes this tracker to an upstream prerequisite's tracker. More... | |
void | UnsubscribeFromPrerequisite (DependencyTracker *prerequisite) |
Unsubscribes this tracker from an upstream prerequisite tracker to which we previously subscribed. More... | |
void | AddDownstreamSubscriber (const DependencyTracker &subscriber) |
Adds a downstream subscriber to this DependencyTracker, which will keep a pointer to the subscribing tracker. More... | |
void | RemoveDownstreamSubscriber (const DependencyTracker &subscriber) |
Removes a downstream subscriber from this DependencyTracker. More... | |
bool | HasPrerequisite (const DependencyTracker &prerequisite) const |
Returns true if this tracker has already subscribed to prerequisite . More... | |
bool | HasSubscriber (const DependencyTracker &subscriber) const |
Returns true if subscriber is one of this tracker's subscribers. More... | |
int | num_prerequisites () const |
Returns the total number of "depends-on" edges emanating from this tracker, pointing to its upstream prerequisites. More... | |
const std::vector< const DependencyTracker * > & | prerequisites () const |
Returns a reference to the prerequisite trackers. More... | |
int | num_subscribers () const |
Returns the total number of "is-prerequisite-of" edges emanating from this tracker, pointing to its downstream subscribers. More... | |
const std::vector< const DependencyTracker * > & | subscribers () const |
Returns a reference to the subscribing trackers. More... | |
Runtime statistics | |
These methods track runtime operations and are useful for debugging and for performance analysis. | |
int64_t | num_notifications_received () const |
What is the total number of notifications received by this tracker? This is the sum of managed-value change event notifications and prerequisite change notifications received. More... | |
int64_t | num_ignored_notifications () const |
How many times did we receive a repeat notification for the same change event that we ignored? More... | |
int64_t | num_notifications_sent () const |
What is the total number of notifications sent to downstream subscribers by this trackers? More... | |
int64_t | num_value_change_events () const |
How many times was this tracker notified of a change event for a direct change to a value it tracks? More... | |
int64_t | num_prerequisite_change_events () const |
How many times was this tracker notified of a change to one of its value's prerequisites? More... | |
Testing/debugging utilities | |
Methods used in test cases or for debugging. | |
void | ThrowIfBadDependencyTracker (const internal::ContextMessageInterface *owning_subcontext=nullptr, const CacheEntryValue *cache_value=nullptr) const |
Throws an std::exception if there is something clearly wrong with this DependencyTracker object. More... | |
Friends | |
class | DependencyGraph |
using PointerMap = std::unordered_map<const DependencyTracker*, const DependencyTracker*> |
(Internal use only)
|
delete |
|
delete |
void AddDownstreamSubscriber | ( | const DependencyTracker & | subscriber | ) |
Adds a downstream subscriber to this
DependencyTracker, which will keep a pointer to the subscribing tracker.
The subscriber will be notified whenever this DependencyTracker is notified of a value or prerequisite change.
const CacheEntryValue* cache_entry_value | ( | ) | const |
(Internal use only) Returns a pointer to the CacheEntryValue if this tracker is a cache entry tracker, otherwise nullptr.
const std::string& description | ( | ) | const |
Returns the human-readable description for this tracker.
std::string GetPathDescription | ( | ) | const |
Returns the description, preceded by the full pathname of the subsystem associated with the owning subcontext.
bool HasPrerequisite | ( | const DependencyTracker & | prerequisite | ) | const |
Returns true
if this tracker has already subscribed to prerequisite
.
This is slow and should not be used in performance-sensitive code.
bool HasSubscriber | ( | const DependencyTracker & | subscriber | ) | const |
Returns true
if subscriber
is one of this tracker's subscribers.
This is slow and should not be used in performance-sensitive code.
void NoteValueChange | ( | int64_t | change_event | ) | const |
Notifies this
DependencyTracker that its managed value was directly modified or made available for mutable access.
That is, this is the initiating event of a value modification. All of our downstream subscribers are notified but the associated cache entry (if any) is not invalidated (see below for why). A unique, positive change_event
should have been obtained from the owning Context and supplied here.
Why don't we invalidate the cache entry? Recall that this method is for initiating a change event, meaning that the quantity that this tracker tracks is initiating an invalidation sweep, as opposed to just reacting to prerequisite changes. Normally cache entries become invalid because their prerequisites change; they are not usually the first step in an invalidation sweep. So it is unusual for NoteValueChange() to be called on a cache entry's dependency tracker. But if it is called, that is likely to mean the cache entry was just given a new value, and is therefore valid; invalidating it now would be an error.
bool notifications_are_suppressed | ( | ) | const |
Returns true if suppress_notifications() has been called on this tracker.
int64_t num_ignored_notifications | ( | ) | const |
How many times did we receive a repeat notification for the same change event that we ignored?
int64_t num_notifications_received | ( | ) | const |
What is the total number of notifications received by this tracker? This is the sum of managed-value change event notifications and prerequisite change notifications received.
int64_t num_notifications_sent | ( | ) | const |
What is the total number of notifications sent to downstream subscribers by this trackers?
int64_t num_prerequisite_change_events | ( | ) | const |
How many times was this tracker notified of a change to one of its value's prerequisites?
int num_prerequisites | ( | ) | const |
Returns the total number of "depends-on" edges emanating from this
tracker, pointing to its upstream prerequisites.
int num_subscribers | ( | ) | const |
Returns the total number of "is-prerequisite-of" edges emanating from this
tracker, pointing to its downstream subscribers.
int64_t num_value_change_events | ( | ) | const |
How many times was this tracker notified of a change event for a direct change to a value it tracks?
|
delete |
|
delete |
const std::vector<const DependencyTracker*>& prerequisites | ( | ) | const |
Returns a reference to the prerequisite trackers.
void RemoveDownstreamSubscriber | ( | const DependencyTracker & | subscriber | ) |
Removes a downstream subscriber from this
DependencyTracker.
void set_cache_entry_value | ( | CacheEntryValue * | cache_value | ) |
(Internal use only) Sets the cache entry value to be marked out-of-date when this tracker's prerequisites change.
const std::vector<const DependencyTracker*>& subscribers | ( | ) | const |
Returns a reference to the subscribing trackers.
void SubscribeToPrerequisite | ( | DependencyTracker * | prerequisite | ) |
Subscribes this
tracker to an upstream prerequisite's tracker.
The upstream tracker will keep a const pointer back to this
tracker in its subscriber list, and this
tracker will keep a pointer to the prerequisite tracker in its prerequisites list.
void suppress_notifications | ( | ) |
(Internal use only) Instructs this tracker to suppress notifications to downstream subscribers.
This can be used by the framework during Context allocation to disable built-in trackers that have nothing to track. For example, if there are no q's we can improve performance and avoid spurious notifications to q-subscribers like configuration_tracker by disabling q's tracker.
void ThrowIfBadDependencyTracker | ( | const internal::ContextMessageInterface * | owning_subcontext = nullptr , |
const CacheEntryValue * | cache_value = nullptr |
||
) | const |
Throws an std::exception if there is something clearly wrong with this DependencyTracker object.
If the owning subcontext is known, provide a pointer to it here and we'll check that this tracker agrees. If you know which cache entry is supposed to be associated with this tracker, supply a pointer to that and we'll check it (trackers that are not associated with a real cache entry are still associated with the CacheEntryValue::dummy()). In addition we check for other internal inconsistencies.
std::exception | for anything that goes wrong, with an appropriate explanatory message. |
DependencyTicket ticket | ( | ) | const |
Returns the DependencyTicket for this DependencyTracker in its containing DependencyGraph.
The ticket is unique within the containing subcontext.
void UnsubscribeFromPrerequisite | ( | DependencyTracker * | prerequisite | ) |
Unsubscribes this
tracker from an upstream prerequisite tracker to which we previously subscribed.
Both the prerequisite list in this
tracker and the subscriber list in prerequisite
are modified.
prerequisite
.
|
friend |