| Store | Cart

[Python-ideas] solving multi-core Python

From: Eric Snow <eric...@gmail.com>
Sat, 20 Jun 2015 15:42:33 -0600
tl;dr Let's exploit multiple cores by fixing up subinterpreters,
exposing them in Python, and adding a mechanism to safely share
objects between them.

This proposal is meant to be a shot over the bow, so to speak.  I plan
on putting together a more complete PEP some time in the future, with
content that is more refined along with references to the appropriate
online resources.

Feedback appreciated!  Offers to help even more so! :)

-eric

--------

Python's multi-core story is murky at best.  Not only can we be more
clear on the matter, we can improve Python's support.  The result of
any effort must make multi-core (i.e. parallelism) support in Python
obvious, unmistakable, and undeniable (and keep it Pythonic).

Currently we have several concurrency models represented via
threading, multiprocessing, asyncio, concurrent.futures (plus others
in the cheeseshop).  However, in CPython the GIL means that we don't
have parallelism, except through multiprocessing which requires
trade-offs. (See Dave Beazley's talk at PyCon US 2015.)

This is a situation I'd like us to solve once and for all for a couple
of reasons.  Firstly, it is a technical roadblock for some Python
developers, though I don't see that as a huge factor.  Regardless,
secondly, it is especially a turnoff to folks looking into Python and
ultimately a PR issue.  The solution boils down to natively supporting
multiple cores in Python code.

This is not a new topic.  For a long time many have clamored for death
to the GIL.  Several attempts have been made over the years and failed
to do it without sacrificing single-threaded performance.
Furthermore, removing the GIL is perhaps an obvious solution but not
the only one.  Others include Trent Nelson's PyParallels, STM, and
other Python implementations..

Proposal
=======

In some personal correspondence Nick Coghlan, he summarized my
preferred approach as "the data storage separation of multiprocessing,
with the low message passing overhead of threading".

For Python 3.6:

* expose subinterpreters to Python in a new stdlib module: "subinterpreters"
* add a new SubinterpreterExecutor to concurrent.futures
* add a queue.Queue-like type that will be used to explicitly share
objects between subinterpreters

This is less simple than it might sound, but presents what I consider
the best option for getting a meaningful improvement into Python 3.6.

Also, I'm not convinced that the word "subinterpreter" properly
conveys the intent, for which subinterpreters is only part of the
picture.  So I'm open to a better name.

Influences
========

Note that I'm drawing quite a bit of inspiration from elsewhere.  The
idea of using subinterpreters to get this (more) efficient isolated
execution is not my own (I heard it from Nick).  I have also spent
quite a bit of time and effort researching for this proposal.  As part
of that, a number of people have provided invaluable insight and
encouragement as I've prepared, including Guido, Nick, Brett Cannon,
Barry Warsaw, and Larry Hastings.

Additionally, Hoare's "Communicating Sequential Processes" (CSP) has
been a big influence on this proposal.  FYI, CSP is also the
inspiration for Go's concurrency model (e.g. goroutines, channels,
select).  Dr. Sarah Mount, who has expertise in this area, has been
kind enough to agree to collaborate and even co-author the PEP that I
hope comes out of this proposal.

My interest in this improvement has been building for several years.
Recent events, including this year's language summit, have driven me
to push for something concrete in Python 3.6.

The subinterpreter Module
=====================

The subinterpreters module would look something like this (a la
threading/multiprocessing):

  settrace()
  setprofile()
  stack_size()
  active_count()
  enumerate()
  get_ident()
  current_subinterpreter()
  Subinterpreter(...)
      id
      is_alive()
      running() -> Task or None
      run(...) -> Task  # wrapper around PyRun_*, auto-calls Task.start()
      destroy()
  Task(...)  # analogous to a CSP process
      id
      exception()
      # other stuff?

      # for compatibility with threading.Thread:
      name
      ident
      is_alive()
      start()
      run()
      join()
  Channel(...)  # shared by passing as an arg to the subinterpreter-running func
      # this API is a bit uncooked still...
      pop()
      push()
      poison()  # maybe
  select()  # maybe

Note that Channel objects will necessarily be shared in common between
subinterpreters (where bound).  This sharing will happen when the one
or more of the parameters to the function passed to Task() is a
Channel.  Thus the channel would be open to the (sub)interpreter
calling Task() (or Subinterpreter.run()) and to the new
subinterpreter.  Also, other channels could be fed into such a shared
channel, whereby those channels would then likewise be shared between
the interpreters.

I don't know yet if this module should include *all* the essential
pieces to implement a complete CSP library.  Given the inspiration
that CSP is providing, it may make sense to support it fully.  It
would be interesting then if the implementation here allowed the
(complete?) formalisms provided by CSP (thus, e.g. rigorous proofs of
concurrent system models).

I expect there will also be a _subinterpreters module with low-level
implementation-specific details.

Related Ideas and Details Under Consideration
====================================

Some of these are details that need to be sorted out.  Some are
secondary ideas that may be appropriate to address in this proposal or
may need to be tabled.  I have some others but these should be
sufficient to demonstrate the range of points to consider.

