How to update a Dynamic object when only a specific Part of an Association changes?

The following example shows three dynamic objects that all update when the value associated with the "d" key of association assoc is changed:

assoc = <|"d" -> 5, "e" -> 5, "f" -> 5|>;
d = e = f = 0;
{Dynamic[d = d + 1; assoc["d"], TrackedSymbols :> {assoc}],
Dynamic[e = e + 1; assoc["e"], TrackedSymbols :> {assoc}],
Dynamic[f = f + 1; assoc["f"], TrackedSymbols :> {assoc}]}
Dynamic[{d, e, f}]
Do[assoc["d"] = 1; assoc["d"] = 2, 100000];

Since two of these objects clearly do not depend on the value associated with "d" of assoc, unnecessary updating is occurring.

The following is a reformulation that performs fewer updates by avoiding use of associations:

assocD = assocE = assocF = 5;
d = e = f = 0;
{Dynamic[d = d + 1; assocD, TrackedSymbols :> {assocD}],
Dynamic[e = e + 1; assocE, TrackedSymbols :> {assocE}],
Dynamic[f = f + 1; assocF, TrackedSymbols :> {assocF}]}
Dynamic[{d, e, f}]
Do[assocD = 1; assocD = 2, 100000];

I would prefer to use associations as opposed to downvalues and pattern-matching, as associations are typically expected to be much faster and offer convenient data manipulation functions. Note that there may by many more than the 3 independent Dynamic elements shown in this example, and the association may hold many more unrelated entries. Are there any formulations with associations that would perform similarly?

Note that this does not work: TrackedSymbols :> {assoc["d"]}

Mathematica 11.0.1

Below are some sub-optimal partial solutions:

Method 1: Use a separate Dynamic object to copy elements of the association assoc to unique symbols whenever any element of assoc changes, and use these symbols to update each main Dynamic object separately:

assoc = <|"d" -> 5, "e" -> 5, "f" -> 5|>;
d = e = f = 0;
Dynamic[assocD = assoc["d"]; assocE = assoc["e"];
assocF = assoc["f"]; "Update Object", TrackedSymbols :> {assoc}]
{Dynamic[d = d + 1; assocD, TrackedSymbols :> {assocD}],
Dynamic[e = e + 1; assocE, TrackedSymbols :> {assocE}],
Dynamic[f = f + 1; assocF, TrackedSymbols :> {assocF}]}
Dynamic[{d, e, f}]
Do[assoc["d"] = 1; assoc["d"] = 2, 100000];

This method allows the variables to be managed in a large association along with other unrelated data, but doubles memory requirements for elements of the association that need to be copied for use in the Dynamics, along with some computation to perform the copying.

Method 2: Manually update unique "trigger" symbols whenever a change is made to the association assoc:

assoc = <|"d" -> 5, "e" -> 5, "f" -> 5|>;
d = e = f = 0;
refreshD = refreshE = refreshF = False;
{Dynamic[refreshD; d = d + 1; assoc["d"], TrackedSymbols :> {refreshD}],
Dynamic[refreshE; e = e + 1; assoc["e"], TrackedSymbols :> {refreshE}],
Dynamic[refreshF; f = f + 1; assoc["f"], TrackedSymbols :> {refreshF}]}
Dynamic[{d, e, f}]
Do[assoc["d"] = 1; refreshD = ! refreshD; assoc["d"] = 2; refreshD = ! refreshD, 100000];

This method uses less memory than Method 1 since only additional Boolean values are needed, avoiding copies of actual data, but requires the programmer to explicitly trigger updates.