Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Include/pytime.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,22 @@ PyAPI_FUNC(int) _PyTime_localtime(time_t t, struct tm *tm);
Return 0 on success, raise an exception and return -1 on error. */
PyAPI_FUNC(int) _PyTime_gmtime(time_t t, struct tm *tm);

#ifdef MS_WINDOWS
PyAPI_FUNC(int) _PyTime_GetWinPerfCounterWithInfo(
_PyTime_t *t,
_Py_clock_info_t *info);
#endif

/* Get the performance counter: clock with the highest available resolution to
measure a short duration. */
PyAPI_FUNC(_PyTime_t) _PyTime_GetPerfCounter(void);

/* Similar to _PyTime_GetPerfCounter(),
but get also clock info if info is non-NULL. */
PyAPI_FUNC(int) _PyTime_GetPerfCounterWithInfo(
_PyTime_t *t,
_Py_clock_info_t *info);

#ifdef __cplusplus
}
#endif
Expand Down
71 changes: 23 additions & 48 deletions Modules/timemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ Fractions of a second may be present if the system clock provides them.");
#endif
#endif

static PyObject*
_PyFloat_FromPyTime(_PyTime_t t)
{
double d = _PyTime_AsSecondsDouble(t);
return PyFloat_FromDouble(d);
}

static PyObject *
floatclock(_Py_clock_info_t *info)
{
Expand All @@ -81,47 +88,19 @@ floatclock(_Py_clock_info_t *info)
}
#endif /* HAVE_CLOCK */

#ifdef MS_WINDOWS
#define WIN32_PERF_COUNTER
/* Win32 has better clock replacement; we have our own version, due to Mark
Hammond and Tim Peters */
static PyObject*
win_perf_counter(_Py_clock_info_t *info)
{
static LONGLONG cpu_frequency = 0;
static LONGLONG ctrStart;
LARGE_INTEGER now;
double diff;

if (cpu_frequency == 0) {
LARGE_INTEGER freq;
QueryPerformanceCounter(&now);
ctrStart = now.QuadPart;
if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) {
PyErr_SetFromWindowsErr(0);
return NULL;
}
cpu_frequency = freq.QuadPart;
}
QueryPerformanceCounter(&now);
diff = (double)(now.QuadPart - ctrStart);
if (info) {
info->implementation = "QueryPerformanceCounter()";
info->resolution = 1.0 / (double)cpu_frequency;
info->monotonic = 1;
info->adjustable = 0;
}
return PyFloat_FromDouble(diff / (double)cpu_frequency);
}
#endif /* MS_WINDOWS */

#if defined(WIN32_PERF_COUNTER) || defined(HAVE_CLOCK)
#if defined(MS_WINDOWS) || defined(HAVE_CLOCK)
#define PYCLOCK
static PyObject*
pyclock(_Py_clock_info_t *info)
{
#ifdef WIN32_PERF_COUNTER
return win_perf_counter(info);
#ifdef MS_WINDOWS
/* Win32 has better clock replacement; we have our own version, due to Mark
Hammond and Tim Peters */
_PyTime_t t;
if (_PyTime_GetWinPerfCounterWithInfo(&t, info) < 0) {
return NULL;
}
return _PyFloat_FromPyTime(t);
#else
return floatclock(info);
#endif
Expand Down Expand Up @@ -939,13 +918,11 @@ static PyObject *
pymonotonic(_Py_clock_info_t *info)
{
_PyTime_t t;
double d;
if (_PyTime_GetMonotonicClockWithInfo(&t, info) < 0) {
assert(info != NULL);
return NULL;
}
d = _PyTime_AsSecondsDouble(t);
return PyFloat_FromDouble(d);
return _PyFloat_FromPyTime(t);
}

static PyObject *
Expand All @@ -962,11 +939,11 @@ Monotonic clock, cannot go backward.");
static PyObject*
perf_counter(_Py_clock_info_t *info)
{
#ifdef WIN32_PERF_COUNTER
return win_perf_counter(info);
#else
return pymonotonic(info);
#endif
_PyTime_t t;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idea for avoiding the redundant double -> _PyTime_t -> double conversion in time.perf_counter():

What if this still kept a compile time conditional branch between the Windows API and the *nix one?

if (_PyTime_GetPerfCounterWithInfo(&t, info) < 0) {
return NULL;
}
return _PyFloat_FromPyTime(t);
}

static PyObject *
Expand Down Expand Up @@ -1448,13 +1425,11 @@ static PyObject*
floattime(_Py_clock_info_t *info)
{
_PyTime_t t;
double d;
if (_PyTime_GetSystemClockWithInfo(&t, info) < 0) {
assert(info != NULL);
return NULL;
}
d = _PyTime_AsSecondsDouble(t);
return PyFloat_FromDouble(d);
return _PyFloat_FromPyTime(t);
}


Expand Down
4 changes: 2 additions & 2 deletions Python/import.c
Original file line number Diff line number Diff line change
Expand Up @@ -1695,7 +1695,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,

if (ximporttime) {
import_level++;
t1 = _PyTime_GetMonotonicClock();
t1 = _PyTime_GetPerfCounter();
accumulated = 0;
}

Expand All @@ -1711,7 +1711,7 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
mod != NULL);

