Commit 1b3c3171 authored by Robert Bradshaw's avatar Robert Bradshaw

Add an optimization for re-aquiring the GIL.

parent cee5f351
...@@ -36,6 +36,9 @@ Features added ...@@ -36,6 +36,9 @@ Features added
* "cdef extern" include files are now also searched relative to the current file. * "cdef extern" include files are now also searched relative to the current file.
Patch by jdemeyer (Github issue #1654). Patch by jdemeyer (Github issue #1654).
* Optional optimization for re-aquiring the GIL, controlled by the
`fast_gil` directive.
Bugs fixed Bugs fixed
---------- ----------
......
...@@ -2079,7 +2079,7 @@ class CCodeWriter(object): ...@@ -2079,7 +2079,7 @@ class CCodeWriter(object):
variable = '__pyx_gilstate_save' variable = '__pyx_gilstate_save'
if declare_gilstate: if declare_gilstate:
self.put("PyGILState_STATE ") self.put("PyGILState_STATE ")
self.putln("%s = PyGILState_Ensure();" % variable) self.putln("%s = __Pyx_PyGILState_Ensure();" % variable)
self.putln("#endif") self.putln("#endif")
def put_release_ensured_gil(self, variable=None): def put_release_ensured_gil(self, variable=None):
...@@ -2089,7 +2089,7 @@ class CCodeWriter(object): ...@@ -2089,7 +2089,7 @@ class CCodeWriter(object):
if not variable: if not variable:
variable = '__pyx_gilstate_save' variable = '__pyx_gilstate_save'
self.putln("#ifdef WITH_THREAD") self.putln("#ifdef WITH_THREAD")
self.putln("PyGILState_Release(%s);" % variable) self.putln("__Pyx_PyGILState_Release(%s);" % variable)
self.putln("#endif") self.putln("#endif")
def put_acquire_gil(self, variable=None): def put_acquire_gil(self, variable=None):
......
...@@ -719,6 +719,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -719,6 +719,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
if has_np_pythran(env): if has_np_pythran(env):
env.use_utility_code(UtilityCode.load_cached("PythranConversion", "CppSupport.cpp")) env.use_utility_code(UtilityCode.load_cached("PythranConversion", "CppSupport.cpp"))
if env.directives['fast_gil']:
env.use_utility_code(UtilityCode.load_cached("FastGil", "ModuleSetupCode.c"))
else:
env.use_utility_code(UtilityCode.load_cached("NoFastGil", "ModuleSetupCode.c"))
def generate_extern_c_macro_definition(self, code): def generate_extern_c_macro_definition(self, code):
name = Naming.extern_c_macro name = Naming.extern_c_macro
...@@ -2151,6 +2155,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -2151,6 +2155,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("%s = PyUnicode_FromStringAndSize(\"\", 0); %s" % ( code.putln("%s = PyUnicode_FromStringAndSize(\"\", 0); %s" % (
Naming.empty_unicode, code.error_goto_if_null(Naming.empty_unicode, self.pos))) Naming.empty_unicode, code.error_goto_if_null(Naming.empty_unicode, self.pos)))
code.putln("__Pyx_FastGilFuncInit();");
for ext_type in ('CyFunction', 'FusedFunction', 'Coroutine', 'Generator', 'StopAsyncIteration'): for ext_type in ('CyFunction', 'FusedFunction', 'Coroutine', 'Generator', 'StopAsyncIteration'):
code.putln("#ifdef __Pyx_%s_USED" % ext_type) code.putln("#ifdef __Pyx_%s_USED" % ext_type)
code.put_error_if_neg(self.pos, "__pyx_%s_init()" % ext_type) code.put_error_if_neg(self.pos, "__pyx_%s_init()" % ext_type)
......
...@@ -176,6 +176,7 @@ _directive_defaults = { ...@@ -176,6 +176,7 @@ _directive_defaults = {
'unraisable_tracebacks': True, 'unraisable_tracebacks': True,
'old_style_globals': False, 'old_style_globals': False,
'np_pythran': False, 'np_pythran': False,
'fast_gil': True, # TODO(robertwb): Consider changing the default to False before releasing.
# set __file__ and/or __path__ to known source/target path at import time (instead of not having them available) # set __file__ and/or __path__ to known source/target path at import time (instead of not having them available)
'set_initial_path' : None, # SOURCEFILE or "/full/path/to/module" 'set_initial_path' : None, # SOURCEFILE or "/full/path/to/module"
...@@ -312,7 +313,8 @@ directive_scopes = { # defaults to available everywhere ...@@ -312,7 +313,8 @@ directive_scopes = { # defaults to available everywhere
# globals() could conceivably be controlled at a finer granularity, # globals() could conceivably be controlled at a finer granularity,
# but that would complicate the implementation # but that would complicate the implementation
'old_style_globals': ('module',), 'old_style_globals': ('module',),
'np_pythran': ('module',) 'np_pythran': ('module',),
'fast_gil': ('module',),
} }
......
...@@ -863,3 +863,133 @@ static int __Pyx_RegisterCleanup(void) { ...@@ -863,3 +863,133 @@ static int __Pyx_RegisterCleanup(void) {
return 0; return 0;
} }
#endif #endif
/////////////// NoFastGil.proto ///////////////
#define __Pyx_PyGILState_Ensure PyGILState_Ensure
#define __Pyx_PyGILState_Release PyGILState_Release
#define __Pyx_FastGilFuncInit()
/////////////// FastGil.proto ///////////////
struct __Pyx_FastGilVtab {
PyGILState_STATE (*Fast_PyGILState_Ensure)(void);
void (*Fast_PyGILState_Release)(PyGILState_STATE oldstate);
};
static struct __Pyx_FastGilVtab __Pyx_FastGilFuncs;
static void __Pyx_FastGilFuncInit(void);
#define __Pyx_PyGILState_Ensure __Pyx_FastGilFuncs.Fast_PyGILState_Ensure
#define __Pyx_PyGILState_Release __Pyx_FastGilFuncs.Fast_PyGILState_Release
#ifdef WITH_THREAD
#ifndef CYTHON_THREAD_LOCAL
#if __STDC_VERSION__ >= 201112
#define CYTHON_THREAD_LOCAL _Thread_local
#elif defined(__GNUC__)
#define CYTHON_THREAD_LOCAL __thread
#elif defined(_MSC_VER)
#define CYTHON_THREAD_LOCAL __declspec(thread)
#endif
#endif
#endif
/////////////// FastGil ///////////////
//@requires: CommonStructures.c::FetchCommonPointer
// The implementations of PyGILState_Ensure/Release calls PyThread_get_key_value
// several times which is turns out to be quite slow (slower in fact than
// acquiring the GIL itself). Simply storing it in a thread local for the
// common case is much faster.
// To make optimal use of this thread local, we attempt to share it between
// modules.
#ifdef CYTHON_THREAD_LOCAL
#include "pythread.h"
#include "pystate.h"
static CYTHON_THREAD_LOCAL PyThreadState *__Pyx_FastGil_tcur = NULL;
static CYTHON_THREAD_LOCAL int __Pyx_FastGil_tcur_depth = 0;
static int __Pyx_FastGil_autoTLSkey = -1;
static CYTHON_INLINE PyThreadState *__Pyx_FastGil_get_tcur(int inc) {
PyThreadState *tcur = __Pyx_FastGil_tcur;
if (tcur == NULL) {
tcur = __Pyx_FastGil_tcur = (PyThreadState*)PyThread_get_key_value(__Pyx_FastGil_autoTLSkey);
}
__Pyx_FastGil_tcur_depth += inc;
if (__Pyx_FastGil_tcur_depth == 0) {
__Pyx_FastGil_tcur = NULL;
}
return tcur;
}
PyGILState_STATE __Pyx_FastGil_PyGILState_Ensure(void) {
int current;
PyThreadState *tcur = __Pyx_FastGil_get_tcur(1);
if (tcur == NULL) {
// Uninitialized, need to initialize now.
return PyGILState_Ensure();
}
current = tcur == _PyThreadState_Current;
if (current == 0) {
PyEval_RestoreThread(tcur);
}
++tcur->gilstate_counter;
return current ? PyGILState_LOCKED : PyGILState_UNLOCKED;
}
void __Pyx_FastGil_PyGILState_Release(PyGILState_STATE oldstate) {
PyThreadState *tcur = __Pyx_FastGil_get_tcur(-1);
if (tcur->gilstate_counter == 1) {
// This is the last lock, do all the cleanup as well.
PyGILState_Release(oldstate);
} else {
--tcur->gilstate_counter;
if (oldstate == PyGILState_UNLOCKED) {
PyEval_SaveThread();
}
}
}
static void __Pyx_FastGilFuncInit0(void) {
/* Try to detect autoTLSkey. */
void* this_thread_state = (void*) PyGILState_GetThisThreadState();
for (int key = 0; key < 100; key++) {
if (PyThread_get_key_value(key) == this_thread_state) {
__Pyx_FastGil_autoTLSkey = key;
break;
}
}
if (__Pyx_FastGil_autoTLSkey != -1) {
__Pyx_PyGILState_Ensure = __Pyx_FastGil_PyGILState_Ensure;
__Pyx_PyGILState_Release = __Pyx_FastGil_PyGILState_Release;
// Already fetched earlier, now we're just posting.
__Pyx_FetchCommonPointer(&__Pyx_FastGilFuncs, "FastGilFuncs");
} else {
__Pyx_PyGILState_Ensure = PyGILState_Ensure;
__Pyx_PyGILState_Release = PyGILState_Release;
}
}
#else
static void __Pyx_FastGilFuncInit0(void) {
CYTHON_UNUSED void* force_use = (void*)&__Pyx_FetchCommonPointer;
__Pyx_PyGILState_Ensure = PyGILState_Ensure;
__Pyx_PyGILState_Release = PyGILState_Release;
}
#endif
static void __Pyx_FastGilFuncInit(void) {
struct __Pyx_FastGilVtab* shared = (struct __Pyx_FastGilVtab*)PyCapsule_Import("_cython_" CYTHON_ABI ".FastGilFuncs", 1);
if (shared) {
__Pyx_FastGilFuncs = *shared;
} else {
PyErr_Clear();
__Pyx_FastGilFuncInit0();
}
}
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