Fault count is not latency
I spent two weeks optimising the wrong number. Lazy restore in CRIU pulls memory pages on demand, so the obvious thing to reduce is the number of page faults the restored process takes before it can answer a request. Sequential prefetch reduces that count enormously. On loopback it removed 85% of the faults, and made time-to-first-request 88% worse.
The reason is not subtle once you accept it. At loopback, a fault is nearly free; the transfer is the cost. Prefetching spends bandwidth and queue depth on pages the process may never touch, and the pages it does need queue behind that waste. The fault counter goes down because faults were never the thing being paid for.
Everything inverts once the link has real latency. Around 125 microseconds of RTT the two strategies cross, and by 1 ms prefetch is 37% faster; at 2 ms demand-only does not finish at all. So the honest statement is not "prefetch helps" or "prefetch hurts" but that the decision belongs to the network, and therefore has to be made at runtime rather than at build time.
The controller I ended up with watches duplicate pressure and queue depth per fault window and switches prefetch off when it sees waste accumulating. It recovers the loopback regression to within 5.5 ms of demand-only, which is statistically indistinguishable, and still cuts prefetch volume by half on memory-light workloads. It is a small piece of code. The expensive part was believing a graph that disagreed with the metric I had chosen.
Corollary I now apply everywhere: a proxy metric is only allowed to stand in for the real one after you have measured the regime where it stops tracking.