if (ximporttime) {
_PyTime_t cum = _PyTime_GetMonotonicClock() - t1;
_PyTime_t cum = _PyTime_GetPerfCounter() - t1;

import_level--;
fprintf(stderr, "import time: %9ld | %10ld | %*s%s\n",
Expand Down
81 changes: 71 additions & 10 deletions Python/pytime.c
Original file line number Diff line number Diff line change
Expand Up @@ -285,8 +285,8 @@ _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv, int raise)
#endif

static int
_PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round,
long unit_to_ns)
_PyTime_FromDouble(_PyTime_t *t, double value, _PyTime_round_t round,
long unit_to_ns)
{
/* volatile avoids optimization changing how numbers are rounded */
volatile double d;
Expand Down Expand Up @@ -315,7 +315,7 @@ _PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round,
PyErr_SetString(PyExc_ValueError, "Invalid value NaN (not a number)");
return -1;
}
return _PyTime_FromFloatObject(t, d, round, unit_to_ns);
return _PyTime_FromDouble(t, d, round, unit_to_ns);
}
else {
long long sec;
Expand Down Expand Up @@ -779,19 +779,80 @@ _PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info)
return pymonotonic(tp, info, 1);
}


#ifdef MS_WINDOWS
int
_PyTime_Init(void)
_PyTime_GetWinPerfCounterWithInfo(_PyTime_t *t, _Py_clock_info_t *info)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this accepted a double *t as the output parameter?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

{
static LONGLONG cpu_frequency = 0;
static LONGLONG ctrStart;
LARGE_INTEGER now;
double diff;

if (cpu_frequency == 0) {
LARGE_INTEGER freq;
QueryPerformanceCounter(&now);
ctrStart = now.QuadPart;
if (!QueryPerformanceFrequency(&freq) || freq.QuadPart == 0) {
PyErr_SetFromWindowsErr(0);
return -1;
}
cpu_frequency = freq.QuadPart;
}
QueryPerformanceCounter(&now);
diff = (double)(now.QuadPart - ctrStart);
if (info) {
info->implementation = "QueryPerformanceCounter()";
info->resolution = 1.0 / (double)cpu_frequency;
info->monotonic = 1;
info->adjustable = 0;
}

diff = diff / (double)cpu_frequency;
return _PyTime_FromDouble(t, diff, _PyTime_ROUND_FLOOR, SEC_TO_NS);
}
#endif


int
_PyTime_GetPerfCounterWithInfo(_PyTime_t *t, _Py_clock_info_t *info)
{
#ifdef MS_WINDOWS
return _PyTime_GetWinPerfCounterWithInfo(t, info);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the call to _PyTime_FromDouble was moved into this wrapper function?

It wouldn't be ideal, since the #ifdef logic would exist in two places, but maybe that duplication could be handled via a common macro definition that was used in both places, rather than by incurring the redundant conversion at runtime on Windows.

#else
return _PyTime_GetMonotonicClockWithInfo(t, info);
#endif
}


_PyTime_t
_PyTime_GetPerfCounter(void)
{
_PyTime_t t;
if (_PyTime_GetPerfCounterWithInfo(&t, NULL) < 0) {
/* should not happen, _PyTime_Init() checked the clock at startup */
Py_UNREACHABLE();
}
return t;
}

/* ensure that the system clock works */
if (_PyTime_GetSystemClockWithInfo(&t, NULL) < 0)
return -1;

/* ensure that the operating system provides a monotonic clock */
if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0)
int
_PyTime_Init(void)
{
/* check that the 3 most important clocks are working properly
to not have to check for exceptions at runtime. If a clock works once,
it cannot fail in next calls. */
_PyTime_t t;
if (_PyTime_GetSystemClockWithInfo(&t, NULL) < 0) {
return -1;

}
if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0) {
return -1;
}
if (_PyTime_GetPerfCounterWithInfo(&t, NULL) < 0) {
return -1;
}
return 0;
}

Expand Down