* further coalesce the (concurrency/parallelism) abstractions between
threading, multiprocessing, asyncio, and this proposal
* only allow one running Task at a time per subinterpreter
* disallow threading within subinterpreters (with legacy support in C)
  + ignore/remove the GIL within subinterpreters (since they would be
single-threaded)
* use the GIL only in the main interpreter and for interaction between
subinterpreters (and a "Local Interpreter Lock" for within a
subinterpreter)
* disallow forking within subinterpreters
* only allow passing plain functions to Task() and
Subinterpreter.run() (exclude closures, other callables)
* object ownership model
  + read-only in all but 1 subinterpreter
  + RW in all subinterpreters
  + only allow 1 subinterpreter to have any refcounts to an object
(except for channels)
* only allow immutable objects to be shared between subinterpreters
* for better immutability, move object ref counts into a separate table
* freeze (new machinery or memcopy or something) objects to make them
(at least temporarily) immutable
* expose a more complete CSP implementation in the stdlib (or make the
subinterpreters module more compliant)
* treat the main interpreter differently than subinterpreters (or
treat it exactly the same)
* add subinterpreter support to asyncio (the interplay between them
could be interesting)

Key Dependencies
================

There are a few related tasks/projects that will likely need to be
resolved before subinterpreters in CPython can be used in the proposed
manner.  The proposal could implemented either way, but it will help
the multi-core effort if these are addressed first.

* fixes to subinterpreter support (there are a couple individuals who
should be able to provide the necessary insight)
* PEP 432 (will simplify several key implementation details)
* improvements to isolation between subinterpreters (file descriptors,
env vars, others)

Beyond those, the scale and technical scope of this project means that
I am unlikely to be able to do all the work myself to land this in
Python 3.6 (though I'd still give it my best shot).  That will require
the involvement of various experts.  I expect that the project is
divisible into multiple mostly independent pieces, so that will help.

Python Implementations
===================

They can correct me if I'm wrong, but from what I understand both
Jython and IronPython already have subinterpreter support.  I'll be
soliciting feedback from the different Python implementors about
subinterpreter support.

C Extension Modules
=================

Subinterpreters already isolate extension modules (and built-in
modules, including sys).  PEP 384 provides some help too.  However,
global state in C can easily leak data between subinterpreters,
breaking the desired data isolation.  This is something that will need
to be addressed as part of the effort.
_______________________________________________
Python-ideas mailing list
Pyth...@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Recent Messages in this Thread
Eric Snow Jun 20, 2015 09:42 pm
Yury Selivanov Jun 20, 2015 10:04 pm
Devin Jeanpierre Jun 20, 2015 10:54 pm
Eric Snow Jun 20, 2015 11:16 pm
Devin Jeanpierre Jun 21, 2015 11:55 am
Gregory P. Smith Jun 22, 2015 05:37 pm
Nick Coghlan Jun 22, 2015 10:30 pm
Sturla Molden Jun 22, 2015 10:50 pm
Gregory P. Smith Jun 22, 2015 11:29 pm
Devin Jeanpierre Jun 22, 2015 11:51 pm
Sturla Molden Jun 23, 2015 11:57 am
Jonas Wielicki Jun 23, 2015 01:14 pm
Sturla Molden Jun 23, 2015 02:55 pm
Devin Jeanpierre Jun 23, 2015 11:32 pm
Eric Snow Jun 24, 2015 06:18 am
Eric Snow Jun 24, 2015 06:19 am
Nathaniel Smith Jun 22, 2015 11:59 pm
Chris Angelico Jun 23, 2015 12:03 am
Nick Coghlan Jun 23, 2015 03:52 am
Barry Warsaw Jun 23, 2015 04:01 pm
Gregory P. Smith Jun 23, 2015 07:26 pm
Eric Snow Jun 24, 2015 06:15 am
Stephen J. Turnbull Jun 24, 2015 02:59 pm
Eric Snow Jun 24, 2015 06:12 am
Eric Snow Jun 24, 2015 06:11 am
Nathaniel Smith Jun 24, 2015 07:19 am
Eric Snow Jun 24, 2015 05:26 am
Devin Jeanpierre Jun 24, 2015 10:10 pm
Sturla Molden Jun 24, 2015 11:30 pm
Devin Jeanpierre Jun 25, 2015 12:09 am
Sturla Molden Jun 25, 2015 12:45 am
Nathaniel Smith Jun 25, 2015 01:05 am
Nathaniel Smith Jun 20, 2015 10:08 pm
Eric Snow Jun 20, 2015 10:54 pm
Nathaniel Smith Jun 21, 2015 05:25 am
Nick Coghlan Jun 21, 2015 06:31 am
Antoine Pitrou Jun 21, 2015 09:48 am
Nick Coghlan Jun 21, 2015 10:25 am
Stefan Behnel Jun 21, 2015 10:40 am
Trent Nelson Jun 23, 2015 06:29 pm
Antoine Pitrou Jun 23, 2015 08:46 pm
Eric Snow Jun 24, 2015 04:15 am
Stefan Behnel Jun 25, 2015 07:00 pm
Antoine Pitrou Jun 21, 2015 10:41 am
Nick Coghlan Jun 21, 2015 10:57 am
Eric Snow Jun 24, 2015 04:21 am
Eric Snow Jun 24, 2015 03:08 am
Devin Jeanpierre Jun 21, 2015 12:13 pm
Sturla Molden Jun 21, 2015 03:14 pm
Eric Snow Jun 24, 2015 05:30 am
Eric Snow Jun 24, 2015 02:37 am
Eric Snow Jun 24, 2015 02:18 am
Wes Turner Jun 21, 2015 06:41 am
Eric Snow Jun 24, 2015 02:39 am
Ron Adam Jun 21, 2015 06:54 pm
Chris Angelico Jun 21, 2015 12:41 am
Eric Snow Jun 21, 2015 12:58 am
Nick Coghlan Jun 21, 2015 01:28 am
Stefan Behnel Jun 21, 2015 01:06 pm
Eric Snow Jun 24, 2015 05:33 am
Nick Coghlan Jun 24, 2015 08:22 am
M.-A. Lemburg Jun 24, 2015 11:43 am
Sturla Molden Jun 24, 2015 04:58 pm
M.-A. Lemburg Jun 24, 2015 08:50 pm
Sturla Molden Jun 24, 2015 09:41 pm
Sturla Molden Jun 24, 2015 09:48 pm
Wes Turner Jun 24, 2015 09:57 pm
Nick Coghlan Jun 25, 2015 01:24 pm
Rustom Mody Jun 21, 2015 03:04 am
Gregory P. Smith Jun 22, 2015 05:03 pm
Chris Angelico Jun 22, 2015 11:59 pm
Steven DAprano Jun 21, 2015 03:38 am
Eric Snow Jun 21, 2015 05:01 am
Antoine Pitrou Jun 21, 2015 09:54 am
Eric Snow Jun 24, 2015 03:05 am
Stefan Behnel Jun 21, 2015 10:54 am
Eric Snow Jun 24, 2015 04:19 am
Sturla Molden Jun 21, 2015 11:41 am
Antoine Pitrou Jun 21, 2015 11:52 am
Antoine Pitrou Jun 21, 2015 01:21 pm
Sturla Molden Jun 21, 2015 04:13 pm
Nick Coghlan Jun 21, 2015 01:09 pm
Sturla Molden Jun 21, 2015 03:45 pm
Nick Coghlan Jun 21, 2015 11:31 pm
Antoine Pitrou Jun 21, 2015 11:39 pm
Nick Coghlan Jun 21, 2015 11:47 pm
Antoine Pitrou Jun 22, 2015 12:49 am
Sturla Molden Jun 22, 2015 12:41 am
Andrew Barnert via Python-ideas Jun 21, 2015 09:24 pm
Eric Snow Jun 24, 2015 05:51 am
Chris Angelico Jun 21, 2015 02:12 pm
Eric Snow Jun 24, 2015 05:01 am
Sturla Molden Jun 24, 2015 03:26 pm
Oleg Broytman Jun 24, 2015 07:08 pm
Gregory P. Smith Jun 24, 2015 07:31 pm
Devin Jeanpierre Jun 24, 2015 10:02 pm
Eric Snow Jun 24, 2015 10:19 pm
Sturla Molden Jun 25, 2015 12:02 am
Trent Nelson Jun 25, 2015 07:01 am
Nick Coghlan Jun 26, 2015 11:20 am
Sturla Molden Jun 24, 2015 04:28 pm
Eric Snow Jun 24, 2015 10:56 pm
Sturla Molden Jun 24, 2015 11:47 pm
Nathaniel Smith Jun 24, 2015 11:55 pm
Trent Nelson Jun 25, 2015 06:50 am
Sturla Molden Jun 25, 2015 09:35 am
Eric Snow Jun 25, 2015 01:57 am
Oscar Benjamin Jun 26, 2015 03:35 pm
Nick Coghlan Jun 25, 2015 02:08 pm
Nick Coghlan Jun 25, 2015 02:31 pm
Sturla Molden Jun 25, 2015 03:25 pm
Wes Turner Jun 25, 2015 09:51 pm
Ethan Furman Jun 25, 2015 10:11 pm
Nick Coghlan Jun 26, 2015 11:12 am
Sturla Molden Jun 25, 2015 03:18 pm
Trent Nelson Jun 25, 2015 04:59 am
Nathaniel Smith Jun 25, 2015 08:58 am
Sturla Molden Jun 25, 2015 09:35 am
M.-A. Lemburg Jun 25, 2015 10:00 am
Nick Coghlan Jun 21, 2015 12:55 pm
Andrew Barnert via Python-ideas Jun 21, 2015 09:08 pm
Antoine Pitrou Jun 21, 2015 10:41 pm
rand...@fastmail.us Jun 23, 2015 05:56 am
Nathaniel Smith Jun 23, 2015 06:18 am
Eric Snow Jun 24, 2015 05:48 am
Nick Coghlan Jun 22, 2015 01:47 am
Eric Snow Jun 24, 2015 06:01 am
Dan OReilly Jun 24, 2015 02:01 pm
Messages in this thread