mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Speedup monotonic() by eliminating the ctypes overhead
This commit is contained in:
parent
af07373638
commit
343574245f
@ -93,6 +93,10 @@ extensions = [
|
||||
optimize_level=2,
|
||||
),
|
||||
|
||||
Extension('monotonic',
|
||||
['calibre/utils/monotonic.c'],
|
||||
),
|
||||
|
||||
Extension('speedup',
|
||||
['calibre/utils/speedup.c'],
|
||||
libraries=[] if iswindows else ['m']
|
||||
|
@ -135,6 +135,7 @@ class Plugins(collections.Mapping):
|
||||
'chm_extra',
|
||||
'icu',
|
||||
'speedup',
|
||||
'monotonic',
|
||||
'html',
|
||||
'freetype',
|
||||
'unrar',
|
||||
|
@ -20,6 +20,7 @@ except ImportError:
|
||||
from calibre.srv.pre_activated import has_preactivated_support
|
||||
from calibre.srv.tests.base import BaseTest, TestServer
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
from calibre.utils.monotonic import monotonic
|
||||
|
||||
class LoopTest(BaseTest):
|
||||
|
||||
@ -209,3 +210,15 @@ class LoopTest(BaseTest):
|
||||
self.ae(r.status, httplib.OK)
|
||||
self.ae(r.read(), b'testbody')
|
||||
self.ae(server.loop.bound_address[1], port)
|
||||
|
||||
def test_monotonic(self):
|
||||
'Test the monotonic() clock'
|
||||
a = monotonic()
|
||||
b = monotonic()
|
||||
self.assertGreaterEqual(b, a)
|
||||
a = monotonic()
|
||||
time.sleep(0.01)
|
||||
b = monotonic()
|
||||
self.assertGreaterEqual(b, a)
|
||||
self.assertGreaterEqual(b - a, 0.01)
|
||||
self.assertLessEqual(b - a, 0.02)
|
||||
|
85
src/calibre/utils/monotonic.c
Normal file
85
src/calibre/utils/monotonic.c
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* monotonic.c
|
||||
* Copyright (C) 2015 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#define UNICODE
|
||||
#include <Python.h>
|
||||
|
||||
/* To millisecond (10^-3) */
|
||||
#define SEC_TO_MS 1000
|
||||
|
||||
/* To microseconds (10^-6) */
|
||||
#define MS_TO_US 1000
|
||||
#define SEC_TO_US (SEC_TO_MS * MS_TO_US)
|
||||
|
||||
/* To nanoseconds (10^-9) */
|
||||
#define US_TO_NS 1000
|
||||
#define MS_TO_NS (MS_TO_US * US_TO_NS)
|
||||
#define SEC_TO_NS (SEC_TO_MS * MS_TO_NS)
|
||||
|
||||
/* Conversion from nanoseconds */
|
||||
#define NS_TO_MS (1000 * 1000)
|
||||
#define NS_TO_US (1000)
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <Windows.h>
|
||||
static LARGE_INTEGER frequency = {0}, ts = {0};
|
||||
|
||||
/* static PyObject* monotonic(PyObject *self, PyObject *args) { */
|
||||
/* return PyFloat_FromDouble(((double)GetTickCount64())/SEC_TO_MS); */
|
||||
/* } */
|
||||
|
||||
static PyObject* monotonic(PyObject *self, PyObject *args) {
|
||||
if (!QueryPerformanceCounter(&ts)) { PyErr_SetFromWindowsErr(0); return NULL; }
|
||||
return PyFloat_FromDouble(((double)ts.QuadPart)/frequency.QuadPart);
|
||||
}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#include <mach/mach_time.h>
|
||||
static mach_timebase_info_data_t timebase = {0};
|
||||
static PyObject* monotonic(PyObject *self, PyObject *args) {
|
||||
return PyFloat_FromDouble(((double)(mach_absolute_time() * timebase.numer) / timebase.denom)/SEC_TO_NS);
|
||||
}
|
||||
|
||||
#else
|
||||
#include <time.h>
|
||||
static struct timespec ts = {0};
|
||||
#ifdef CLOCK_HIGHRES
|
||||
const static clockid_t clk_id = CLOCK_HIGHRES;
|
||||
#elif defined(CLOCK_MONOTONIC_RAW)
|
||||
const static clockid_t clk_id = CLOCK_MONOTONIC_RAW;
|
||||
#else
|
||||
const static clockid_t clk_id = CLOCK_MONOTONIC;
|
||||
#endif
|
||||
static PyObject* monotonic(PyObject *self, PyObject *args) {
|
||||
if (clock_gettime(clk_id, &ts) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
||||
return PyFloat_FromDouble((((double)ts.tv_nsec) / SEC_TO_NS) + (double)ts.tv_sec);
|
||||
}
|
||||
#endif
|
||||
|
||||
static PyMethodDef monotonic_methods[] = {
|
||||
{"monotonic", monotonic, METH_NOARGS,
|
||||
"monotonic()\n\nReturn a monotonically increasing time value."
|
||||
},
|
||||
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
initmonotonic(void) {
|
||||
PyObject *m;
|
||||
#ifdef _MSC_VER
|
||||
if(!QueryPerformanceFrequency(&frequency)) { PyErr_SetFromWindowsErr(0); return; }
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
mach_timebase_info(&timebase);
|
||||
#endif
|
||||
m = Py_InitModule3("monotonic", monotonic_methods,
|
||||
"Implementation of time.monotonic() in C for speed"
|
||||
);
|
||||
if (m == NULL) return;
|
||||
}
|
||||
|
@ -1,110 +1,9 @@
|
||||
# vim:fileencoding=utf-8
|
||||
|
||||
from __future__ import division, absolute_import
|
||||
from calibre.constants import plugins
|
||||
|
||||
try:
|
||||
try:
|
||||
# >=python-3.3, Unix
|
||||
from time import clock_gettime
|
||||
try:
|
||||
# >={kernel}-sources-2.6.28
|
||||
from time import CLOCK_MONOTONIC_RAW as CLOCK_ID
|
||||
except ImportError:
|
||||
from time import CLOCK_MONOTONIC as CLOCK_ID # NOQA
|
||||
|
||||
monotonic = lambda: clock_gettime(CLOCK_ID)
|
||||
|
||||
except ImportError:
|
||||
# >=python-3.3
|
||||
from time import monotonic # NOQA
|
||||
|
||||
except ImportError:
|
||||
import ctypes
|
||||
import sys
|
||||
NSEC_PER_SEC = 1e9
|
||||
|
||||
try:
|
||||
if sys.platform == 'win32':
|
||||
# Windows only
|
||||
perf_frequency = ctypes.c_uint64()
|
||||
if ctypes.windll.kernel32.QueryPerformanceFrequency(ctypes.byref(perf_frequency)) == 0:
|
||||
from time import time as monotonic # noqa
|
||||
else:
|
||||
perf_frequency = perf_frequency.value
|
||||
|
||||
def monotonic():
|
||||
perf_counter = ctypes.c_uint64()
|
||||
if ctypes.windll.kernel32.QueryPerformanceCounter(ctypes.byref(perf_counter)) == 0:
|
||||
raise ctypes.WinError()
|
||||
return perf_counter.value / perf_frequency
|
||||
|
||||
elif sys.platform == 'darwin':
|
||||
# Mac OS X
|
||||
from ctypes.util import find_library
|
||||
|
||||
libc_name = find_library('c')
|
||||
if not libc_name:
|
||||
raise OSError
|
||||
|
||||
libc = ctypes.CDLL(libc_name, use_errno=True)
|
||||
|
||||
mach_absolute_time = libc.mach_absolute_time
|
||||
mach_absolute_time.argtypes = ()
|
||||
mach_absolute_time.restype = ctypes.c_uint64
|
||||
|
||||
class mach_timebase_info_data_t(ctypes.Structure):
|
||||
_fields_ = (
|
||||
('numer', ctypes.c_uint32),
|
||||
('denom', ctypes.c_uint32),
|
||||
)
|
||||
mach_timebase_info_data_p = ctypes.POINTER(mach_timebase_info_data_t)
|
||||
|
||||
_mach_timebase_info = libc.mach_timebase_info
|
||||
_mach_timebase_info.argtypes = (mach_timebase_info_data_p,)
|
||||
_mach_timebase_info.restype = ctypes.c_int
|
||||
|
||||
def mach_timebase_info():
|
||||
timebase = mach_timebase_info_data_t()
|
||||
_mach_timebase_info(ctypes.byref(timebase))
|
||||
return (timebase.numer, timebase.denom)
|
||||
|
||||
timebase = mach_timebase_info()
|
||||
factor = timebase[0] / (timebase[1] * NSEC_PER_SEC)
|
||||
|
||||
def monotonic(): # NOQA
|
||||
return mach_absolute_time() * factor
|
||||
else:
|
||||
# linux only (no librt on OS X)
|
||||
import os
|
||||
|
||||
# See <bits/time.h>
|
||||
CLOCK_MONOTONIC = 1
|
||||
CLOCK_MONOTONIC_RAW = 4
|
||||
|
||||
class timespec(ctypes.Structure):
|
||||
_fields_ = (
|
||||
('tv_sec', ctypes.c_long),
|
||||
('tv_nsec', ctypes.c_long)
|
||||
)
|
||||
tspec = timespec()
|
||||
|
||||
librt = ctypes.CDLL('librt.so.1', use_errno=True)
|
||||
clock_gettime = librt.clock_gettime
|
||||
clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)]
|
||||
|
||||
if clock_gettime(CLOCK_MONOTONIC_RAW, ctypes.pointer(tspec)) == 0:
|
||||
# >={kernel}-sources-2.6.28
|
||||
clock_id = CLOCK_MONOTONIC_RAW
|
||||
elif clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(tspec)) == 0:
|
||||
clock_id = CLOCK_MONOTONIC
|
||||
else:
|
||||
raise OSError
|
||||
|
||||
def monotonic():
|
||||
if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(tspec)) != 0:
|
||||
errno_ = ctypes.get_errno()
|
||||
raise OSError(errno_, os.strerror(errno_))
|
||||
return tspec.tv_sec + tspec.tv_nsec / NSEC_PER_SEC
|
||||
|
||||
except:
|
||||
from time import time as monotonic # noqa
|
||||
monotonicp, err = plugins['monotonic']
|
||||
if err:
|
||||
raise RuntimeError('Failed to load the monotonic module with error: ' + err)
|
||||
monotonic = monotonicp.monotonic
|
||||
del monotonicp, err
|
||||
|
Loading…
x
Reference in New Issue
Block a user