Commit 78f07563 authored by Stefan Krah's avatar Stefan Krah

Speed up _decimal by another 10-15% by caching the thread local context

that was last accessed. In the pi benchmark (64-bit platform, prec=9),
_decimal is now only 1.5x slower than float.
parent 95aeae01
...@@ -55,6 +55,10 @@ Core and Builtins ...@@ -55,6 +55,10 @@ Core and Builtins
Library Library
------- -------
- Speed up _decimal by another 10-15% by caching the thread local context
that was last accessed. In the pi benchmark (64-bit platform, prec=9),
_decimal is now only 1.5x slower than float.
- Remove the packaging module, which is not ready for prime time. - Remove the packaging module, which is not ready for prime time.
- Issue #15154: Add "dir_fd" parameter to os.rmdir, remove "rmdir" - Issue #15154: Add "dir_fd" parameter to os.rmdir, remove "rmdir"
......
...@@ -76,6 +76,9 @@ typedef struct { ...@@ -76,6 +76,9 @@ typedef struct {
PyObject *traps; PyObject *traps;
PyObject *flags; PyObject *flags;
int capitals; int capitals;
#ifndef WITHOUT_THREADS
PyThreadState *tstate;
#endif
} PyDecContextObject; } PyDecContextObject;
typedef struct { typedef struct {
...@@ -123,6 +126,8 @@ static PyObject *module_context = NULL; ...@@ -123,6 +126,8 @@ static PyObject *module_context = NULL;
#else #else
/* Key for thread state dictionary */ /* Key for thread state dictionary */
static PyObject *tls_context_key = NULL; static PyObject *tls_context_key = NULL;
/* Invariant: NULL or the most recently accessed thread local context */
static PyDecContextObject *cached_context = NULL;
#endif #endif
/* Template for creating new thread contexts, calling Context() without /* Template for creating new thread contexts, calling Context() without
...@@ -1182,6 +1187,9 @@ context_new(PyTypeObject *type, PyObject *args UNUSED, PyObject *kwds UNUSED) ...@@ -1182,6 +1187,9 @@ context_new(PyTypeObject *type, PyObject *args UNUSED, PyObject *kwds UNUSED)
SdFlagAddr(self->flags) = &ctx->status; SdFlagAddr(self->flags) = &ctx->status;
CtxCaps(self) = 1; CtxCaps(self) = 1;
#ifndef WITHOUT_THREADS
self->tstate = NULL;
#endif
return (PyObject *)self; return (PyObject *)self;
} }
...@@ -1189,6 +1197,11 @@ context_new(PyTypeObject *type, PyObject *args UNUSED, PyObject *kwds UNUSED) ...@@ -1189,6 +1197,11 @@ context_new(PyTypeObject *type, PyObject *args UNUSED, PyObject *kwds UNUSED)
static void static void
context_dealloc(PyDecContextObject *self) context_dealloc(PyDecContextObject *self)
{ {
#ifndef WITHOUT_THREADS
if (self == cached_context) {
cached_context = NULL;
}
#endif
Py_XDECREF(self->traps); Py_XDECREF(self->traps);
Py_XDECREF(self->flags); Py_XDECREF(self->flags);
Py_TYPE(self)->tp_free(self); Py_TYPE(self)->tp_free(self);
...@@ -1555,18 +1568,19 @@ PyDec_SetCurrentContext(PyObject *self UNUSED, PyObject *v) ...@@ -1555,18 +1568,19 @@ PyDec_SetCurrentContext(PyObject *self UNUSED, PyObject *v)
} }
#else #else
/* /*
* Thread local storage currently has a speed penalty of about 16%. * Thread local storage currently has a speed penalty of about 4%.
* All functions that map Python's arithmetic operators to mpdecimal * All functions that map Python's arithmetic operators to mpdecimal
* functions have to look up the current context for each and every * functions have to look up the current context for each and every
* operation. * operation.
*/ */
/* Return borrowed reference to thread local context. */ /* Get the context from the thread state dictionary. */
static PyObject * static PyObject *
current_context(void) current_context_from_dict(void)
{ {
PyObject *dict = NULL; PyObject *dict;
PyObject *tl_context = NULL; PyObject *tl_context;
PyThreadState *tstate;
dict = PyThreadState_GetDict(); dict = PyThreadState_GetDict();
if (dict == NULL) { if (dict == NULL) {
...@@ -1577,16 +1591,15 @@ current_context(void) ...@@ -1577,16 +1591,15 @@ current_context(void)
tl_context = PyDict_GetItemWithError(dict, tls_context_key); tl_context = PyDict_GetItemWithError(dict, tls_context_key);
if (tl_context != NULL) { if (tl_context != NULL) {
/* We already have a thread local context and /* We already have a thread local context. */
* return a borrowed reference. */
CONTEXT_CHECK(tl_context); CONTEXT_CHECK(tl_context);
return tl_context;
} }
else {
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
return NULL; return NULL;
} }
/* Otherwise, set up a new thread local context. */ /* Set up a new thread local context. */
tl_context = context_copy(default_context_template); tl_context = context_copy(default_context_template);
if (tl_context == NULL) { if (tl_context == NULL) {
return NULL; return NULL;
...@@ -1598,11 +1611,34 @@ current_context(void) ...@@ -1598,11 +1611,34 @@ current_context(void)
return NULL; return NULL;
} }
Py_DECREF(tl_context); Py_DECREF(tl_context);
}
/* refcount is 1 */ /* Cache the context of the current thread, assuming that it
* will be accessed several times before a thread switch. */
tstate = PyThreadState_GET();
if (tstate) {
cached_context = (PyDecContextObject *)tl_context;
cached_context->tstate = tstate;
}
/* Borrowed reference with refcount==1 */
return tl_context; return tl_context;
} }
/* Return borrowed reference to thread local context. */
static PyObject *
current_context(void)
{
PyThreadState *tstate;
tstate = PyThreadState_GET();
if (cached_context && cached_context->tstate == tstate) {
return (PyObject *)cached_context;
}
return current_context_from_dict();
}
/* ctxobj := borrowed reference to the current context */ /* ctxobj := borrowed reference to the current context */
#define CURRENT_CONTEXT(ctxobj) \ #define CURRENT_CONTEXT(ctxobj) \
ctxobj = current_context(); \ ctxobj = current_context(); \
...@@ -1664,6 +1700,7 @@ PyDec_SetCurrentContext(PyObject *self UNUSED, PyObject *v) ...@@ -1664,6 +1700,7 @@ PyDec_SetCurrentContext(PyObject *self UNUSED, PyObject *v)
Py_INCREF(v); Py_INCREF(v);
} }
cached_context = NULL;
if (PyDict_SetItem(dict, tls_context_key, v) < 0) { if (PyDict_SetItem(dict, tls_context_key, v) < 0) {
Py_DECREF(v); Py_DECREF(v);
return NULL; return NULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment