The endless GIL debate: why not remove thread support instead?
Last month there was a discussion on Python-Dev regarding removal of reference counting to remove the GIL. I hope you forgive me for continuing the debate. I think reference counting is a good feature. It prevents huge piles of garbage from building up. It makes the interpreter run more smoothly. It is not just important for games and multimedia applications, but also servers under high load. Python does not pause to look for garbage like Java or .NET. It only pauses to look for dead reference cycles. This can be safely turned off temporarily; it can be turned off completely if you do not create reference cycles. With Java and .NET, no garbage is ever reclaimed except by the intermittent garbage collection. Python always reclaims an object when the reference count drops to zero whether the GC is enabled or not. This makes Python programs well-behaved. For this reason, I think removing reference counting is a genuinely bad idea. Even if the GIL is evil, this remedy is even worse. I am not a Python core developer; I am a research scientist who use Python because Matlab is (or used to be) a bad programming language, albeit a good computing environment. As most people who have worked with scientific computing know, there are better paradigms for concurrency than threads. In particular, there are message-passing systems like MPI and Erlang, and there are autovectorizing compilers for OpenMP and Fortran 90/95. There are special LAPACK, BLAS and FFT libraries for parallel computer architectures. There are fork-join systems like cilk and java.util.concurrent. Threads seem to be used only because mediocre programmers don't know what else to use. I genuinely think the use of threads should be discouraged. It leads to code that are full of bugs and difficult to maintain - race conditions, deadlocks, and livelocks are common pitfalls. Very few developers are capable of implementing efficient load-balancing by hand. Multi-threaded programs tend to scale badly because they are badly written. If the GIL discourages the abuse of threads, it serves a purpose albeit being evil like the Linux kernel's BKL. Python could be better off doing what tcl does. Allow each process to embed multiple interpreters; run each interpreter in its own thread. Implement a fast message-passing system between the interpreters (e.g. copy-on-write by making communicated objects immutable), and Python would be closer to Erlang than Java. I thus think the main offender is the thread and threading modules - not the GIL. Without thread support in the interpreter, there would be no threads. Without threads, there would be no need for a GIL. Both sources of evil can be removed by just removing thread support from the Python interpreter. In addition, it would make Python faster at executing linear code. Just copy the concurrency model of Erlang instead of Java and get rid of those nasty threads. In the meanwhile, I'll continue to experiment with multiprocessing. Removing reference counting to encourage the use of threads is like shooting ourselves in the leg twice. Thats my two cents on this issue. There is another issue to note as well: If you can endure a 200x loss of efficacy by using Python instead of Fortran, scalability on dual or quad-core processors may not be that important. Just move the bottlenecks out of Python and you are much better off. Regards, Sturla Molden
Hi, replying to the topic only: because many C libraries support threading and Python extension modules can integrate them in a way that allows concurrency in a safe way (although 'safe' is definitely something that is paid for in developer days). Stefan
2008/12/12 Sturla Molden
Last month there was a discussion on Python-Dev regarding removal of reference counting to remove the GIL. I hope you forgive me for continuing the debate. [...] Python could be better off doing what tcl does. Allow each process to embed multiple interpreters; run each interpreter in its own thread. Implement a fast message-passing system between the interpreters (e.g. copy-on-write by making communicated objects immutable), and Python would be closer to Erlang than Java.
Too much to comment individually here, but I'd agree that message-passing approaches are a better model in general. Some specific points: 1. The Queue module gives the bones of a message-passing model, building something based on that is possible now (and may already exist). You have to do isolation by convention rather than having it enforced by the system, but that's OK for coding. (It doesn't help the "remove the GIL" debate, though). 2. I'd like to see isolation based on multiple interpreters, but the problem lies with extensions (and at a lower level with the Python C API) which wasn't designed with isolation in mind. Changing that may be nice, but it's probably too late (or if not, it's likely to be a lot of work to do it in a compatible manner). 3. Exposing multiple interpreters at the Python level would let most of this be done outside the core. But it may result in pure Python code being able to crash the application if not done carefully. And of course, the overriding points: - This needs to be done in a backward compatible manner (Python 3.0 is out now!) - A working patch is hugely more likely to make progress, as all the evidence shows that the current core developers don't find this issue important enough to spend their limited coding time on. Paul.
Paul Moore wrote:
2. I'd like to see isolation based on multiple interpreters, but the problem lies with extensions (and at a lower level with the Python C API) which wasn't designed with isolation in mind. Changing that may be nice, but it's probably too late (or if not, it's likely to be a lot of work to do it in a compatible manner).
Actually, I believe 3.0 already took a big step towards allowing this by changing the way modules are initialised. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------
Nick Coghlan schrieb:
Actually, I believe 3.0 already took a big step towards allowing this by changing the way modules are initialised.
You are believing correctly. Martin has designed and implemented a nicely working API to store extension module data per interpreter state. For now interpreter states are used for sub interpreters only. http://www.python.org/dev/peps/pep-3121/ Christian
Christian Heimes schrieb:
Nick Coghlan schrieb:
Actually, I believe 3.0 already took a big step towards allowing this by changing the way modules are initialised.
You are believing correctly. Martin has designed and implemented a nicely working API to store extension module data per interpreter state. For now interpreter states are used for sub interpreters only.
But the extension modules still have to changed to use this mechanism, right? -- Thanks, Thomas
Thomas Heller wrote:
Christian Heimes schrieb:
Nick Coghlan schrieb:
Actually, I believe 3.0 already took a big step towards allowing this by changing the way modules are initialised. You are believing correctly. Martin has designed and implemented a nicely working API to store extension module data per interpreter state. For now interpreter states are used for sub interpreters only.
But the extension modules still have to changed to use this mechanism, right?
Yep, but at least it's *possible* now. With 2.x, it isn't possible for an extension module to support subinterpreters properly, even if they want to. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------
Nick Coghlan wrote:
Actually, I believe 3.0 already took a big step towards allowing this by changing the way modules are initialised.
It's a step, but I wouldn't call it a big one. There are many other problems to be solved before fully independent interpreters are possible. -- Greg
2008/12/17 Greg Ewing
Nick Coghlan wrote:
Actually, I believe 3.0 already took a big step towards allowing this by changing the way modules are initialised.
It's a step, but I wouldn't call it a big one. There are many other problems to be solved before fully independent interpreters are possible.
Do you know if these remaining problems are listed anywhere? AIUI, certain software (for example mod_python) has been using multiple interpreters for a long while now - admittedly not without issues, but certainly enough to imply that multiple interpreters are at least "possible" - although not perfect. Experience with such software would probably be a great guide to where the issues exist. Maybe a page on the Python Wiki, or a FAQ entry, would be useful here. If only to make things explicit, and clear up some of the FUD around multiple interpreters. Paul.
Paul Moore wrote:
Do you know if these remaining problems are listed anywhere?
There was a big discussion about this in comp.lang.python not long ago. Basically all the built-in types and constants are shared between interpreters, which means you still need a GIL to stop different interpreters stepping on each other's toes.
AIUI, certain software (for example mod_python) has been using multiple interpreters for a long while now
Multiple interpeters are possible, they're just not completely independent. Whether this is a problem depends on the reason you want multiple interpreters. In the Apache case, it's probably more about providing virtual Python environments than free-threading between interpreters. -- Greg
Greg Ewing wrote:
Paul Moore wrote:
Do you know if these remaining problems are listed anywhere?
There was a big discussion about this in comp.lang.python not long ago. Basically all the built-in types and constants are shared between interpreters, which means you still need a GIL to stop different interpreters stepping on each other's toes.
That kind of thing is under the core's control though - the 2.x module initialisation problem means that you can't write a multiple interpreter friendly extension module even if you want to. The new per-interpreter state mechanism could also be used internally by the core to duplicate some of that global state for each new interpreter. I see the introduction of the interpreter specific state mechanism as a big step because it provides an underlying mechanism that makes the problem solvable *in principle* through a combination of per-interpreter state and finer grained shared locking, making it just a practical implementation problem to see if that can be done without adversely impacting single interpreter performance. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------
2008/12/18 Greg Ewing
Paul Moore wrote:
Do you know if these remaining problems are listed anywhere?
There was a big discussion about this in comp.lang.python not long ago. Basically all the built-in types and constants are shared between interpreters, which means you still need a GIL to stop different interpreters stepping on each other's toes.
AIUI, certain software (for example mod_python) has been using multiple interpreters for a long while now
Multiple interpeters are possible, they're just not completely independent. Whether this is a problem depends on the reason you want multiple interpreters. In the Apache case, it's probably more about providing virtual Python environments than free-threading between interpreters.
OK, but how close is it to providing isolation for threads running under the control of the GIL? I'm thinking of something along the lines of an in-process version of fork(), which spawns a new interpreter and runs the 2 interpreters as threads, still using the GIL to enforce serialisation, but otherwise independent. I believe that Perl uses this model for its "interpreter threads" implementation. Paul.
Paul Moore schrieb:
OK, but how close is it to providing isolation for threads running under the control of the GIL? I'm thinking of something along the lines of an in-process version of fork(), which spawns a new interpreter and runs the 2 interpreters as threads, still using the GIL to enforce serialisation, but otherwise independent. I believe that Perl uses this model for its "interpreter threads" implementation.
How is your idea different from subinterpreters? Today you can have multiple subinterpreters inside a single process. Each subinterpreter has its own state and can see only its own objects. Christian
OK, but how close is it to providing isolation for threads running under the control of the GIL?
They won't be indedepent. If an extension module has a global variable, that will be shared across interpreters. If that variable supports modifiable state, such modifications will "leak" across interpreters. For example, there will be only a single object class. With that in mind, take a look at object.__subclasses__(); it would provide access to all classes, including those in the other interpreters. Likewise, gc.get_objects() will give you the complete list of all objects. So the isolation is not strong enough to run untrusted code isolated from other code. Regards, Martin
Sturla Molden wrote:
Last month there was a discussion on Python-Dev regarding removal of reference counting to remove the GIL. I hope you forgive me for continuing the debate.
Anything to do with removing the GIL/threads/whatever other core language feature someone doesn't like really belongs on c.l.p. or python-ideas rather than here. Ideas should be at least remotely feasible before they're brought to python-dev. That said, I'll bite anyway... Treating threads as communicating sequential processes (via the Queue module) actually makes them pretty easy to use correctly. They are then extraordinarily handy for performing multiple non-GIL bound tasks (such as IO operations or number crunching using an extension module like numpy) in parallel. For GIL bound tasks, switching from the threading module to the multiprocessing module now allows the activity to scale to multiple CPUs. Removing thread support merely because concurrent programming is hard (no matter how you do it) would be... odd (to say the least). Changing the underlying concurrency mechanism from threads to subinterpreters to processes to whole computers doesn't make understanding and coping with the concepts involved in concurrency any easier (and in fact will often make them harder to handle by increasing the communications latency). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia ---------------------------------------------------------------
On Fri, Dec 12, 2008 at 02:13, Sturla Molden
I genuinely think the use of threads should be discouraged. It leads to code that are full of bugs and difficult to maintain - race conditions, deadlocks, and livelocks are common pitfalls.
The use of threads for load balancing should be discouraged, yes. That is not what they are designed for. Threads are designed to allow blocking processes to go on in the background without blocking the main process. This, they are very useful for. Removing thread support would therefore be a very big mistake. It's needed, it has it's uses, just not the one *you* want. -- Lennart Regebro: Zope and Plone consulting. http://www.colliberty.com/ +33 661 58 14 64
On 12/12/2008 11:52 AM, Lennart Regebro wrote:
The use of threads for load balancing should be discouraged, yes. That is not what they are designed for. Threads are designed to allow blocking processes to go on in the background without blocking the main process.
It seems that most programmers with Java or Windows experience don't understand this; hence the ever lasting GIL debate. With multiple interpreters - one interpreter per thread - this could still be accomplished. Let one interpreter block while another continues to work. Then the result of the blocking operation is messaged back. Multi-threaded C libraries could be used the in same way. But there would be no need for a GIL, because each interpreter would be a single-threaded compartment. .NET have something similar in what is called 'appdomains'. I am not suggesting removal of threads but rather the Java threading model. I just think it is a mistake to let multiple OS threads touch the same interpreter. Sturla Molden
On Fri, Dec 12, 2008 at 12:23, Sturla Molden
It seems that most programmers with Java or Windows experience don't understand this; hence the ever lasting GIL debate.
Yes. Maybe writing this with big letters in the thread module docs would help?
I am not suggesting removal of threads but rather the Java threading model. I just think it is a mistake to let multiple OS threads touch the same interpreter.
Does Python have a java threading model? I don't know java well enough to know what that is. :) -- Lennart Regebro: Zope and Plone consulting. http://www.colliberty.com/ +33 661 58 14 64
Lennart Regebro wrote:
On Fri, Dec 12, 2008 at 02:13, Sturla Molden
wrote: I genuinely think the use of threads should be discouraged. It leads to code that are full of bugs and difficult to maintain - race conditions, deadlocks, and livelocks are common pitfalls.
The use of threads for load balancing should be discouraged, yes. That is not what they are designed for. Threads are designed to allow blocking processes to go on in the background without blocking the main process. This, they are very useful for. Removing thread support would therefore be a very big mistake. It's needed, it has it's uses, just not the one *you* want.
That's an interesting assertion about what threads were designed for. Do you have anything to back it up? Michael -- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/blog
Yes, this is what threads were designed for. As an abstraction to have
multiple "threads of control" on a *single* processor (in a single
process). The whole multi-core business came decades later. (Classic
multi-processors have something called threads too, but they, too,
came later than the original single-core-single-CPU thread concept,
and often threads on those systems have properties that don't match
how threads work on modern multi-core CPUs.)
On Sat, Dec 13, 2008 at 5:32 AM, Michael Foord
Lennart Regebro wrote:
On Fri, Dec 12, 2008 at 02:13, Sturla Molden
wrote: I genuinely think the use of threads should be discouraged. It leads to code that are full of bugs and difficult to maintain - race conditions, deadlocks, and livelocks are common pitfalls.
The use of threads for load balancing should be discouraged, yes. That is not what they are designed for. Threads are designed to allow blocking processes to go on in the background without blocking the main process. This, they are very useful for. Removing thread support would therefore be a very big mistake. It's needed, it has it's uses, just not the one *you* want.
That's an interesting assertion about what threads were designed for. Do you have anything to back it up?
Michael
-- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/blog
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/guido%40python.org
-- --Guido van Rossum (home page: http://www.python.org/~guido/)
If I remember correctly (when threading was invented in the mid-1980s) threads were originally described as "lightweight processes". The perceived advantage at the time was the ability to have multiple threads of control with shared memory: this was much faster than the available inter-process communication mechanisms. On a single-processor computer synchronization was much less of a problem. regards Steve Guido van Rossum wrote:
Yes, this is what threads were designed for. As an abstraction to have multiple "threads of control" on a *single* processor (in a single process). The whole multi-core business came decades later. (Classic multi-processors have something called threads too, but they, too, came later than the original single-core-single-CPU thread concept, and often threads on those systems have properties that don't match how threads work on modern multi-core CPUs.)
On Sat, Dec 13, 2008 at 5:32 AM, Michael Foord
wrote: Lennart Regebro wrote:
On Fri, Dec 12, 2008 at 02:13, Sturla Molden
wrote: I genuinely think the use of threads should be discouraged. It leads to code that are full of bugs and difficult to maintain - race conditions, deadlocks, and livelocks are common pitfalls.
The use of threads for load balancing should be discouraged, yes. That is not what they are designed for. Threads are designed to allow blocking processes to go on in the background without blocking the main process. This, they are very useful for. Removing thread support would therefore be a very big mistake. It's needed, it has it's uses, just not the one *you* want.
That's an interesting assertion about what threads were designed for. Do you have anything to back it up?
-- Steve Holden +1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/
Steve Holden schrieb:
If I remember correctly (when threading was invented in the mid-1980s) threads were originally described as "lightweight processes". The perceived advantage at the time was the ability to have multiple threads of control with shared memory: this was much faster than the available inter-process communication mechanisms. On a single-processor computer synchronization was much less of a problem.
Initially one of Java's main target platforms were set-top boxes. Back in the 90ties set-top boxes had limited hardware and dumb processors. Most of the boxes had no MMU and so didn't support multiple processes. Threads were the easiest way to have some kind of concurrency. Back in those days threads were the only solution for concurrency but today - about 15 years later with powerful processors even in cheap mobile phones - people are still indoctrinated with the same philosophy ... Christian
On Sat, Dec 13, 2008 at 9:13 AM, Christian Heimes
Steve Holden schrieb:
If I remember correctly (when threading was invented in the mid-1980s) threads were originally described as "lightweight processes". The perceived advantage at the time was the ability to have multiple threads of control with shared memory: this was much faster than the available inter-process communication mechanisms. On a single-processor computer synchronization was much less of a problem.
Initially one of Java's main target platforms were set-top boxes. Back in the 90ties set-top boxes had limited hardware and dumb processors. Most of the boxes had no MMU and so didn't support multiple processes. Threads were the easiest way to have some kind of concurrency.
Just let's not rewrite history and believe Java invented threads. They were around well before that.
Back in those days threads were the only solution for concurrency but today - about 15 years later with powerful processors even in cheap mobile phones - people are still indoctrinated with the same philosophy ...
It's not so much indoctrination. Threads are a useful tool. The problem is that some people perceive threads as the *only* tool. There's a whole spectrum of tools, from event handling to multiple processes, and they don't all solve the same problem. (I guess it doesn't help that the word process is given new meanings by some languages.) -- --Guido van Rossum (home page: http://www.python.org/~guido/)
If I remember correctly (when threading was invented in the mid-1980s) threads were originally described as "lightweight processes".
According to http://www.serpentine.com/blog/threads-faq/the-history-of-threads/ that's when threads where *reinvented*. They were originally invented in 1965, on Multics (1970) they were used to perform compilation in the background. When Unix came along, it *added* address space separation, introducing what is now known as processes.
The perceived advantage at the time was the ability to have multiple threads of control with shared memory: this was much faster than the available inter-process communication mechanisms. On a single-processor computer synchronization was much less of a problem.
Historically, it was vice versa. First there were threads/processes/tasks with shared variables, semaphores, etc, and later address space separation was added. Regards, Martin
On Dec 13, 2008, at 5:47 PM, Martin v. Löwis wrote:
They were originally invented in 1965, on Multics (1970) they were used to perform compilation in the background. When Unix came along, it *added* address space separation, introducing what is now known as processes.
Yes, and a lot of the subsequent interest in threads came due to the
historically debilitating overhead of fork() on some important Unices,
notably Solaris.
--
Ivan Krstić
participants (13)
-
"Martin v. Löwis"
-
Christian Heimes
-
Greg Ewing
-
Guido van Rossum
-
Ivan Krstić
-
Lennart Regebro
-
Michael Foord
-
Nick Coghlan
-
Paul Moore
-
Stefan Behnel
-
Steve Holden
-
Sturla Molden
-
Thomas Heller