4.4 Full Tree Analysis: Formal Definition
4.4.2 Cache Operations
The first set of functions to be defined concern the normal operation of the cache. As previously stated, a typical cache algorithm can be broken up into three operations:
1. Classification: Determining if a cache access is a hit or a miss
2. Eviction and Replacement : Determining a cache line to evict and re- place the contents with a new cache line. For a PLRU cache, this is implemented by following the pointers in the cache tree.
3. Touch: Determining the actions needed to prolong the life of an ele- ment in the cache. For a PLRU cache, this is implemented by setting each pointer on the path to the selected element away from the ele- ment.
However, in addition to the normal definition for a PLRU cache, these operations must be defined to work on the abstract representation, where both cache line index location and pointer values may be uncertain. The next sections define the relevant formal functions.
Classification
Using the concretisation function, it is possible to construct a classification function cs. The classification function simply determines if a memory access is a hit, miss or unclassifiable, and does this by inspecting all concrete states that the cache may occupy. Hence the classification function is defined as follows: cs(t, m) = hit if ∀t0 ∈ c(t), m ∈ t0 miss if ∀t0 ∈ c(t), m 6∈ t0 notclassif ied otherwise
Evict And Replace
The eviction and replacement operator takes a cache state and evicts the cache line as indicated by the pointers. In the case of a pointer which is unknown, the eviction operator must take into account both possibilities that arise. This may result in many successor cache states from a single cache state; however, this is not a problem as later compression is used to reduce the number of states. Further, the eviction operation must also take into account that it might not be known if a cache line is in a cache state, and hence eviction must update the cache appropriately; this can be accomplished by exhausting all possible locations for the cache line and creating appropriate copies of the state where each of these assumptions is true. To do this, it is necessary to define the purge function, which removes all references to a cache line from a subtree. Note that this also handles the case that the location of a memory block is unknown (e.g. from an initial unknown state), where the cache state will not correspond to a single behaviour and hence sets of potential memory blocks have a size greater than 1. purge(hl, v, ri, m) = hl, v \ {m}, ri if height(hl, v, ri) = 0 hpurge(l, m), v, purge(r, m)i otherwise
(4.7)
Note that the purge function deletes information, and is only usable if all possible locations of m are considered.
Given the requirements, it is possible to define the insertion function insert, which takes an abstract state and maps it to the set of states given by inserting m at the location given by the pointers.
insert(hl, v, ri, m) =
{hl, {m}, ri} if height(hl, v, ri) = 0 {hinsert(l, m), v, ri} if v = 0 {hl, v, insert(r, m)i} if v = 1
insert(hl, 0, ri, m) ∪ insert(hl, 1, ri, m) if v =⊥ (4.8) As the insert function does not check on the presence of m within the cache state, it can only be applied in the case of a guaranteed miss. Othe-
wise, an insertion of memory block m is not guaranteed to result in a definite location within the cache. Hence the evict function is defined as follows, which also provides the necessary logic to apply evictions as necessary.
evict(s, m) = {s} if cs(s) = hit insert(s, m) if cs(s) = miss {s} ∪ insert(purge(s, m), m) otherwise (4.9)
The cases of the function ensure that the eviction is only applied in the cases when it is necessary; in the first case this ensures that eviction is not applied if the access is guaranteed to be a hit. In the second case, a guaranteed miss, insert is used to get the appropriate set of cache states where m has been inserted to the locations the pointers indicate. In the third case, where the access is not classified, the result is the union of both the previous cases, but purge is used to construct a state where the assumption of a miss is true.
Touch
The touch operation sets all pointers to be pointing away from the path to a given cache line index. The difficulty in this function is that the abstract representation may not be able to give a precise location for a cache line index. If at a given stage, it is not certain which subtree a cache line index, m, is contained in, it is necessary to consider both possibilities. This is accomplished in a similar manner to the evict function, by utilising the purge function to assert where the possible location could be. Hence, it is possible to define the touch function, which maps an abstract cache state to a set of a successor states as follows, assuming that the touched cache line index m is indeed a member of the cache, and utilising the classification function cs:
touch(hl, v, ri, m) =
{hl, v, ri} if height(hl, v, ri) = 0 {htouch(l, m), 1, ri} if cs(l, m) = hit {hl, 0, touch(r, m)i} if cs(r, m) = hit {htouch(l, m), 1, purge(r, m)i,
hpurge(l, m), 0, touch(r, m)i } otherwise
(4.10) Note that the touch function exploits the fact that for two cache subtrees S0, S1 of the same height, which belong to the same cache state, cs(S0, m) =
hit ⇒ cs(S1, m) = miss as if m is definitely in S0 it cannot be in S1 as well.
Hence the final case only occurs when cs(l, m) = cs(r, m) = notclassif ied. Also note that in cache operation, whenever touch is called it is guaranteed that the cache line m will be in the cache at some location, even if that location is not precisely known.