13 ms·
Has the Python GIL Been Slain? Subinterpreters in Python 3.8
- riskneutral 7y ago"How much overhead does a sub-interpreter have? Short answer: More than a thread, less than a process." So ... No.
- moefh 7y agoI know nothing about Python internals, but my understanding from the article is that this "overhead" is about creating a new sub-interpreter (loading modules is particularly slow in Python), not the performance of executing code after it's created. The article also makes it clear that each sub-interpreter still has its own GIL, but two sub-intepreters can run at the same time without having to care about each other's GILs.
- chrisseaton 7y agoSo how do these subinterpreters communicate? By copying? There’s the overhead compared to threads. > Each of these [methods for communicating between subonterpreters] has pro’s and con’s, all of them have an overhead.
- loeg 7y agoOther overheads include spinning up a full interpreter state (including object and malloc caches, GC, etc) per sub-interpreter. And there are some modules with process-global semantics, such as signal-handling — it's unclear how that will be coordinated between co-interpreters, if at all.
- loeg 7y agoMessage passing via serialization / copy is inherently more expensive just passing a reference between threads. So any benefit vs threaded Python depends on the ratio of IPC to actual Python bytecodes interpreted. IPC-heavy programs with low concurrency may suffer worse under this model than threaded with traditional single GIL. As threads approach infinity, though, anything that scales beats the single GIL model.
- newen 7y agoExactly. This seems more like a way to advertise that the GIL is gone while not actually having shared memory parallelism via threads. It reminds me of how python advertising tricks people into thinking python has real parallelism by talking about ayncio and import multiprocessing; people then actually try those out only to discover the sad state of affairs.
- Animats 7y agoThis is just a way to do the same thing as "multiprocessing", but with less memory usage. You still have multiple Python instances that send messages back and forth. I wonder if they ever fixed the CPickle bug which broke it if you were using CPickle from multiple threads.
- mintplant 7y agoLess memory usage, and - hopefully - without all the quirks that crop up with multiprocessing. Off the top of my head: subprocesses don't always want to die along with the main process; error conditions can cause the underlying IPC layer to end up in a permanently stalled state.
- frutiger 7y agoI think you can avoid these kinds of issues if you use multiprocessing.Pool as a context manager.
- loeg 7y agoIt avoids some quirks but also introduces new quirks multiprocessing doesn't have, like broken C modules (including parts of the core interpreter and stdlib) that have global state, rather than per-interpreter state. There's a huge ecosystem of Python libraries in the world and most have been able to more or less ignore the distinction between per-interpreter state and global state prior to this proposal. (Not true if you actually used the C API to embed many interpreters in a process, but most people don't do that.)
- btown 7y agoIs it possible to load an instance of a native library per interpreter? Give each one its own memory space?
- pas 7y agoThat requires making the native parts of the stdlib position independent (relocatable). I don't know what obstacles are there currently, but basically you'd need to have a runtime linker that gives a reference to the loaded object/module to access the symbols, but these are usually handled by the C compiler and linker. So it's doable, just a lot of effort.
- bch 7y agoThis sounds like an application (or variation) of the apartment threading model[0]. Given the problem and it’s desrciption/characteristic (Global Interpretter Lock), this sounds like an elegant approach. [0] https://docs.microsoft.com/en-us/windows/desktop/com/processes--threads--and-apartments https://docs.microsoft.com/en-us/windows/desktop/com/process...
- tus87 7y agoThe ghost of Perl5 lives on...
- gpapilion 7y agoI was thinking this sounded nearly identical to perl 5s threading implementation.
- Ultimatt 7y agoGood thing Perl 6 was created and has a fantastic multi threading story. https://docs.perl6.org/language/concurrency https://docs.perl6.org/language/concurrency https://youtu.be/l2fSbOPeSQs https://youtu.be/l2fSbOPeSQs
- zaro 7y agoThat's what I thought also when I saw the article.
- _wmd 7y agoWhoever is downvoting this, please realize this design stinks strongly of something Perl5 had a long time ago, that almost nothing could make use of due to a variety of limitations. The question in my mind is how the Python design addresses the problems that made the perl equivalent so unusable. It's only had ~20 years of context to do better, so I'm hopeful
- fanf2 7y agoYes this is very reminiscent of the interpreter threads model from perl 5.8 (2002) https://perldoc.perl.org/threads.html https://perldoc.perl.org/threads.html
- tyingq 7y agoThe warning there is worth pulling up here: "The "interpreter-based threads" provided by Perl are not the fast, lightweight system for multitasking that one might expect or hope for. Threads are implemented in a way that make them easy to misuse. Few people know how to use them correctly or will be able to provide help. The use of interpreter-based threads in perl is officially discouraged." Perl5 also had a similar queue based scheme for sharing data across the interpreters: https://perldoc.perl.org/Thread/Queue.html https://perldoc.perl.org/Thread/Queue.html
- yingw787 7y agoFrom my limited understanding, I think Eric Snow’s push to use subinterpreters is to move an orchestration layer for multiple Python processes from the service layer to the language layer. It may also modularize Pythons’s C API scope. It may also be one of the cheapest ways in order to provide for true CPU bound concurrency in Python, which is important given Python’s limited resources.
- gigatexal 7y agoNo, Mr. Click-baity-title it’s not. They’re still there just you can use many interpreters now like one would when using the multiprocessing module. I do like the idea of Go-like queues for message passing.
- sbierwagen 7y agoBetteridge's law of headlines is an adage that states: "Any headline that ends in a question mark can be answered by the word no." https://en.wikipedia.org/wiki/Betteridge%27s_law_of_headlines https://en.wikipedia.org/wiki/Betteridge%27s_law_of_headline...
- zapzupnz 7y agoIt makes sense. If the answer could be 'yes', the title would be an affirmative statement.
- Alex3917 7y agoAre there any overall benchmarks for Python 3.8 yet? I know there are a bunch of performance improvements for calling functions and creating objects, but I have no idea how that translates to real software.
- pmontra 7y agoIt's somewhat similar to the GIL removal effort in Ruby [1] They are isolating the GIL into Guilds there, which are containers for language threads sharing the same GIL. They are providing two primitives for communication between threads in different guilds. Send, for immutable data (zero copy) and move, for mutable data (copy). They remove the need for the boiler plate code for marshalling and unmarshalling. However I bet that there will be some library to hide that code in Python too. [1] http://www.atdot.net/%7Eko1/activities/2018_RubyElixirConfTaiwan.pdf http://www.atdot.net/%7Eko1/activities/2018_RubyElixirConfTa...
- riffraff 7y agoIIUC, python's sub-interpreters won't have a `move`. That might not be a bad idea because I am worried `move` will end up being problematic in ruby, but time will tell.
- AlexTWithBeard 7y agoCopy + move looks like a typical function call to me: pass in a bunch of immutable arguments, return a result. Such approach would cover many, if not most user cases for multithreading.
- rocqua 7y agoWhat happens when the caller modifies the read only arguments while the function is running?
- pmontra 7y agoI'm not sure if this the question you're asking but in Ruby the program fails. gsub! is a method of String that mutates the object (vs gsub which returns a new string) freeze is a method that makes immutable the object it's called upon. 2.3.0 :001 > def replace_a_with_b(s) 2.3.0 :001?> s.gsub!("a", "b") 2.3.0 :001?> end => :replace_a_with_b 2.3.0 :002 > replace_a_with_b("abc") => "bbc" 2.3.0 :003 > replace_a_with_b("abc".freeze) RuntimeError: can't modify frozen String from (irb):20:in `gsub!' from (irb):20:in `replace_a_with_b' from (irb):23 from /home/me/.rvm/rubies/ruby-2.3.0/bin/irb:11:in `<main>'
- deleted 7y ago[deleted]
- Uptrenda 7y agoThere's nothing wrong with the GIL as long as you know its there. It makes writing concurrent code in Python semi-magical and thats a huge benefit. Concurrent != parallel though, so if there's really a need to scale up to multiple cores there's always the option of forking with multi-processing or "sub interpreters." I can think of maybe having network code run in their own process and the UI in another. That way there's no risk of bottle necks slowing down the UI and transfers are likewise protected. If you look at bottle.py it seems that this approach could add A LOT of performance for managing downloads / uploads if it's done right.
- weberc2 7y agoHow does the GIL help you write concurrent code?
- dual_basis 7y agoIt means you never have to worry about message passing or locks, because they aren't a thing at all. On the other hand, what looks like concurrent code often isn't, and is actually run slower than single process code, also because of the GIL.
- weberc2 7y agoThat reduces to "you don't have to worry about performance because the GIL doesn't let you write performant code". That's not what I think of as "helpful".
- gmueckl 7y agoHm, this solution seems very cumbersome, inelegant and not like python's "batteries included" approach at all. This means that python will have native threads that behhave as expected minus true parallel execution, so you shouldn't use those, even though the interface is fairly simple. Instead, you should learn to use this weird contraption that is neither multiprocessing nor intuitive multithreading and comes with a cumbersome interface. I get that the GIL is a very hard problem to solve, but this solution is so inelegant in my eyes that python would be better off without it. I'd feel better if this was a hidden implementation detail that coukd be improved transparently. Just my two cents.
- akvadrako 7y agoI completely disagree - Python threads are basically "green threads", so they have their place but aren't related to parallelisation. But true multiprocessing is ugly when you have hundreds of cores, which is where CPUs are going. There is no standard UI convention on most OSes to group those processes per app, in terms of signals or stats or whatever. So besides the unproven possibility of removing the GIL, subinterpreters are the best way forward, better than threads or the multiprocessing package.
- icebraining 7y agoIt's not that Python shouldn't have subinterpreters, but that they should only be added when they have a better API - at least as good multiprocessing.
- chrisseaton 7y agoI don’t think they’re green threads - they’re full OS threads. They just have the GIL.
- zbentley 7y ago> Python threads are basically "green threads" That's not accurate. > they have their place but aren't related to parallelisation You can parallelize all sorts of things with Python threads--just not some things you'd expect to be able to parallelize, due to the GIL. Waiting on or buffering I/O, calling out to compiled code, doing cryptographic operations--all of those can be parallelized, as (in many cases) they entail releasing the GIL. > But true multiprocessing is ugly when you have hundreds of cores Why? > There is no standard UI convention on most OSes to group those processes per app, in terms of signals or stats or whatever. I have no idea what this means. What do UIs have to do with process groups? Do you know how many processes your Chrome instance is running on the operating system? There are very solid conventions regarding process management, at least on Unix-ish systems: process groups and parent-child relationships are well established and well understood, as is their relationship with signals and signal handling.
- olliej 7y agoThis is essentially the same concurrency model as Workers in JS engines - on the one hand it’s a fairly limiting crutch[1], on the other hand it is harder to create a bunch of different classes of concurrency bugs. [1] vs fully shared state of C-like, .NET, JVM, etc, etc. Rust’s no-shared-mutable state model allows it to do some fun stuff but python (and JS) don’t really have a strong concept of mutable vs immutable, let alone ownership so I don’t think it would be applicable?
- icebraining 7y agoPython already has fully shared state across multiprocesses: https://docs.python.org/3.7/library/multiprocessing.html#shared-ctypes-objects https://docs.python.org/3.7/library/multiprocessing.html#sha...
- maayank 7y agoVery limited shared state (only c types, including structs), which in practice means (for non trivial apps) some form of marshaling from python classes to the C structs. Boilerplate abound! In other words, nothing behind mmap with simple size calculations, which is what you'd really want to abstract away.
- olliej 7y agoAs @maayank has already said, python’s shared state is limited to pure c-structures, rather than actual python objects. Web workers have the same through SharedArrayBuffer (I am unsure though whether the security pains from spectre, etc have allowed them to be turned on again). SharedArrayBuffer is fundamentally the same as python’s C-only sharing - a bunch of raw bytes not tied to the host environment’s type system.
- nicoburns 7y agoJS (or at least wasm) should be getting opt-in shared memory at some point. That seems like a pretty good way to do it to me.
- qwerty456127 7y ago> The GIL also means that whilst CPython can be multi-threaded, only 1 thread can be executing at any given time. How does this make sense? What's the point of having multiple threads then?
- munchbunny 7y agoI believe this is still beneficial in I/O bound processes.
- keypusher 7y agoNot all operations are CPU bound. For anything that is IO bound, such as reading a file, db access, network calls, etc, CPython threads work just fine.
- tylerhou 7y agoAnd even for CPU bound programs, if your CPU is running non-Python code (e.g. numpy functions that call to FORTRAN/C) then CPython threads still work.
- weberc2 7y agoUntil someone adds a callback from C back into Python and the unexpected GIL contention makes performance dramatically worse even as compared to the single-threaded version.
- boulos 7y agoThe usual answer is: in the case of blocking I/O, the thread running send/recv can block while other python code runs. In practice, this doesn’t work particularly well, as you rarely have massively I/O bound things in Python.
- keypusher 7y agoNot sure what kind of applications you work on, but most web apps are IO bound (waiting on network calls).
- mrmonkeyman 7y agoLet it rest in peace please. All non-python devs know it's taking its last breaths. Give it some space. Python is dead, long live python.
- zaptheimpaler 7y agoYou might want to look at some data around that lol.
- yjftsjthsd-h 7y agoYep, Python is dying. Just like C, Java, FORTRAN, and all the others:)
- sara7262 7y agoCute dog doing exercise like humans http://bit.ly/2Jq3rz1 http://bit.ly/2Jq3rz1 Funny gorilla acting like humans see his video on link below http://bit.ly/2JmTNxr http://bit.ly/2JmTNxr Best way to get rid from dark spots on face just follow this https://zoomtips.blogspot.com/2019/05/Dark-spots.html https://zoomtips.blogspot.com/2019/05/Dark-spots.html Funny panda is playing with friends they are sho cute http://bit.ly/2JvDQF4 http://bit.ly/2JvDQF4
- andrewshadura 7y agoTcl has had threads that were subinterpreters since a decade ago or more. I find it quite ironic that Python, it would seem, is reinventing it, only in a less elegant way.
- yjftsjthsd-h 7y agoEverything old is new again:)
- fithisux 7y agoMy thoghts exactly.
- cmacleod4 7y agoActually Tcl has been successfully using the model of one or more interpreters per thread since Tcl 8.1, released in 1999, a full TWO decades now.
- mixmastamyk 7y agoThe functionality was always there it just rusted over from disuse.
- rkeene2 7y agoI'm personally glad that Python is (poorly) copying this feature from Tcl. This means it's closer to the time when JavaScript (poorly) copies it from Python ! ;-)
- imhoguy 7y agoWouldn't it be good to have Python 4.x next with all these workarounds cleaned up and with only one right pythonic way for parallel procesing? Surelly with a bit of backward compatibility sacrificed like 2 vs 3.
- anewhnaccount2 7y agoPlease no. There's a long tail of unmaintained but working libraries. Throwing away compatibility is not worth it.
- DonHopkins 7y agoDon't think of it as throwing away compatibility, it's just adding incompatibility. ;)
- azinman2 7y agoYou could always have a compat API that’s different, or puts in “legacy” mode.
- zbentley 7y agoIn order for such an API to work, you'd have to bundle the vast majority of the existing Python runtime.
- stefano 7y agoYou'd think people would learn from the 2to3 debacle.
- _wmd 7y agoConsidering this will require basically a total rewrite of the C extension API, without having had a chance to look yet, the idea it could be done in a minor release seems shocking
- stefano 7y ago> Surelly with a bit of backward compatibility sacrificed like 2 vs 3. After 10 years, the 2to3 transition is still ongoing, and lots of companies will be dealing with python2 code for a long, long time. Breaking backward compatibility was a terrible decision. Breaking it again now that most libraries and open source projects have finally moved to python3 would be suicide. Programming languages should take backward compatibility as one of their most important features, because of the hundreds of millions of lines of code already written and that would need to be changed and validated. This is especially true in a dynamic language where you can't even rely on the compiler to point out the issues.
- FartyMcFarter 7y ago> This, in turn, means that Python developers can utilize async code, multi-threaded code and never have to worry about acquiring locks on any variables or having processes crash from deadlocks. Dangerous advice. Whether this is true or not depends on lots of things such as how many and which operations you're doing on those variables. Sure, CPython might do lots of simple operations atomically, but this is not enough to avoid the need for all locks. Threads can still interleave their execution in many ways. See also: https://blog.qqrs.us/blog/2016/05/01/which-python-operations-are-atomic/ https://blog.qqrs.us/blog/2016/05/01/which-python-operations...
- tasubotadas 7y agoThe current state of threading and parallel processing in Python is a joke. While they are still clinging to the GIL and single core performance, the rest of the world is moving to 32 core (consumer) CPUs. Python's performance, in general, is a crappy[1] and is beaten even by PHP these days. All the people that suggest relying on multiprocessing probably haven't done anything that's CPU and Memory intensive because if you have a code that operates on a "world-state" each new process will have to copy that from a parent. If the state takes ~10GB each process will multiply that. Others keep suggesting Cython. Well, guess what? If I am required to use another programming language to use threads, I might as well go with Go/Rust/Java instead and save the trouble of dabbling with two languages. So where does that leave (pure-)Python? It can only be used in I/O bound applications where the performance of the VM itself doesn't matter. So it's basically only used by web/desktop applications that CRUD the databases. It's really amazing that the machine learning community has managed to hack around that with C-based libraries like SciPy and NumPy. However, my suggestion would be to drop GIL and copy the whatever model has been working for Go/Java/C#. If you can't drop GIL because some esoteric features depend on that, then drop them as well. [1] https://benchmarksgame-team.pages.debian.net/benchmarksgame/faster/python.html https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
- deleted 7y ago[deleted]
- AlexTWithBeard 7y agoCython is nice, but debugging it requires gdb. For the PyCharm-loving end-users it may be quite cumbersome. Those recommending to use multiprocessing have probably never been in that bitter spot when serializing something and computing something takes exactly the same time. Also forking didn't really work until Python 3.6.
- juststeve 7y ago> Go/Rust/Java And there's also Kotlin.
- gray_-_wolf 7y ago> If the state takes ~10GB each process will multiply that. In POSIX there is such thing as copy-on-write memory during forks.. So if that state is mostly read-only, additional memory required by each slave should be minimal.
- AlexTWithBeard 7y agoLarry Hastings gilectomy project is an interesting approach. https://lwn.net/Articles/754577/ https://lwn.net/Articles/754577/ TLDR: simply replacing object usage counters with their atomic versions grinds the interpreter to the halt.
- sciurus 7y agoSome earlier coverage: https://lwn.net/Articles/754162/ https://lwn.net/Articles/754162/
- sandGorgon 7y agohmm...there's no mention of Gevent - does Gevent share GIL state as well ?
- mballantyne 7y agoRacket's "places" work a similar way, though do a bit extra to get down to one memory copy, rather than two: https://www.cs.utah.edu/plt/publications/dls11-tsffd.pdf https://www.cs.utah.edu/plt/publications/dls11-tsffd.pdf
- madhadron 7y agoAm I misreading, or does this say that I have to serialize and deserialize data within the same process?
- eximius 7y agoOof, that code-as-strings API guarantees I will never use it.
- firethief 7y ago> If you want truly concurrent code in CPython, you have to use multiple processes. Uh what?
- cyphar 7y ago> Another issue is that file handles belong to the process, so if you have a file open for writing in one interpreter, the sub interpreter won’t be able to access the file (without further changes to CPython). Wouldn't just using CLONE_FILES when forking off interpreters solve this problem?
- dragonwriter 7y agoHuh. This sounds a lot like Ruby Guilds. This looks it will land sooner, though likely in less complete form, as even the prototype Guild implementation has inter-guild communication.
- magwa101 7y agoSame process for everyone, small team bootstraps with Python. With success they find another language, now, mostly Go.
- MichaelMoser123 7y agoWow, just like perl threads since perl 5.8 (1) When in doubt, look at the granddaddy of scripting languages, all your trials and tribulations in scripting land have been considered in the past.. let's all sing 'living in the past' by Jethro Tull (2) this one is also good (3) (1) https://perldoc.perl.org/threads.html https://perldoc.perl.org/threads.html (2) https://m.youtube.com/watch?v=EsCyC1dZiN8 https://m.youtube.com/watch?v=EsCyC1dZiN8 (3) https://m.youtube.com/watch?v=mXeoNX7DSc8 https://m.youtube.com/watch?v=mXeoNX7DSc8