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_REALTIME immediately jumps to the new value.
  • Non-Linear Deltas: Calculating elapsed time using t2 - t1 with 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_MONOTONIC may 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 the CLOCK_MONOTONIC_RAW variant.

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