Skip to main content

Modules/timemodule.c (part 7)

Source:

cpython 3.14 @ ab2d84fe1023/Modules/timemodule.c

This annotation covers high-resolution timers and sleep. See modules_time6_detail for time.time, time.gmtime, time.mktime, and time.strftime.

Map

LinesSymbolRole
1-80time.sleepSleep with signal safety
81-160time.monotonicMonotonically increasing clock
161-240time.perf_counterHighest-resolution clock for benchmarking
241-360time.process_timeCPU time used by the process
361-500time.get_clock_infoMetadata about each clock

Reading

time.sleep

// CPython: Modules/timemodule.c:480 pysleep
static int
pysleep(_PyTime_t timeout)
{
_PyTime_t deadline = _PyTime_Add(_PyTime_GetMonotonicClock(), timeout);
do {
/* Check for signals between sleep intervals */
if (Py_MakePendingCalls() < 0) return -1;
_PyTime_t remaining = _PyTime_Add(deadline,
-_PyTime_GetMonotonicClock());
if (remaining <= 0) return 0;
/* Platform sleep */
#ifdef MS_WINDOWS
DWORD ms = (DWORD)_PyTime_AsMilliseconds(remaining, _PyTime_ROUND_CEILING);
Py_BEGIN_ALLOW_THREADS
Sleep(ms);
Py_END_ALLOW_THREADS
#else
struct timespec ts;
_PyTime_AsTimespec(remaining, &ts);
Py_BEGIN_ALLOW_THREADS
clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
Py_END_ALLOW_THREADS
#endif
} while (1);
}

time.sleep(1.5) releases the GIL via Py_BEGIN_ALLOW_THREADS while sleeping. If a signal arrives and interrupts the sleep, Py_MakePendingCalls processes it and the loop recalculates the remaining time against the deadline.

time.monotonic

// CPython: Modules/timemodule.c:620 time_monotonic
static PyObject *
time_monotonic(PyObject *self, PyObject *unused)
{
_PyTime_t t = _PyTime_GetMonotonicClock();
return _PyTime_AsFloat(t);
}

time.monotonic() uses clock_gettime(CLOCK_MONOTONIC) on Linux, mach_absolute_time on macOS, QueryPerformanceCounter on Windows. It never goes backward, even when the system clock is adjusted. Not suitable for wall-clock time differences.

time.perf_counter

// CPython: Modules/timemodule.c:660 time_perf_counter
static PyObject *
time_perf_counter(PyObject *self, PyObject *unused)
{
_PyTime_t t = _PyTime_GetPerfCounter();
return _PyTime_AsFloat(t);
}

time.perf_counter() is the highest-resolution clock available. On Linux, it uses CLOCK_MONOTONIC (typically nanosecond resolution). On Windows, QueryPerformanceCounter. The origin is arbitrary; only differences are meaningful.

time.process_time

// CPython: Modules/timemodule.c:700 time_process_time
static PyObject *
time_process_time(PyObject *self, PyObject *unused)
{
_PyTime_t t;
#if defined(MS_WINDOWS)
FILETIME creation, exit, kernel, user;
GetProcessTimes(GetCurrentProcess(), &creation, &exit, &kernel, &user);
t = _PyTime_FromWindowsFileTime(&user) + _PyTime_FromWindowsFileTime(&kernel);
#else
struct timespec ts;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
t = _PyTime_FromTimespec(&ts);
#endif
return _PyTime_AsFloat(t);
}

time.process_time() counts CPU time, not wall-clock time. Sleeping or blocking on I/O does not advance it. Useful for profiling CPU-bound code in the presence of I/O.

time.get_clock_info

# CPython: Modules/timemodule.c:780 time_get_clock_info
# Returns a namespace: resolution, implementation, monotonic, adjustable
# time.get_clock_info('monotonic').resolution → 1e-09 (nanoseconds on Linux)
# time.get_clock_info('time').adjustable → True (system clock can be changed)

get_clock_info returns metadata about each named clock so callers can check resolution and whether the clock is adjustable before relying on it for precision timing.

gopy notes

time.sleep is module/time.Sleep in module/time/module.go. It calls time.Sleep(duration) in Go but checks for pending signals between intervals. time.monotonic calls time.Now().UnixNano() via a monotonic-clock-backed time.Time. time.perf_counter uses runtime.nanotime via unsafe.