Removing the GIL (Me, not you!)
Hi, I had a whole long email about exactly what I was doing, but I think I'll get to the point instead. I'm trying to implement a python concurrency API and would like to use cpython to do it. To do that, I would like to remove the GIL. So, since I'm new to interpreter hacking, some help would be appreciated. I've listed what I think the GIL does; if you guys could add to this list or refine it, I would very much appreciate it. Roles of the GIL: 1. Some global interpreter state/modules are protected (where are these globals at?) 2. When writing C extensions I can change the state of my python object without worrying about synchronization 3. When writing C extensions I can change my own internal C state without worrying about synchronization (unless I have other, non-python threads running) Does anyone know of a place where the GIL is required when not operating on a python object? It seems like this would never happen, and would make replacing the GIL somewhat easier. I've only started looking at the code recently, so please forgive my naivety. I'm still learning how the interpreter works on a high level, let alone all the nitty gritty details! Thanks, Justin
1. Some global interpreter state/modules are protected (where are these globals at?)
2. When writing C extensions I can change the state of my python object without worrying about synchronization 3. When writing C extensions I can change my own internal C state without worrying about synchronization (unless I have other, non-python threads running)
It's the interpreter and thread state itself (pystate.h), for the thread state, also _PyThreadState_Current. Then there is the GC state, in particular "generations". There are various caches and counters also. 4. The builtin container types are protected by the GIL, and various other builtin objects 5. Reference counting is protected by the GIL 6. PyMalloc is protected by the GIL.
Does anyone know of a place where the GIL is required when not operating on a python object?
See 6 above, also (obviously) 1.
I've only started looking at the code recently, so please forgive my naivety. I'm still learning how the interpreter works on a high level, let alone all the nitty gritty details!
Good luck! Martin
On 9/11/07, "Martin v. Löwis" <martin@v.loewis.de> wrote:
1. Some global interpreter state/modules are protected (where are these globals at?)
It's the interpreter and thread state itself (pystate.h), for the thread state, also _PyThreadState_Current. Then there is the GC state, in particular "generations". There are various caches and counters also.
Caches seem like they definitely might be a problem. Would you mind expanding on this a little? What gets cached and why? Justin
Justin> Caches seem like they definitely might be a problem. Would you Justin> mind expanding on this a little? What gets cached and why? I believe the integer free list falls into this category. Skip
It's the interpreter and thread state itself (pystate.h), for the thread state, also _PyThreadState_Current. Then there is the GC state, in particular "generations". There are various caches and counters also.
Caches seem like they definitely might be a problem. Would you mind expanding on this a little? What gets cached and why?
Depends on the Python version what precisely gets cached. Several types preserve a pool of preallocated objects, to speed up allocation. Examples are intobject.c (block_list, free_list), frameobject.c (free_list), listobject.c (free_list), methodobject.c (free_list), float_object.c (block_list, free_list), classobject.c (free_list). Plus there are tons of variables caching string objects. From classobject.c alone: getattrstr, setattrstr, delattrs, docstr, modstr, namestr, initstr, delstr, reprstr, strstr, hashstr, eqstr, cmpstr, getitemstr, setitemstr, delitemstr, lenstr, iterstr, nextstr, getslicestr, setslicestr, delslicestr, __contains__, all arguments to UNARY, UNARY_FB, BINARY, BINARY_INPLACE (e.g. instance_neg, instance_or, instance_ior, then cmp_obj, nonzerostr, indexstr. (admittedly, classobject.c is extreme here). There are probably more classes which I just forgot. Regards, Martin
On 9/11/07, "Martin v. Löwis" <martin@v.loewis.de> wrote:
It's the interpreter and thread state itself (pystate.h), for the thread state, also _PyThreadState_Current. Then there is the GC state, in particular "generations". There are various caches and counters also.
Caches seem like they definitely might be a problem. Would you mind expanding on this a little? What gets cached and why?
Depends on the Python version what precisely gets cached. Several types preserve a pool of preallocated objects, to speed up allocation. Examples are intobject.c (block_list, free_list), frameobject.c (free_list), listobject.c (free_list), methodobject.c (free_list), float_object.c (block_list, free_list), classobject.c (free_list).
Plus there are tons of variables caching string objects. From classobject.c alone: getattrstr, setattrstr, delattrs, docstr, modstr, namestr, initstr, delstr, reprstr, strstr, hashstr, eqstr, cmpstr, getitemstr, setitemstr, delitemstr, lenstr, iterstr, nextstr, getslicestr, setslicestr, delslicestr, __contains__, all arguments to UNARY, UNARY_FB, BINARY, BINARY_INPLACE (e.g. instance_neg, instance_or, instance_ior, then cmp_obj, nonzerostr, indexstr. (admittedly, classobject.c is extreme here).
There are probably more classes which I just forgot.
We should probably document where all of these globals lists are instead of relying on looking for all file level static declarations or something. Or would there be benefit to moving things like this to the interpreter struct so that threads within a single interpreter call are locked but interpreters can act much more independently? -Brett
On Sep 11, 2007, at 3:30 PM, Brett Cannon wrote:
We should probably document where all of these globals lists are instead of relying on looking for all file level static declarations or something. Or would there be benefit to moving things like this to the interpreter struct so that threads within a single interpreter call are locked but interpreters can act much more independently?
This would be nice. It would be really nice if python was embeddable more like TCL: separate interpreters really are separate, and don't share state. That means basically everything has to be stored in an interp-specific data structure, not in static variables. But this has been raised before, and was rejected as not worth the amount of work that would be required to achieve it. (it's certainly not worth it enough for *me* to do the work, so I can't blame anyone else for making the same determination.) James
We should probably document where all of these globals lists are instead of relying on looking for all file level static declarations or something.
I'm not sure what would be gained here, except for people occasionally (i.e. every three years) asking how they can best get rid of the GIL.
Or would there be benefit to moving things like this to the interpreter struct so that threads within a single interpreter call are locked but interpreters can act much more independently?
The "multiple interpreter" feature doesn't quite work, and likely won't for a foreseeable future; specifically, objects can easily leak across interpreters. That's actually not a problem for immutable objects, like the strings, but here come the global objects into play which PJE mentions: types, including exceptions. Making them per-interpreter would probably break quite some code. As for the cached strings - it would be easy to make a global table of these, e.g. calling them _PyS__init__, _PyS__add__, and so on. These could be initialized at startup, simplifying the code that uses them because they don't have to worry about failures. Regards, Martin
Brett> We should probably document where all of these globals lists are Brett> instead of relying on looking for all file level static Brett> declarations or something. I smell a wiki page. Skip Brett> Or would there be benefit to moving things like this to the Brett> interpreter struct so that threads within a single interpreter Brett> call are locked but interpreters can act much more independently? Would that simplify things all that much? All containers would probably still rely on the GIL. Also, all objects rely on it to do reference count increment/decrement as I recall. Skip
On Tue, Sep 11, 2007, Justin Tulloss wrote:
I had a whole long email about exactly what I was doing, but I think I'll get to the point instead. I'm trying to implement a python concurrency API and would like to use cpython to do it. To do that, I would like to remove the GIL.
You should review the work Greg Stein did to remove the GIL in 1.5.2; although the interpreter core has changed considerably since then, I believe the basic principles of the GIL are the same. -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ "Many customs in this life persist because they ease friction and promote productivity as a result of universal agreement, and whether they are precisely the optimal choices is much less important." --Henry Spencer http://www.lysator.liu.se/c/ten-commandments.html
participants (6)
-
"Martin v. Löwis" -
Aahz -
Brett Cannon -
James Y Knight -
Justin Tulloss -
skip@pobox.com