An optimizer can remove an empty C loop whose execution has no effect. That is useful in production code, but it makes the loop impossible to time as written. A measurement must preserve the work it intends to measure. section 16.4
Three loop designs resist elimination, but measure different work:
- A volatile loop counter forces operations on the counter; GCC and Clang emit repeated loads and stores. section 16.4
- A nonvolatile counter with a volatile store preserves the loop with less overhead. section 16.4
- An index calculation whose result is opaque to the compiler preserves a loop closer to the measured computation. section 16.4
Listing 16.7 Instrumenting three for loops with struct timespec
timespec_get(&t[0], TIME_UTC);
/* Volatile for i ensures that the loop is effected */
for (uint64_t volatile i = 0; i < iterations; ++i) {
/* do nothing */
}
timespec_get(&t[1], TIME_UTC);
/* s must be volatile to ensure that the loop is effected */
for (uint64_t i = 0; i < iterations; ++i) {
s = i;
}
timespec_get(&t[2], TIME_UTC);
/* Opaque computation ensures that the loop is effected */
for (uint64_t i = 1; accu0 < upper; i += 2) {
accu0 += i;
}
timespec_get(&t[3], TIME_UTC);
Compare the three timed C loops: counter qualification, a volatile store, and an opaque accumulation each prevent elimination differently. section 16.4
TAKEAWAY: Inspect generated instructions as well as timings. The volatile-store loop measured about 81% less time per iteration than the volatile-counter loop; their overhead is not interchangeable. section 16.4





























