Monotonic vs Real-Time Wall Clocks in C
In C programming, choosing the correct clock type is critical for ensuring reliable timekeeping, benchmarking, and scheduling. The primary operational difference between a monotonic clock and a real-time wall clock lies in how they respond to system time changes: a real-time clock measures calendar time and can jump forward or backward due to manual updates or Network Time Protocol (NTP) synchronizations, while a monotonic clock continuously advances at a steady rate from an arbitrary point, guaranteeing that time never moves backward.
Real-Time Wall Clocks:
CLOCK_REALTIME
A real-time wall clock measures standard calendar time, traditionally
represented in POSIX C systems as elapsed seconds and nanoseconds since
the Unix Epoch (00:00:00 UTC on January 1, 1970). It is accessed
primarily through functions like
clock_gettime(CLOCK_REALTIME, &ts) or
gettimeofday().
Key Operational Characteristics:
- Subject to Jumps: If a system administrator changes
the system time, or if NTP applies a step correction to fix clock drift,
CLOCK_REALTIMEimmediately jumps to the new value. - Non-Linear Deltas: Calculating elapsed time using
t2 - t1with a real-time clock can produce negative durations, zero, or artificially massive intervals if a time adjustment occurs between measurements. - Leap Seconds and Time Zones: While the underlying value is UTC, wall-clock representations interact with daylight saving rules, timezone configurations, and leap seconds.
Best Use Cases:
- Displaying human-readable timestamps (e.g., file modification times, logging events with calendar dates).
- Synchronizing events across different machines that must agree on absolute UTC time.
#include <stdio.h>
#include <time.h>
void log_current_time(void) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
printf("Epoch Time: %ld.%09ld seconds\n", ts.tv_sec, ts.tv_nsec);
}Monotonic Clocks:
CLOCK_MONOTONIC
A monotonic clock represents elapsed time from an unspecified
starting reference point—typically the moment the operating system
booted. It is queried via
clock_gettime(CLOCK_MONOTONIC, &ts).
Key Operational Characteristics:
- Guaranteed Directionality: The clock cannot move backward. Each successive read will yield a value equal to or greater than the preceding read.
- Immunity to Manual Adjustments: Alterations to the system calendar time do not affect the monotonic timeline.
- NTP Frequency Slew vs. Step: Standard
CLOCK_MONOTONICmay be slewed (its frequency slightly sped up or slowed down) by NTP to eliminate drift, but it will never step-jump. For scenarios requiring absolute raw hardware frequency without NTP slewing, Linux provides theCLOCK_MONOTONIC_RAWvariant.
Best Use Cases:
- Performance profiling, benchmarking, and execution timing.
- Implementing timeouts, retry loops, and sleep durations in network sockets or multithreaded locks.
- Event loop rate-limiting and frame-rate regulation in graphics and game engines.
#include <stdio.h>
#include <time.h>
void measure_duration(void) {
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
// Work to be timed occurs here
clock_gettime(CLOCK_MONOTONIC, &end);
double elapsed = (end.tv_sec - start.tv_sec) +
(end.tv_nsec - start.tv_nsec) / 1e9;
printf("Elapsed: %.9f seconds\n", elapsed);
}Summary of Differences
| Feature | CLOCK_REALTIME |
CLOCK_MONOTONIC |
|---|---|---|
| Reference Point | Unix Epoch (Jan 1, 1970 UTC) | Arbitrary (typically system boot) |
| Monotonicity | No (can jump backward or forward) | Yes (strictly non-decreasing) |
| Affected by NTP Steps | Yes | No |
| Affected by Admin Edits | Yes | No |
| Primary Purpose | Absolute date and time stamping | Relative time measurement and intervals |