The pointer to the next linked list node is unavailable until it has been read from the current linked list node; a software prefetch cannot invent that pointer. Once it is available, however, the current node's computation is useful independent work. Assuming the next field shares the already loaded cache line, use this symbolic loop:
p := head
while p != null:
    q := LOAD [p + next_offset]
    if q != null:
        PREFETCH [q]
    process_payload(p)
    p := q
Here process_payload must not change the next pointer, and its results must not be needed to determine q. Move the hint before this computation. The real load instruction for the next node still occurs in the next iteration and waits if the prefetch has not finished. The null test can be omitted when the hint's contract safely ignores that target; its cost, when retained, belongs in the loop overhead.
Let be the additional demand cache miss penalty per node, the independent processing time after discovering q, and the cost of issuing one hint. Ignore common loop and cache hit overhead, assume requests do not contend, and assume the prefetched cache line remains resident. Without prefetching, a long traversal takes approximately cycles per node. A hint followed by cycles of work leaves at most cycles of waiting, giving
Consequently the ideal steady-state speedup is
For , and , the comparison is . Each hint has enough independent work behind it to hide the entire additional cache miss penalty. For instead, the comparison is : most of the wait remains. With negligible the one-node-ahead model approaches a factor of two when , not an unlimited speedup.
Figure 1.
One-node-ahead software prefetch overlaps the next linked list node's transfer with processing of the current node; synthetic costs , , cycles, with the final processing interval shown only in part
. Details.
The first linked list node still has a startup miss. A node occupying several cache lines may need several hints, and a separately missing next field adds a genuine pointer chasing delay. Very short node computations, cache pollution or saturation of the data-transfer path can eliminate the benefit. The speedup comes from overlap with work on the current node; a prefetch does not remove the linked list's address dependence.

Articles by others on the same topic (0)

There are currently no matching articles.