Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Future of Pypy?

281 views
Skip to first unread message

Dave Farrance

unread,
Feb 22, 2015, 7:45:15 AM2/22/15
to
As an engineer, I can quickly knock together behavioural models of
electronic circuits, complete units, and control systems in Python, then
annoyingly in a few recent cases, have to re-write in C for speed.

I've tried PyPy, the just-in-time compiler for Python, and that is
impressively, hugely fast in comparison, but it's no good making these
models if I can't display the results in a useful way, and at the moment
PyPy just doesn't have the huge range of useful time-saving libraries that
CPython has. It's still quicker to do a re-write in the more cumbersome C
than try to work with PyPy because C, like CPython, also has many useful
libraries.

A few years back, I recall people saying that PyPy was going to be the
future of Python, but it seems to me that CPython still has the lion's
share of the momentum, is developing faster and has ever more libraries,
while PyPy is struggling to get enough workers to even get Numpy
completed.

Maybe there's not enough people like me that have really felt the need for
the speed. Or maybe it's simply the accident of the historical
development path that's set-in-stone an interpreter rather than a JIT.
Anybody got a useful perspective on this?

jkn

unread,
Feb 22, 2015, 7:58:12 AM2/22/15
to
I'm curious what ...behavioural... models you are creating quickly in Python that then need rewriting in C for speed. SPICE? some other CAD? Might be interesting to learn more about what and how you are actually doing.

How about running your front end (simulation) work in PyPy, and the backend display work on CPython, if there are some missing features in PyPy that you need. This may be more or less easy depending on your requirements and any intermediate format you have.

Or you could offer to assist in the PyPy porting? Or express an interest in specific libraries being ported?

Cheers
Jon N

Laura Creighton

unread,
Feb 22, 2015, 8:28:02 AM2/22/15
to Dave Farrance, pytho...@python.org
In a message of Sun, 22 Feb 2015 12:45:03 +0000, Dave Farrance writes:

>Maybe there's not enough people like me that have really felt the need for
>the speed. Or maybe it's simply the accident of the historical
>development path that's set-in-stone an interpreter rather than a JIT.
>Anybody got a useful perspective on this?

I don't understand 'an interpreter rather than a JIT'. PyPy has a
JIT, that sort of is the whole point.

One problem is that hacking on PyPy itself is hard. Lots of people
find it too hard, and give up. (Of course, lots of people give up
on hacking CPython too. I think that hacking on PyPy is harder than
hacking on CPython, but I am quite biased.) So this is a barrier to
getting more people to work on it.

Provided your code is in pure python, PyPy already works great for you.
as you found out. Pure Python libraries aren't a problem, either.

The problem arises when you want to add your fancy graphic library to
what you do, because chances are that fancy library is a C extension.
And there is no magic 'sprinke this dust here' on a C extension that
makes it acceptable to PyPy. It's a hard job. The PyPy team has
gone after, and rewritten how to do this I think 5 times now. Maybe
more. Every time the goal has been to make it easier for the average
programmer to make an interface, and, of course to not slow things down
too much. C extensions, in general, make PyPy code run a lot slower because
you cannot get the JIT in there to speed things up, so you may be
stuck with unjitted PyPy performance, even on your python code,
which isn't speedy. You also find lots of bugs in the C extensions,
which don't get noticed until you, for instance, no longer have a ref
counting GC.

Some of the things aren't really bugs, exactly, just that the person
who wrote the thing knew far, far, too much about how CPython works
and has produced something that had no need or desire to be portable
anywhere else. The closer the person who wrote the extension
was 'to the metal' .. knowing _exactly_ how CPython does things and how
to squeeze that for the tiniest last drop of performance improvement ....
the harder things are for whoever wants to get it to work with PyPy, which
has a competely different architecture and a whole lot of other assumptions.

And the slower that it will run. So, if it is a small thing, then the
usual suggestion is to rewrite it in pure python and let the JIT handle
it. Very, very often the result is faster than the pure C code, but
clearly this isn't something you want to do with a huge graphics
library ...

There is hope that we can take another crack at this problem using
things learned from the Transactional Memory stuff, but nobody is promising
anything yet. Also Armin has had a new neat thought.

https://mail.python.org/pipermail/pypy-dev/2015-February/013085.html

If we can get rid of the warmup time, then PyPy should be more popular
than it is now. Lots of people run PyPy once, un-warmed up, see no
big improvement, and decide it's not for them.

But the problem of

'here is a huge chunk of C code, designed to work with Language X.
Now make it work with Language Y (PyPy) which isn't written in C'
can only be simplified to a certain extent. There comes a point
where _this is just bloody hard_.

Laura

Steven D'Aprano

unread,
Feb 22, 2015, 9:05:57 AM2/22/15
to
Dave Farrance wrote:

> As an engineer, I can quickly knock together behavioural models of
> electronic circuits, complete units, and control systems in Python, then
> annoyingly in a few recent cases, have to re-write in C for speed.
>
> I've tried PyPy, the just-in-time compiler for Python, and that is
> impressively, hugely fast in comparison, but it's no good making these
> models if I can't display the results in a useful way, and at the moment
> PyPy just doesn't have the huge range of useful time-saving libraries that
> CPython has.

I assume you're talking about drawing graphics rather than writing text. Can
you tell us which specific library or libraries won't run under PyPy?



--
Steven

Dave Farrance

unread,
Feb 22, 2015, 10:31:08 AM2/22/15
to
jkn <jkn...@nicorp.f9.co.uk> wrote:

> I'm curious what ...behavioural... models you are creating quickly in
> Python that then need rewriting in C for speed. SPICE? some other CAD?
> Might be interesting to learn more about what and how you are actually
> doing.

The convert-to-C cases were complex filtering functions. I do make good
use of spice-based tools, but I often find it useful to make a more
abstract model, usually before completing the design. This helps with
component selection, finalizing the design, and making sure that I
understood the what the circuit would do.

I started work 1980ish, had an early 6502-based home computer, and my then
place of work had some 6502-based Pet computers, so I gained the ability
to quickly write BASIC programs as an engineering aid. Later, when BASIC
dropped into obscurity, I switched to C and C++, although I always found
that cumbersome compared to the old BASIC. Later still, when I found that
my Google queries for code examples started returning more Python than C,
I tried that -- and discovered that Python was like BASIC, only better.

But that's just me. Other hardware engineers use a variety of modeling
applications. Or don't need to because they're just that clever? Or they
give the modeling work to system engineers who will use whatever apps that
system engineers use, and will return a result a few weeks later.
Personally, I've tended to get used to writing code in just one
general-purpose language, and it seems to me that I get a useful result
relatively quickly.

> How about running your front end (simulation) work in PyPy, and the
> backend display work on CPython, if there are some missing features in
> PyPy that you need. This may be more or less easy depending on your
> requirements and any intermediate format you have.

Maybe I should look at that again. In the case of the filter models,
their usefulness had grown to the point that requiring support by other
people was a possibility, so converting them to C seemed better than
writing something that bridged between two language implementations.

> Or you could offer to assist in the PyPy porting? Or express an interest
> in specific libraries being ported?

I'm a hardware engineer not a software engineer, so I have to plead lack
of ability there. I do appreciate the work that's done on Python, and
have to be grateful for what is available, since I'm not paying for it.

Dave Farrance

unread,
Feb 22, 2015, 10:36:54 AM2/22/15
to
Laura Creighton <l...@openend.se> wrote:

>I don't understand 'an interpreter rather than a JIT'. PyPy has a
>JIT, that sort of is the whole point.

Yes. I meant that from my end-user, non-software-engineer perspective, it
looked as though CPython was proceeding with leaps and bounds and that
PyPy remained mostly a proof-of-concept after several years.

But thanks for your description of the issues. So once the core issues
are sorted out, if enough people can be found to work on it, then
hopefully the library conversions will follow apace.

Dave Farrance

unread,
Feb 22, 2015, 10:45:09 AM2/22/15
to
Steven D'Aprano <steve+comp....@pearwood.info> wrote:

>I assume you're talking about drawing graphics rather than writing text. Can
>you tell us which specific library or libraries won't run under PyPy?

Yes, mainly the graphics. I'm a hardware engineer, not a software
engineer, so I might well be misunderstanding PyPy's current capability.

For easy-to-use vector graphics output, like 1980s BASIC computers, I've
settled on Pygame. CPython libraries that I've used for other reasons
include Scipy, Matplotlib, PIL, CV2, and Kivy.

Laura Creighton

unread,
Feb 22, 2015, 12:22:42 PM2/22/15
to Dave Farrance, pytho...@python.org, l...@openend.se
In a message of Sun, 22 Feb 2015 15:36:42 +0000, Dave Farrance writes:
>Laura Creighton <l...@openend.se> wrote:
>
>>I don't understand 'an interpreter rather than a JIT'. PyPy has a
>>JIT, that sort of is the whole point.
>
>Yes. I meant that from my end-user, non-software-engineer perspective, it
>looked as though CPython was proceeding with leaps and bounds and that
>PyPy remained mostly a proof-of-concept after several years.

Oh no, that was the state of the world about 8 years ago. PyPy works
great, and more and more people are using it in production all the
time.

>But thanks for your description of the issues. So once the core issues
>are sorted out, if enough people can be found to work on it, then
>hopefully the library conversions will follow apace.

Well, most libraries just run, of course. It's the ones that are written
in C that cause most of the problems.

One of the problems, from a pure Have-PyPy-Take-Over-the-World
perspective is that as things were moving along quite merrily,
the CPython core developers decided to invent Python 3.0. This
meant that everybody on the planet, who wanted their library to
work with Python 3.0 had to convert it to work there.

There was, and still is, an enormous amount of resentment about this.
For a lot of people, the perception was, and still is that the benefits
of Python 3.x over Python 2.x was not worth breaking backwards
compatibility. And there still are plenty of places whose plan is
to use Python 2.7 indefinitely into the far future. I've got 15
years worth of commerical python code out there in the world, and
nobody wants it converted enough to pay me to do so. Their position
is that it runs quite well enough as it is. I'm sure not
going to convert the stuff for fun. Practically every Python consultant on
the planet is in the same boat.

Things will get converted when there is a compelling business argument
to do so, but not before, and for a lot of code the answer is never.

Given the nature of this political problem, it is not surprising that
the proponents of Python 3.0 spent a lot of effort talking about the
benefits, and praising the people who converted their stuff, and
making a huge effort in the public relations lines. The whole thing
could have blown up in their faces, as is quite common when you decide
to make the 'second version' of a language. It happened to Perl. So
this creates buzz and warm feelings about Python 3.0.

In contrast, on the PyPy team, there is nobody who doesn't consider
public relations and marketing and 'creating the warm fuzzy feelings in
the users' somewhere between 'unpleasant duty' and 'sheer torture'.
The set of human skills you need to have to be good as this sort of
thing is not a set that we have, either collectively or in individuals.
We're much more into 'letting the code speak for itself', which, of
course, it does not do.

A lot of us, indeed were raised on a philosophy that it is morally wrong
to try to influence people. You can give them options, and you can
even explain the options that you are giving them, and you can argue
with others in favour of certain options, but when it comes right down to
it, everybody has to make their own decision.

This is all well and virtuous, but the fact of the matter is that a large
number of people aren't competant to make their own decisions, and even
among those that are there exist a large number who very
much don't want to do such a thing. If you are trying to get such people
to adopt your software, you need to provide a completely different
experience. They need to feel comfortable, and safe, and among a
large community, and, well, I don't know what else they want. That is
part of the problem. I am pretty sure that what they want is something
that I never pay a lot of attention to. I mean I'm a charter member of
the 'always-sacrifice-comfort-in-order-to-have-fun-and-interesting-times'
club. And my marketing skills, such as they are, are much above average
for the PyPy gang - though some members are learning a bit, slowly, through
necessity. But you notice that we have only 1 blog, and things are added
to it very slowly. There are people all over planet python who blog about
things every week, for fun. There is no way we can compete with them.

So, until some people with such skills decide to take an interest in
PyPy, our marketing effort is going to limp. And I personally feel
pretty bad about asking some poor soul who has just made his C extension
work with 3.0 go back and do it _again_ in a more PyPy friendly way.

But if Armin gets the Transactional Memory to be usable in a robust
way, (as opposed to now where it is only a bit more than a proof of
concept) then things could rocket off again. Because one thing we
do know is that people who are completely and utterly ignorant about
whether having multiple cores will improve their code still want to
use a language that lets them use the multiple processors. If the
TM dream of having that just happen, seemlessly (again, no promises)
is proven to be true, well .... we think that the hordes will suddenly
be interested in PyPy.

But you never know. Lots of people were doing
-complain-complain-python-is-too-slow, and wouldn't take the argument
that developer time is worth more than CPU time and the like. We
made them PyPy. They've stopped complaining about the speed, but they
aren't using PyPy either. Seems they didn't really need the speed,
after all. It is tempting to believe that they just liked
complaining. So maybe the 'cannot use all my cores' complaint is
fueled by other people who complain for the fun of it. Then there
will be no hordes even when we crack that nut too.

But it is very, very interesting and a whole lot of fun.

Laura

Paul Rubin

unread,
Feb 22, 2015, 2:02:57 PM2/22/15
to
Laura Creighton <l...@openend.se> writes:
> Because one thing we do know is that people who are completely and
> utterly ignorant about whether having multiple cores will improve
> their code still want to use a language that lets them use the
> multiple processors. If the TM dream of having that just happen,
> seemlessly (again, no promises) is proven to be true, well .... we
> think that the hordes will suddenly be interested in PyPy.

TM is a useful feature but it's unlikely to be the thing that attracts
"the hordes". More important is to eliminate the GIL and hopefully have
lightweight (green) threads that can still run on multiple cores, like
in GHC and Erlang. Having them communicate by mailboxes/queues is
sufficient most of the time, and in Erlang it's the only method allowed
in theory (there are some optimizations taking place behind the scenes).
TM hasn't gotten that much uptake in GHC (one of the earliest HLL
implementations of TM) in part because its performance cost is
significant when there's contention. I wonder if Clojure programmers
use it more.

Dave Farrance

unread,
Feb 22, 2015, 2:20:55 PM2/22/15
to
I see that PyPy's website says that PIL (Pillow) works. I have so far
only used Python libraries that were readily available as binaries for
Windows, or were already available in Linux distro repositories. In
Ubuntu, for example, Pillow is available for CPython but not PyPy. Is
there a guide to tell me (in non-developer language, hopefully) how to
install Pillow for PyPy on Ubuntu?

Laura Creighton

unread,
Feb 22, 2015, 2:52:39 PM2/22/15
to pytho...@python.org
The GIL isn't going away from PyPy any time real soon, alas. Armin has
some pretty cool ideas about what to do about contention, but if
you want to hear them, its better if you go post that to pypy...@python.org
so you can get it from the man directly rather that hearing my
paraphrase. Or ask away on the #pypy channel on freenode ...

But this reminds me that I have to get Lennart Augustsson and Armin
Rigo in the same room some time. Should be fun.

Laura

Paul Rubin

unread,
Feb 22, 2015, 3:15:00 PM2/22/15
to
Laura Creighton <l...@openend.se> writes:
> The GIL isn't going away from PyPy any time real soon, alas.

I thought the GIL's main purpose was to avoid having to lock all the
CPython refcount updates, so if PyPy has tracing GC, why is there still
a GIL? And how is TM going to help with parallelism if the GIL is still
there?

> Armin has some pretty cool ideas about what to do about contention,
> but if you want to hear them, its better if you go post that to
> pypy...@python.org... Or ask away on the #pypy channel on freenode

It would be nice if he blogged something about them.

> But this reminds me that I have to get Lennart Augustsson and Armin
> Rigo in the same room some time. Should be fun.

I thought the STM stuff in GHC was done by the Simon's. Armin should
certainly have Simon Marlow's book about concurrency and Haskell:

http://chimera.labs.oreilly.com/books/1230000000929/index.html

Laura Creighton

unread,
Feb 22, 2015, 4:46:08 PM2/22/15
to pytho...@python.org
Good news -- it seems to be working fine with PyPy.
https://travis-ci.org/hugovk/Pillow/builds

for me, not extensively tested, it just seems to be working.

I have several pypy's floating around here, each within its own
virtualenv. If you aren't familiar with virtualenv, read all
about it here:
http://www.dabapps.com/blog/introduction-to-pip-and-virtualenv-python/

Note the first question to the blog writer is 'how to get it to work
with pypy'. Do what he says. virtualenv -p /path/to/pypy env
but, if you want to use more bleeding edge pypy you will want:

# from a tarball
$ virtualenv -p /opt/pypy-c-jit-41718-3fb486695f20-linux/bin/pypy my-pypy-env

# from the mercurial checkout
$ virtualenv -p /path/to/pypy/pypy/translator/goal/pypy-c my-pypy-env

I've only got bleeding edge PyPys around here, in virtualenvs, but
in all of them

import sys
from PIL import Image

for infile in sys.argv[1:]:
try:
with Image.open(infile) as im:
print(infile, im.format, "%dx%d" % im.size, im.mode)
except IOError:
pass

which I pasted right in from
http://pillow.readthedocs.org/en/latest/handbook/tutorial.html

seems to be working just fine for me. Hardly an exhaustive test,
but ... well, try it and see how it goes for you.

I don't know what time it is where you are, but it is 22:44 here now, and
alas I promised a kivy demo to a client tomorrow morning, and, double
alas, I haven't written it yet. It shouldn't take more than an hour or
three to write, but I am going to have to stop having pleasant chats
about pypy for a while and get this thing done ... :)

Laura




Laura Creighton

unread,
Feb 22, 2015, 5:14:42 PM2/22/15
to pytho...@python.org, l...@openend.se
In a message of Sun, 22 Feb 2015 12:14:45 -0800, Paul Rubin writes:
>Laura Creighton <l...@openend.se> writes:
>> The GIL isn't going away from PyPy any time real soon, alas.
>
>I thought the GIL's main purpose was to avoid having to lock all the
>CPython refcount updates, so if PyPy has tracing GC, why is there still
>a GIL? And how is TM going to help with parallelism if the GIL is still
>there?

This requires a long answer. a very long answer.
More later, I must work this evening.

>> Armin has some pretty cool ideas about what to do about contention,
>> but if you want to hear them, its better if you go post that to
>> pypy...@python.org... Or ask away on the #pypy channel on freenode
>
>It would be nice if he blogged something about them.

You are asking for water to roll up-hill. If you want the joy of
hearing the cool ideas as Armin has them, you need to hang out on
the irc channel. Of course, if you are interested in such things
this makes hanging out there worthwhile.

>> But this reminds me that I have to get Lennart Augustsson and Armin
>> Rigo in the same room some time. Should be fun.
>
>I thought the STM stuff in GHC was done by the Simon's. Armin should
>certainly have Simon Marlow's book about concurrency and Haskell:

Of course, but if you think that Lennart Augustsson is not familiar
with every aspect of every Haskell compiler on the planet .... well
then I know Lennart better than you do. And given that Lennart is
a friend, well really a good friend of my lover and a something-better-
than-an-acquaintance with me ---- I should make the effort to get these
two under the same roof (mine, by preference) for the fun of the
experience.

So thank you for giving me this idea ...

Laura


Steven D'Aprano

unread,
Feb 22, 2015, 8:19:17 PM2/22/15
to
Paul Rubin wrote:

> Laura Creighton <l...@openend.se> writes:
>> Because one thing we do know is that people who are completely and
>> utterly ignorant about whether having multiple cores will improve
>> their code still want to use a language that lets them use the
>> multiple processors. If the TM dream of having that just happen,
>> seemlessly (again, no promises) is proven to be true, well .... we
>> think that the hordes will suddenly be interested in PyPy.
>
> TM is a useful feature but it's unlikely to be the thing that attracts
> "the hordes". More important is to eliminate the GIL

*rolls eyes*

I'm sorry, but the instant somebody says "eliminate the GIL", they lose
credibility with me. Yes yes, I know that in *your* specific case you've
done your research and (1) multi-threaded code is the best solution for
your application and (2) alternatives aren't suitable.

Writing multithreaded code is *hard*. It is not a programming model which
comes naturally to most human beings. Very few programs are inherently
parallelizable, although many programs have *parts* which can be
successfully parallelized.

I think that for many people, "the GIL" is just a bogeyman, or is being
blamed for their own shortcomings. To take an extreme case, if you're
running single-thread code on a single-core machine and still complaining
about the GIL, you have no clue.

(That's not *you personally* Paul, it's a generic "you".)

There are numerous alternatives for those who are genuinely running into
GIL-related issues. Jeff Knupp has a good summary:

http://www.jeffknupp.com/blog/2013/06/30/pythons-hardest-problem-revisited/

One alternative that he misses is that for some programs, the simplest way
to speed it up is to vectorize the core parts of your code by using numpy.
No threads needed.

For those who think that the GIL and the GIL alone is the problem, consider
that Jython is nearly as old as CPython, it goes back at least 15 years.
IronPython has been around for a long time too, and is possibly faster than
CPython even in single threaded code. Neither has a GIL. Both are mature
implementations, built on well-known, powerful platforms with oodles of
business credibility (the JVM and .Net). IronPython even has the backing of
Microsoft, it is one of the few non-Microsoft languages with a privileged
position in the .Net ecosystem.

Where are the people flocking to use Jython and IronPython?

In fairness, there are good reasons why some people cannot use Jython or
IronPython, or one of the other alternatives. But that demonstrates that
the problem is more complex than just "the GIL".

For removal of the GIL to really make a difference:

- you must have at least two cores (that, at least, applies to most people
these days);

- you must be performing a task which is parallelizable and not inherently
sequential (no point using multiple threads if each thread spends all its
time waiting for the previous thread);

- the task must be one that moving to some other multi-processing model
(such as greenlets, multiprocess, etc.) is infeasible;

- you must actually use multiple threads, and use them properly (no busy
wait loops);

- your threading bottleneck must be primarily CPU-bound, not I/O bound
(CPython's threads are already very effective at parallelising I/O tasks);

- and you must be using libraries and tools which prevent you moving to
Jython or IronPython or some other alternative.

I can't help but feel that the set of people for whom removal of the GIL
would actually help is much smaller than, and different to, the set of
people who complain about the GIL.



--
Steven

Paul Rubin

unread,
Feb 22, 2015, 9:04:36 PM2/22/15
to
Steven D'Aprano <steve+comp....@pearwood.info> writes:
> I'm sorry, but the instant somebody says "eliminate the GIL", they lose
> credibility with me. Yes yes, I know that in *your* specific case you've
> done your research and (1) multi-threaded code is the best solution for
> your application and (2) alternatives aren't suitable.

I don't see what the big deal is. I hear tons of horror stories about
threads and I believe them, but the thing is, they almost always revolve
around acquiring and releasing locks in the wrong order, forgetting to
lock things, stuff like that. So I've generally respected the terror
and avoided programming in that style, staying with a message passing
style that may take an efficiency hit but seems to avoid almost all
those problems. TM also helps with lock hazards and it's a beautiful
idea--I just haven't had to use it yet. The Python IRC channel seems to
rage against threads and promote Twisted for concurrency, but Twisted
has always reminded me of Java. I use threads in Python all the time
and haven't gotten bitten yet.

> Writing multithreaded code is *hard*. It is not a programming model
> which comes naturally to most human beings. Very few programs are
> inherently parallelizable, although many programs have *parts* which
> can be successfully parallelized.

Parallel algorithms are complicated and specialized but tons of problems
amount to "do the same thing with N different pieces of data", so-called
embarassingly parallel. The model is you have a bunch of worker threads
reading off a queue and processing the items concurrently. Sometimes
separate processes works equally well, other times it's good to have
some shared data in memory instead of communicating through sockets. If
the data is mutable then have one thread own it and access it only with
message passing, Erlang style. If it's immutable after initialization
(protect it with a condition variable til initialization finishes) then
you can have read-only access from anywhere.

> if you're running single-thread code on a single-core machine and
> still complaining about the GIL, you have no clue.

Even the Raspberry Pi has 4 cores now, and fancy smartphones have had
them for years. Single core cpu's are specialized and/or historical.

> for some programs, the simplest way to speed it up is to vectorize the
> core parts of your code by using numpy. No threads needed.

Nice for numerical codes, not so hot for anything else.

> Where are the people flocking to use Jython and IronPython?

Shrug, who knows, those implementations were pretty deficient from what
heard.

> For removal of the GIL to really make a difference: ...
> - you must be performing a task which is parallelizable and not inherently
> sequential (no point using multiple threads if each thread spends all its
> time waiting for the previous thread);

That's most things involving concurrency these days.

> - the task must be one that moving to some other multi-processing model
> (such as greenlets, multiprocess, etc.) is infeasible;

I don't understand this--there can be multiple ways to solve a problem.

> - your threading bottleneck must be primarily CPU-bound, not I/O bound
> (CPython's threads are already very effective at parallelising I/O tasks);

If your concurrent program's workload makes it cpu-bound even 1% of the
time, then you gain something by having it use your extra cores at those
moments, instead of having those cores always do nothing.

> - and you must be using libraries and tools which prevent you moving to
> Jython or IronPython or some other alternative.

I don't get this at all. Why should I not want Python to have the same
capabilities?

Chris Angelico

unread,
Feb 22, 2015, 9:16:39 PM2/22/15
to pytho...@python.org
On Mon, Feb 23, 2015 at 1:04 PM, Paul Rubin <no.e...@nospam.invalid> wrote:
>> if you're running single-thread code on a single-core machine and
>> still complaining about the GIL, you have no clue.
>
> Even the Raspberry Pi has 4 cores now, and fancy smartphones have had
> them for years. Single core cpu's are specialized and/or historical.

Or virtual. I have a quad-core + hyperthreading CPU, but most of my
VMs (in fact, all except the one that runs a Python buildbot, I
think!) are restricted to a single core. If you rent a basic cheap
machine from a cloud provider, you'll quite possibly be getting a
single core, too.

ChrisA

Paul Rubin

unread,
Feb 22, 2015, 9:45:16 PM2/22/15
to
Laura Creighton <l...@openend.se> writes:
> And given that Lennart is a friend, well really a good friend of my
> lover and a something-better- than-an-acquaintance with me ---- I
> should make the effort to get these two under the same roof (mine, by
> preference) for the fun of the experience.

Oh cool, right, I forgot that you are in Sweden where he is. I've never
met him but he's pretty visible online and yes, he sure knows a lot
about Haskell.

Ryan Stuart

unread,
Feb 22, 2015, 10:16:54 PM2/22/15
to Paul Rubin, pytho...@python.org
On Mon Feb 23 2015 at 12:05:46 PM Paul Rubin <no.e...@nospam.invalid> wrote:
I don't see what the big deal is.  I hear tons of horror stories about
threads and I believe them, but the thing is, they almost always revolve
around acquiring and releasing locks in the wrong order, forgetting to
lock things, stuff like that.  So I've generally respected the terror
and avoided programming in that style, staying with a message passing
style that may take an efficiency hit but seems to avoid almost all
those problems.  TM also helps with lock hazards and it's a beautiful
idea--I just haven't had to use it yet.  The Python IRC channel seems to
rage against threads and promote Twisted for concurrency, but Twisted
has always reminded me of Java.  I use threads in Python all the time
and haven't gotten bitten yet.


Many people have written at length about why it's bad. The most recent example I have come across is here -> https://glyph.twistedmatrix.com/2014/02/unyielding.html

It's not a specific Python problem. I must be in the limited crowd that believes that the GIL is a *feature* of Python. Then again, maybe it isn't so limited since the GIL-less python implementations haven't really taken off.

I have yet to come across a scenario I couldn't solve with either Processes, NumPy or event loops. Yes, when using processes, the passing of data can be annoying sometimes. But that is far less annoying then trying to debug something that shares state across threads.

It's great that you haven't been bitten yet. But, the evidence seems to suggest the either you *will* be bitten at some point or, you already have been, and you just don't know it.

Cheers
 

Chris Angelico

unread,
Feb 22, 2015, 10:26:02 PM2/22/15
to pytho...@python.org
On Mon, Feb 23, 2015 at 2:16 PM, Ryan Stuart <ryan.st...@gmail.com> wrote:
> Many people have written at length about why it's bad. The most recent
> example I have come across is here ->
> https://glyph.twistedmatrix.com/2014/02/unyielding.html
>
> It's not a specific Python problem. I must be in the limited crowd that
> believes that the GIL is a *feature* of Python. Then again, maybe it isn't
> so limited since the GIL-less python implementations haven't really taken
> off.

The GIL isn't a problem, per se. It's a solution to an underlying
problem (concurrent access to internal data structures) which comes
with its own tradeoffs. Every method of eliminating the GIL is really
an alternate solution to the same underlying problem, with its own
tradeoffs. The GIL has simplicity on its side.

ChrisA

Paul Rubin

unread,
Feb 22, 2015, 10:46:19 PM2/22/15
to
Ryan Stuart <ryan.st...@gmail.com> writes:
> Many people have written at length about why it's bad. The most recent
> example I have come across is here ->
> https://glyph.twistedmatrix.com/2014/02/unyielding.html

That article is about the hazards of mutable state shared between
threads. The key to using threads safely is to not do that. So the
"transfer" example in the article would instead be a message handler in
the thread holding the account data, and it would do the transfer in the
usual sequential way. You'd start a transfer by sending a message
through a Queue, and get back a reply through another queue.

In Erlang that style is enforced: it has basically no such thing as data
mutation or sharing. In Python it's straightforward to write in that
style; it's just that the language doesn't stop you from departing from
it, sort of like a language with weak type-checking. You can survive it
if the code complexity isn't too bad. In most programs I've dealt with,
the number of distinct handlers is not all that large (there might be
1000 threads in a high-concurrency network server, but they're all doing
about the same thing). So it hasn't been that hard to "color inside the
lines" with a bit of thoughtfulness and code inspection.

You might like this:

http://jlouisramblings.blogspot.com/2012/08/getting-25-megalines-of-code-to-behave.html

Ryan Stuart

unread,
Feb 22, 2015, 11:00:36 PM2/22/15
to pytho...@python.org
On Mon Feb 23 2015 at 1:50:40 PM Paul Rubin <no.e...@nospam.invalid> wrote:
That article is about the hazards of mutable state shared between
threads.  The key to using threads safely is to not do that.  So the
"transfer" example in the article would instead be a message handler in
the thread holding the account data, and it would do the transfer in the
usual sequential way.  You'd start a transfer by sending a message
through a Queue, and get back a reply through another queue.

I think that is a pretty accurate summary. In fact, the article even says that. So, just to iterate its point, if you are using non-blocking Queues to communicate to these threads, then you just have a communicating event loop. Given that Queues work perfectly with with processes as well, what is the point of using a thread? Using a process/fork is far safer in that someone can't "accidentally" decide to alter mutable state in the future.
Thanks for this, I'll take a look.

Cheers
 

--
https://mail.python.org/mailman/listinfo/python-list

Paul Rubin

unread,
Feb 23, 2015, 1:14:00 AM2/23/15
to
Ryan Stuart <ryan.st...@gmail.com> writes:
> I think that is a pretty accurate summary. In fact, the article even
> says that. So, just to iterate its point, if you are using
> non-blocking Queues to communicate to these threads, then you just
> have a communicating event loop. Given that Queues work perfectly with
> with processes as well, what is the point of using a thread?

What do you mean about Queues working with processes? I meant
Queue.Queue. There is multiprocessing.Queue but that's much less
capable, and it uses cumbersome IPC like pipes or sockets instead of a
lighter weight lock. Threads can also share read-only data and you can
pass arbitrary objects (such as code callables that you want the other
thread to execute--this is quite useful) through Queue.Queue. I don't
think you can do that with the multiprocessing module.

Terry Reedy

unread,
Feb 23, 2015, 1:35:08 AM2/23/15
to pytho...@python.org
--
Terry Jan Reedy

Ryan Stuart

unread,
Feb 23, 2015, 2:32:25 AM2/23/15
to pytho...@python.org
On Mon Feb 23 2015 at 4:15:42 PM Paul Rubin <no.e...@nospam.invalid> wrote:
What do you mean about Queues working with processes?  I meant
Queue.Queue.  There is multiprocessing.Queue but that's much less
capable, and it uses cumbersome IPC like pipes or sockets instead of a
lighter weight lock.  Threads can also share read-only data and you can
pass arbitrary objects (such as code callables that you want the other
thread to execute--this is quite useful) through Queue.Queue.  I don't
think you can do that with the multiprocessing module.

These things might be convenient but they are error prone for the reasons pointed out. Also, the majority can be achieved via the process approach. For example, using fork to take a copy of the current process (including the heap) you want to use will give you access to any callables on the heap. 

The vital point here is that fork takes a *copy* of the process and runs in a *separate* memory space. This means there can be no accidents here. If it were to run in the same memory space like a thread then bugs anywhere in the code you run could cause very nasty problems. This includes not only bugs in your code, but also bugs in any other library code. Not only are they nasty, they could be close to invisible.

And when I saw any code you run, I literally mean any. Even if you are extra careful to not touch any shared state in your code, you can almost be guaranteed that code higher up the stack, like malloc for example, *will* be using shared state.

The risk of unintended and difficult to track issues when using threads is very high because of shared state. Even if you aren't sharing state in your code directly, code higher up the stack will be sharing state. That is the whole point of a thread, that's what they were invented for. Using threads safely might well be impossible much less verifiable. So when there are other options that are just as viable/functional, result in far less risk and are often much quicker to implement correctly, why wouldn't you use them? If it were easy to use threads in a verifiably safe manner, then there probably wouldn't be a GIL.

Cheers
 
--
https://mail.python.org/mailman/listinfo/python-list

Steven D'Aprano

unread,
Feb 23, 2015, 2:41:40 AM2/23/15
to
Paul Rubin wrote:

> Steven D'Aprano <steve+comp....@pearwood.info> writes:
>> I'm sorry, but the instant somebody says "eliminate the GIL", they lose
>> credibility with me. Yes yes, I know that in *your* specific case you've
>> done your research and (1) multi-threaded code is the best solution for
>> your application and (2) alternatives aren't suitable.
>
> I don't see what the big deal is. I hear tons of horror stories about
> threads and I believe them, but the thing is, they almost always revolve
> around acquiring and releasing locks in the wrong order, forgetting to
> lock things, stuff like that.

Deadlocks, livelocks, and the simple fact that debugging threaded code is
much, much, much harder than debugging single-thread code.

You'll notice that nowhere in my post did I say "Don't use threads!". But
they aren't a panacea, and there are other concurrency models available
which don't have the same disadvantages as threads (they have their own pros
and cons instead).


[...]
>> For removal of the GIL to really make a difference: ...
[...]
>> - the task must be one that moving to some other multi-processing model
>> (such as greenlets, multiprocess, etc.) is infeasible;
>
> I don't understand this--there can be multiple ways to solve a problem.

Yes, but my point is that if some other way solves the problem, then you
should *use that other technique* rather than complain about the GIL. The
GIL is not a bottleneck if you can bypass it.

I can sympathize with somebody who says "I have this problem that can only
be solved with threads, anything else is too difficult, but the GIL makes it
too slow".

But I don't sympathize with somebody who says "I have this problem that can
be solved with threads or multiprocessing, but I insist that only threads
can be used. And now the GIL makes it too slow. Python sux."

No. You [generic you] sux, for not investigating whether multiprocessing
will do the job. It even has the same public API as threads.


>> - your threading bottleneck must be primarily CPU-bound, not I/O bound
>> (CPython's threads are already very effective at parallelising I/O
>> tasks);
>
> If your concurrent program's workload makes it cpu-bound even 1% of the
> time, then you gain something by having it use your extra cores at those
> moments, instead of having those cores always do nothing.

Not necessarily. Threading has overhead too. Even in GIL-less Python, two
threads aren't twice as fast as one thread. So there comes a point of
diminishing returns, where the benefit you get from threading is less than
the cost of threading. Now your code will actually run slower with threads,
GIL or no GIL.


>> - and you must be using libraries and tools which prevent you moving to
>> Jython or IronPython or some other alternative.
>
> I don't get this at all. Why should I not want Python to have the same
> capabilities?

Python does have the same capabilities. Jython and IronPython aren't
different languages, they are Python.

If you want *CPython* to work without a GIL, well, are you volunteering to
do the work? It is a massive job, and the core devs aren't terribly
interested. Probably because they understand that the GIL is not often an
unsurmountable problem in real life. That brings us to the point I was
making: I believe, and the core devs appear to think the same, that the
*actual* number of people who would benefit from CPython losing the GIL is
not large enough to justify the work it would require.


--
Steve

Marko Rauhamaa

unread,
Feb 23, 2015, 3:16:12 AM2/23/15
to
Steven D'Aprano <steve+comp....@pearwood.info>:

> Yes, but my point is that if some other way solves the problem, then
> you should *use that other technique* rather than complain about the
> GIL. The GIL is not a bottleneck if you can bypass it.
>
> I can sympathize with somebody who says "I have this problem that can
> only be solved with threads, anything else is too difficult, but the
> GIL makes it too slow".
>
> But I don't sympathize with somebody who says "I have this problem
> that can be solved with threads or multiprocessing, but I insist that
> only threads can be used. And now the GIL makes it too slow. Python
> sux."

I'm as "antithread" as anybody, but I think you are blaming the victim
here. I do sympathize with a person who wants to use threads and finds
out there is no true parallelism.

GIL is a downside you have to accept when using a language like Python
that has to "hold the world still" while it's manipulating its data
structures in a safe way. Because of the dynamism involved, effective
parallelism is difficult to achieve.

I must say I have never been bitten by this problem because I only use
threads under duress (occasionally when dealing with blocking APIs) and
because I have very modest performance requirements for Python. For
anything that needs to be fast, I use other programming languages.
Currently, my work is maybe 45% bash, 45% Python and 10% C/C++ (and even
in my C code, I generally steer clear of threads).


Marko

Chris Angelico

unread,
Feb 23, 2015, 4:19:59 AM2/23/15
to pytho...@python.org
On Mon, Feb 23, 2015 at 6:41 PM, Steven D'Aprano
<steve+comp....@pearwood.info> wrote:
>>> - and you must be using libraries and tools which prevent you moving to
>>> Jython or IronPython or some other alternative.
>>
>> I don't get this at all. Why should I not want Python to have the same
>> capabilities?
>
> Python does have the same capabilities. Jython and IronPython aren't
> different languages, they are Python.

Presuming that he meant CPython, one other good reason for wanting to
stick with it rather than jump to another interpreter is all the new
features of the recent versions. (Do Jython or IronPython support
Python 3.x at all, even?) Every new release of CPython has new and
cool features, and the other Pythons are generally playing catch-up.

ChrisA

Dave Cook

unread,
Feb 23, 2015, 6:37:13 AM2/23/15
to
On 2015-02-22, Dave Farrance <DaveFa...@OMiTTHiSyahooANDTHiS.co.uk> wrote:

> It's still quicker to do a re-write in the more cumbersome C

You should try Cython.

Dave

Dave Farrance

unread,
Feb 23, 2015, 9:05:13 AM2/23/15
to
Laura Creighton <l...@openend.se> wrote:

>Good news -- it seems to be working fine with PyPy.
>https://travis-ci.org/hugovk/Pillow/builds
>
>for me, not extensively tested, it just seems to be working.
>
>I have several pypy's floating around here, each within its own
>virtualenv. If you aren't familiar with virtualenv, read all
>about it here:
>http://www.dabapps.com/blog/introduction-to-pip-and-virtualenv-python/

OK, thanks. I had to do a bit of experimenting to find something that
worked. The "virtualenv" that came with Ubuntu failed in various ways if
used with PyPy. But after downloading this and that via the PyPy download
webpage, I found that "Squeaky's portable Linux binaries" contained
'virtualenv-pypy' which worked OK, then Pillow installed OK with "pip".

It does seem that the PyPy that you get from the Ubuntu repository (and
presumably as packaged in most Linux distros) is rather limited. Other
than the core libraries included with the PyPy package itself, all that's
in the repository for PyPy is Tk and an ImageMagick interface and that's
it. CPython, on the other hand, has more repository packages than I can
count -- hundreds, I think. So as a non-developer linux-user, I had just
installed Python packages from the repository, rather than using pip, as
would most linux users I would guess. So that's why I got the impression
that PyPy had almost no library support.

Dave Farrance

unread,
Feb 23, 2015, 9:13:29 AM2/23/15
to
I did try Cython when I was trying to figure out what to do about the slow
speed. My initial attempt showed no speedup at all. The documentation
told me that I needed to change the data types to special C-like types, so
it seemed to me that it would become half way between Python and C and
would be as cumbersome to develop as C. So at that point, I just rewrote
it in C.

Anyway, now that I've been told how to install libraries in PyPy, I'll
probably be able to use PyPy if I need speedups in future.

Stefan Behnel

unread,
Feb 23, 2015, 10:44:04 AM2/23/15
to pytho...@python.org
Dave Farrance schrieb am 23.02.2015 um 15:13:
> Dave Cook wrote:
>> On 2015-02-22, Dave Farrance wrote:
>>
>>> It's still quicker to do a re-write in the more cumbersome C
>>
>> You should try Cython.
>
> I did try Cython when I was trying to figure out what to do about the slow
> speed. My initial attempt showed no speedup at all. The documentation
> told me that I needed to change the data types to special C-like types, so
> it seemed to me that it would become half way between Python and C and
> would be as cumbersome to develop as C. So at that point, I just rewrote
> it in C.

The main selling point of Cython is that, while it gives you the speed of C
if you write C-ish code (because it translates it to the obvious C code),
you don't have to write that C-ish code unless you decide to do so. Right
the next line, you can use a set comprehension or yield a value back from a
generator. So, it's not "half way between Python and C", it actually covers
both, almost entirely. (Oh, and also C++, if you feel like it.)

Stefan


Laura Creighton

unread,
Feb 23, 2015, 11:17:12 AM2/23/15
to Dave Farrance, pytho...@python.org, l...@openend.se
Arrgh! I forgot to warn you that you need a very recent version of
virtualenv to work with PyPy. I am very sorry about that. Glad to
see that things are working now.

Laura

Ethan Furman

unread,
Feb 23, 2015, 2:40:12 PM2/23/15
to pytho...@python.org
On 02/22/2015 11:41 PM, Steven D'Aprano wrote:

> If you want *CPython* to work without a GIL, well, are you volunteering to
> do the work? It is a massive job, and the core devs aren't terribly
> interested. Probably because they understand that the GIL is not often an
> unsurmountable problem in real life. That brings us to the point I was
> making: I believe, and the core devs appear to think the same, that the
> *actual* number of people who would benefit from CPython losing the GIL is
> not large enough to justify the work it would require.

If memory serves, the first and primary point to losing the GIL is that single-threaded code must run just as fast (or
at least close to just as fast) as the GIL version, and previous attempts have all badly failed that requisite.

--
~Ethan~

signature.asc

Dave Cook

unread,
Feb 23, 2015, 6:23:11 PM2/23/15
to
On 2015-02-23, Dave Farrance <DaveFa...@OMiTTHiSyahooANDTHiS.co.uk> wrote:

> I did try Cython when I was trying to figure out what to do about the slow
> speed. My initial attempt showed no speedup at all. The documentation
> told me that I needed to change the data types to special C-like types, so
> it seemed to me that it would become half way between Python and C and
> would be as cumbersome to develop as C. So at that point, I just rewrote
> it in C.

Right, you have add type info to get the speedup. This can be done as
needed. What Cython provides is a transparent interface between the C
compiler and Python, without the need for any wrapper code. I'm
curious about the process you use to interface to C that is less
cumbersome.

Dave Cook

Paul Rubin

unread,
Feb 23, 2015, 7:11:53 PM2/23/15
to
Ryan Stuart <ryan.st...@gmail.com> writes:
> Threads can also share read-only data and you can pass arbitrary
> objects (such as code callables that you want the other thread to
> execute--this is quite useful) through Queue.Queue. I don't think
> you can do that with the multiprocessing module.
>
> These things might be convenient but they are error prone for the
> reasons pointed out.

I don't see the error-proneness since nothing there seems to set off
mutation of shared data.

> Also, the majority can be achieved via the process approach. For
> example, using fork to take a copy of the current process (including
> the heap) you want to use will give you access to any callables on the
> heap.

What if you want to dynamically construct a callable and send it to
another process?

> Even if you are extra careful to not touch any shared state in your
> code, you can almost be guaranteed that code higher up the stack, like
> malloc for example, *will* be using shared state.

This isn't the 1980's any more--any serious malloc implementation these
days is thread safe. People write multi-threaded C programs all the
time and those programs use malloc in more than one thread.

> Even if you aren't sharing state in your code directly, code higher up
> the stack will be sharing state. That is the whole point of a thread,
> that's what they were invented for. Using threads safely might well
> be impossible much less verifiable.

You're basically saying it's impossible to write a reliable operating
system, since OS's by nature have to do that stuff. Of course there are
verified OS's, and some of the early pioneers in concurrency were the
same guys who worked in program verification, e.g. Dijkstra's
semaphores. Even Erlang uses data sharing under the hood (ETS tables
and large binaries) though their API makes it look like the data is
copied between processes.

What I'd say is that multi-threaded programs tend to have miniature OS's
inside them, so it helps to have had some exposure to OS implementation
techniques if you're going to write this kind of code. But if you've
had that exposure then it all becomes less scary.

> So when there are other options that are just as viable/functional,
> result in far less risk and are often much quicker to implement
> correctly, why wouldn't you use them?

I should give the multiprocessing module a try sometime (haven't used it
so far because it's relatively new and I'm comfortable with threads).
It has the disadvantages that I noted, though.

> If it were easy to use threads in a verifiably safe manner, then there
> probably wouldn't be a GIL.

Nah, the GIL is just a CPython artifact. As Steven says, IronPython and
Jython don't have GIL's. Java has no GIL, OCaml has no GIL, GHC has no
GIL, etc. Someone made a CPython version with no GIL some years ago and
it worked fine and it got a speedup on multiple cores. The only problem
was that on a single core, it was significantly slower than regular
CPython, specifically because of the overhead of having to lock all the
refcount updates, so it was considered a failure. Laura Creighton may
have more to say about this, but I've been under the impression that the
main obstacle to getting rid of the CPython GIL is the refcount system
(which is also easy to make mistakes with, by the way). That's why I
was surprised to hear that PyPy has a GIL.

Michael Torrie

unread,
Feb 23, 2015, 7:25:16 PM2/23/15
to pytho...@python.org
On 02/22/2015 08:30 AM, Dave Farrance wrote:
> I started work 1980ish, had an early 6502-based home computer, and my then
> place of work had some 6502-based Pet computers, so I gained the ability
> to quickly write BASIC programs as an engineering aid. Later, when BASIC
> dropped into obscurity, I switched to C and C++, although I always found
> that cumbersome compared to the old BASIC. Later still, when I found that
> my Google queries for code examples started returning more Python than C,
> I tried that -- and discovered that Python was like BASIC, only better.

I know BASIC is a hiss and a byword for many folks, and certainly it's
off topic here. But modern BASIC, particarly in the form of the
FreeBASIC[1] compiler, is very much alive and well on all modern
platforms (including Windows, Mac, Linux, and Arm devices). BASIC today
appears to be every bit as structured as any modern language.

It's relatively easy to translate C header files into FreeBASIC include
files, but it is tedious. I would love to have a FreeBASIC translation
of the libpython header files and then I could write Python extensions
in FreeBASIC.

Back on topic, have you tried using Cython to compile python modules
that need a speedup into a binary module?

[1] http://www.freebasic.net/

Chris Angelico

unread,
Feb 23, 2015, 7:32:49 PM2/23/15
to pytho...@python.org
On Tue, Feb 24, 2015 at 11:11 AM, Paul Rubin <no.e...@nospam.invalid> wrote:
> What if you want to dynamically construct a callable and send it to
> another process?

I'm not sure what that would actually mean. Do you try to construct it
out of code that already exists in the other process? Are you passing
actual code to the other process? Does the callable, when called,
actually execute in the calling process? And what about its context -
its globals, and possibly nonlocals (if it's a closure)?

ChrisA

Ryan Stuart

unread,
Feb 23, 2015, 7:36:06 PM2/23/15
to pytho...@python.org
On Tue Feb 24 2015 at 10:15:40 AM Paul Rubin <no.e...@nospam.invalid> wrote:
I don't see the error-proneness since nothing there seems to set off
mutation of shared data.

I'm not sure what else to say really. It's just a fact of life that Threads by definition run in the same memory space and hence always have the possibility of nasty unforeseen problems. They are unforeseen because it is extremely difficult (maybe impossible?) to try and map out and understand all the different possible mutations to state. Sure, your code might not be making any mutations (that you know of), but malloc definitely is [1], and that's just the tip of the iceberg. Other things like buffers for stdin and stdout, DNS resolution etc. all have the same issue. 

I have no doubt someone can come up with a scenario where they need to use threads. I can't come up with one myself, but maybe someone else can. But in the work I have done, processes have sufficed - even for the example of dynamic callables you gave.

To borrow from the original article I linked - "Nevertheless I still think it’s a bad idea to make things harder for ourselves if we can avoid it."

Cheers

 

Paul Rubin

unread,
Feb 23, 2015, 8:47:51 PM2/23/15
to
Steven D'Aprano <steve+comp....@pearwood.info> writes:
> Deadlocks, livelocks, and the simple fact that debugging threaded code is
> much, much, much harder than debugging single-thread code.

Deadlocks and livelocks can happen in multi-process code just like with
threads. Debugging threaded code hasn't been too bad a problem for me
so far. The basic tactic is use the logging module a lot (it is thread
safe), log the i/o events coming into the system if you observe
misbehaviour, and play the log back through your test harness to
reproduce the problem and debug it by normal means. You have to do
something like that anyway, if the indeterminacy in the system comes
from the unpredictable ordering of external i/o events. IMHO you'd get
basicaly similar bugs with other concurrency mechanisms.

Other programs like Postgres are written in dangerous languages like C
and use millions of LOC and vast numbers of fine-grained locks and are
still considered very reliable. Python threads communicating through
queues has to be a breeze by comparison. Compared to what Linux kernel
hackers have to deal with, it's not even on the same planet. This book
is amazing:

https://www.kernel.org/pub/linux/kernel/people/paulmck/perfbook/perfbook.html

> No. You [generic you] sux, for not investigating whether multiprocessing
> will do the job. It even has the same public API as threads.

I will give multiprocessing a try next time the matter comes up (it
hasn't always been available) but it doesn't seem as flexible. E.g. say
I have an employee handling thread (untested pseudocode, forgive
errors):

def employee_loop(queue):
while True:
func, args = queue.get()
func(*args)

def adjust_salary(person, update_func):
salaries[person] = update_func(salaries[person])

threading.Thread(target=employee_loop,
args=[employee_request_queue]).start()

Now in another thread, I want to give you a 50% raise:

employee_request_queue.put(
(adjust_salary, ('Steven',
lambda old_salary: old_salary * 1.5)))

Can I do that with multiprocessing without a bunch more boilerplate?

> Even in GIL-less Python, two threads aren't twice as fast as one
> thread. So there comes a point of diminishing returns

Well if two threads are 1.5x as fast as one thread, that's a win.

> Python does have the same capabilities. Jython and IronPython aren't
> different languages, they are Python.

Oh ok. I think of Jython as a Java thing and IronPython as a Windows
thing and I don't want to deal with the monstrous JVM or CLR systems.
So I unconsciously didn't think of them as Python. I do think of PyPy
as Python.

> If you want *CPython* to work without a GIL, well, are you
> volunteering to do the work? It is a massive job, and the core devs
> aren't terribly interested.

It's not a massive job, it's been done and it worked, the problem is
that locking all the CPython refcount updates slowed down the important
single-cpu case so it was never adopted. So the real massive job would
be moving to a tracing GC and changing every extension module ever
written. But, that's what PyPy is (among other things) so I'm waiting
to hear from Laura why PyPy has a GIL.

Paul Rubin

unread,
Feb 23, 2015, 8:50:36 PM2/23/15
to
Chris Angelico <ros...@gmail.com> writes:
>> What if you want to dynamically construct a callable and send it to
>> another process?
> I'm not sure what that would actually mean. Do you try to construct it
> out of code that already exists in the other process? Are you passing
> actual code to the other process?

I gave an example in a reply to Steven, something like

other_thread_queue.put(lambda x: x*x)

to tell the other thread it is supposed to square something. It
receives a callable and calls it in its own context.

Chris Angelico

unread,
Feb 23, 2015, 9:03:24 PM2/23/15
to pytho...@python.org
So, you would have to pass code to the other process, probably. What about this:

y = 4
other_thread_queue.put(lambda x: x*y)

Or this:

y = [4]
def next_y():
y[0] += 1
return y[0]
other_thread_queue.put(next_y)

It may not be obvious with your squaring example, but every Python
function has its context (module globals, etc). You can't pass a
function around without also passing, or sharing, its data.

With threads in a single process, this isn't a problem. They all
access the same memory space, so they can all share state. As soon as
you go to separate processes, these considerations become serious.

ChrisA

Steven D'Aprano

unread,
Feb 23, 2015, 9:16:00 PM2/23/15
to
That was then. Now even smartphones have multiple cores. It might be time to
reconsider. If not now, then surely by the time entry-level machines have 8
cores, the requirement that 1-core machines aren't penalized will surely be
dropped :-)

It may turn out that removing the GIL doesn't improve things *enough*, who
knows what will happen. I recall from the first attempt, back in Python 1.5
days, that there was a 40% slowdown for single core machines, a very slight
speedup for two-core, and increasing the number of cores beyond four made no
real difference. So it might turn out that "removing the GIL" is great in
theory and not so useful in practice, but I guess only time will tell.



--
Steve

Paul Rubin

unread,
Feb 23, 2015, 11:40:57 PM2/23/15
to
Chris Angelico <ros...@gmail.com> writes:

> So, you would have to pass code to the other process, probably. What
> about this:
> y = 4
> other_thread_queue.put(lambda x: x*y)

the y in the lambda is a free variable that's a reference to the
surrounding mutable context, so that's at best dubious. You could use:

other_thread_queue.put(lambda x, y=y: x*y)

> Or this:
>
> y = [4]
> def next_y():
> y[0] += 1
> return y[0]
> other_thread_queue.put(next_y)

There you have shared mutable data, which isn't allowed in this style.

> It may not be obvious with your squaring example, but every Python
> function has its context (module globals, etc). You can't pass a
> function around without also passing, or sharing, its data.

That is ok as long as the data can't change.

> With threads in a single process, this isn't a problem. They all
> access the same memory space, so they can all share state. As soon as
> you go to separate processes, these considerations become serious.

Right, that's a limitation of processes compared to threads.

Paul Rubin

unread,
Feb 24, 2015, 12:27:58 AM2/24/15
to
Ryan Stuart <ryan.st...@gmail.com> writes:
> I'm not sure what else to say really. It's just a fact of life that
> Threads by definition run in the same memory space and hence always
> have the possibility of nasty unforeseen problems. They are unforeseen
> because it is extremely difficult (maybe impossible?) to try and map
> out and understand all the different possible mutations to state.

Sure, the shared memory introduces the possibility of some bad errors,
I'm just saying that I've found that by staying with a certain
straightforward style, it doesn't seem difficult in practice to avoid
those errors.

> Sure, your code might not be making any mutations (that you know of),
> but malloc definitely is [1], and that's just the tip of the iceberg.
> Other things like buffers for stdin and stdout, DNS resolution etc.
> all have the same issue.

I don't understand what you mean about malloc. I looked at that code
and there's a mutex to make multi-threaded programs work right, and an
ifdef (maybe for better performance) to use different code if there are
no threads. IOW they spent a bunch of time handling threads. Are you
saying there's a bug? Re stdin/stdout: obviously you can't have
multiple threads messing with the same fd's; that's the same thing as
data sharing. Re DNS: if gethostbyname isn't thread-safe I'd think of
that as a pretty bad bug. But I'm having a vague memory of having had
an issue with this though, and IIRC it took part of a morning to figure
out what was going on, annoying but not a multi-month bug-hunt or
anything like that. It didn't happen on my workstation, but only on the
embedded target that was probably running an old or weird libc.

> To borrow from the original article I linked - "Nevertheless I still
> think it’s a bad idea to make things harder for ourselves if we can
> avoid it."

That article was interesting in some ways but confused in others. One
way it was interesting is it said various non-thread approaches (such as
coroutines) had about the same problems as threads. Some ways it was
confused were:

1) thinking Haskell threads were like processes with separate address
spaces. In fact they are in the same address space and programming
with them isn't all that different from Python threads, though the
synchronization primitives are a bit different. There is also an STM
library available that is ingenious though apparently somewhat slow.

2) it has a weird story about the brass cockroach, that basically
signified that they didn't have a robust enough testing system to be
able to reproduce the bug. That is what they should have worked on.

3) It goes into various hazards of the balance transfer example not
mentioning that STM (available in Haskell and Clojure) completely
solves it.

4) It says: "eventually a system which communicates exclusively through
non-blocking queues effectively becomes a set of communicating event
loops, and its problems revert to those of an event-driven system;
it doesn’t look like regular programming with threads any more."

That is essentially what an Erlang program is, and it misses the fact
that those low-level event loops can use blocking operations to their
heart's content, without the inversion of control (callback spaghetti)
of traditional evented systems (I haven't used asyncio yet). Also,
the low-level loops can run in parallel on multiple cores, while a
asyncio-style coroutine loop is sequential under the skin.

In Erlang/OTP, you don't even see the event loops directly, since they
are abstracted away by the OTP framework and it looks like RPC calls
at the application level. But, it helps to know what is going on
underneath.

I'm realizing some people program Python in an ultra-dynamic style where
the mutability of modules, functions, etc. really comes into play, so
that make threads much more dangerous. I've tended to write Python with
much less dynamism or even as if it were statically typed, so maybe that
helps.

Anyway, I got one thing out of this, which is that the multiprocessing
module looks pretty nice and I should try it even when I don't need
multicore parallelism, so thanks for that.

In reality though, Python is still my most productive language for
throwaway, non-concurrent scripting, but for more complicated concurrent
programs, alternatives like Haskell, Erlang, and Go all have significant
attractions.

Chris Angelico

unread,
Feb 24, 2015, 12:57:17 AM2/24/15
to pytho...@python.org
On Tue, Feb 24, 2015 at 4:27 PM, Paul Rubin <no.e...@nospam.invalid> wrote:
>> Sure, your code might not be making any mutations (that you know of),
>> but malloc definitely is [1], and that's just the tip of the iceberg.
>> Other things like buffers for stdin and stdout, DNS resolution etc.
>> all have the same issue.
>
> Re stdin/stdout: obviously you can't have
> multiple threads messing with the same fd's; that's the same thing as
> data sharing.

Actually, you can quite happily have multiple threads messing with the
underlying file descriptors, that's not a problem. (Though you will
tend to get interleaved output. But if you always produce output in
single blocks of text that each contain one line with a trailing
newline, you should see interleaved lines that are each individually
correct. I'm also not sure of any sane way to multiplex stdin -
merging output from multiple threads is fine, but dividing input
between multiple threads is messy.) The problem is *buffers* for stdin
and stdout, where you have to be absolutely sure that you're not
trampling all over another thread's data structures. If you unbuffer
your output, it's probably going to be thread-safe.

ChrisA

Paul Rubin

unread,
Feb 24, 2015, 1:24:00 AM2/24/15
to
Chris Angelico <ros...@gmail.com> writes:
> Actually, you can quite happily have multiple threads messing with the
> underlying file descriptors,... The problem is *buffers* for stdin
> and stdout, where you have to be absolutely sure that you're not
> trampling all over another thread's data structures.

Oh ok, sure, yeah, the distinction is valid. I guess the classic
interleaved "print 'a'" "print 'b'" loops could crash if the stdio
structures get corrupted through conflicting updates somehow.

Steven D'Aprano

unread,
Feb 24, 2015, 1:56:19 AM2/24/15
to
Most people are not using the bleeding edge version of Python, and even
those who do, aren't usually using it in production. There are still plenty
of people using Python 2.3 in production, and even a few using 1.5.

But as you say, there are good reasons for wishing to stick to CPython over
Jython, IronPython, PyPy or Stackless, let alone less mature or experimental
implementations. I get that.

But the point I am trying to make is that there are an awful lot of people
who mindlessly bash "Python" the language for "the GIL". The argument is
even worse than "Python sux cos significant whitespace", because the
whitespace issue at least is mostly a matter for personal preference, but
the GIL appears to be a technical, objective judgement. Trouble is that it
is *wrong*. Python the language doesn't have a GIL. Implementations with a
GIL aren't slower because of it, they are faster. And removing the GIL
doesn't necessarily speed up multithreaded code. So they are technically
wrong, and in practical terms often wrong too.

I am *happy* when people come up with nuanced and considered opinions that
they found that Python was unsuitable for some specific project, and can
give good cogent reasons for it. If they can demonstrate reasons for
believing that CPython would have been suitable *if the GIL was removed*,
that's great. Given sufficiently many such projects, and 1- and 2-core CPUs
gradually becoming obsolete, the time may come for somebody else to take a
shot at removing the GIL from CPython again. That time might even be now, if
there is a volunteer.

And C coders out there want a nice meaty project to work on during rainy
weekends?



--
Steve

Steven D'Aprano

unread,
Feb 24, 2015, 1:57:31 AM2/24/15
to
Paul Rubin wrote:

>> With threads in a single process, this isn't a problem. They all
>> access the same memory space, so they can all share state. As soon as
>> you go to separate processes, these considerations become serious.
>
> Right, that's a limitation of processes compared to threads.
>

I think the point is that it's not a *limitation* of processes, but a
*feature* of processes that they don't share state. (Well, I think there are
explicit ways to have shared memory, but that's another story.)

An interesting point of view: threading is harmful because it removes
determinism from your program.

http://radar.oreilly.com/2007/01/threads-considered-harmful.html

As I once wrote:

A programmer had a problem, and thought Now he has "I know, I'll solve
two it with threads!" problems.

http://code.activestate.com/lists/python-list/634273/


Some discussion of the pros and cons of threading:

http://c2.com/cgi/wiki?ThreadsConsideredHarmful




--
Steven

Chris Angelico

unread,
Feb 24, 2015, 2:17:23 AM2/24/15
to pytho...@python.org
On Tue, Feb 24, 2015 at 5:56 PM, Steven D'Aprano
<steve+comp....@pearwood.info> wrote:
> Most people are not using the bleeding edge version of Python, and even
> those who do, aren't usually using it in production. There are still plenty
> of people using Python 2.3 in production, and even a few using 1.5.
>
> But as you say, there are good reasons for wishing to stick to CPython over
> Jython, IronPython, PyPy or Stackless, let alone less mature or experimental
> implementations. I get that.

Yes, not everyone is running Python 3.5 in production, I totally get
that :) But if I'm writing an app to run on Debian or Ubuntu, I can
depend on there being a CPython 3.x available, even on the oldest
currently-supported releases (Ubuntu Lucid and Debian Squeeze both
ship Python 3.1), and it won't be long before saying "requires Python
3.3" won't be a problem. Plus, you can always compile CPython from
source on any Unix-like system, or download the .msi for Windows, and
run 3.4 with no problems. But if you want to use PyPy, Jython, or any
other implementation of the language, you're quite possibly stuck on
2.x, or if 3.x support does exist, it's the new and experimental code.
Even a willingness to compile from source won't get you past that, so
using any of the new features of Python 3.x is likely to cut out some
of the alternate interpreters.

It's fine to use Python 2.3 if that's all you need (and if you don't
need any bug fixes from python.org, which presumably means you have
security support from someone else). But it's also fine to want to use
a newer Python. It'd be nice if all the major Pythons supported 3.4,
but the reality of volunteer time is that CPython is pretty much
always going to be in the lead.

ChrisA

wxjm...@gmail.com

unread,
Feb 24, 2015, 2:58:00 AM2/24/15
to
Blah blah blah...

pypy fails as soon as one lives the ascii world.
(Not exactly for the same reasons as in CPy 3.3+.)

Regards.
jmf

Marko Rauhamaa

unread,
Feb 24, 2015, 3:08:30 AM2/24/15
to
Chris Angelico <ros...@gmail.com>:

> Actually, you can quite happily have multiple threads messing with the
> underlying file descriptors, that's not a problem. (Though you will
> tend to get interleaved output. But if you always produce output in
> single blocks of text that each contain one line with a trailing
> newline, you should see interleaved lines that are each individually
> correct. I'm also not sure of any sane way to multiplex stdin -
> merging output from multiple threads is fine, but dividing input
> between multiple threads is messy.) The problem is *buffers* for stdin
> and stdout, where you have to be absolutely sure that you're not
> trampling all over another thread's data structures. If you unbuffer
> your output, it's probably going to be thread-safe.

Here's an anecdote describing one real-life threading problem. We had a
largish multithreading framework (in Java, but I'm setting it in Python
and in a much simplified form).

We were mindful of deadlocks caused by lock reversal so we had come up
with a policy whereby objects form a layered hierarchy. An object higher
up in the hierarchy was allowed to call methods of objects below while
holding locks. The opposite was not allowed; if an object desired to
call a method of an object above it (through a registered callback), it
had to relinquish all locks before doing so.

However, a situation like this arose:

class App:
def send_stream(self, sock):
with self.lock:
self.register_socket(sock)

class SocketWrapper:
def read(_, count):
return sock.recv(count)
def close(_):
sock.close()
with self.lock:
self.unregister_socket(sock)

self.transport.forward_and_close(SocketWrapper(sock))

class Transport:
def forward_and_close(self, readable):
with self.lock:
more = readable.read(1000)
if more is WOULDBLOCK:
self.reschedule(readable)
elif more:
... # out of scope for the anecdote
else:
# EOF reached
readable.close()

Now the dreaded lock reversal arises when the App object calls
self.transport.forward_and_close() and Transport calls readable.close()
at the same time.

So why lock categorically like that? Java has a handy "synchronized"
keyword that wraps the whole method in "with self.lock". Ideally, that
handy idiom could be employed methodically. More importantly, to avoid
locking problems, the methodology should be rigorous and mindless. If
the developer must perform a deep locking analysis at every turn, they
are bound to make mistakes, especially when more than one developer is
involved, with differing intuitions.

Unfortunately, that deep locking analysis *is* required at every turn,
and mistakes *are* bound to happen.


Marko

Marko Rauhamaa

unread,
Feb 24, 2015, 3:12:23 AM2/24/15
to
Paul Rubin <no.e...@nospam.invalid>:

> Deadlocks and livelocks can happen in multi-process code just like
> with threads.

The main thing to avoid is to assume that writing can block until its
completion. At least one of the communicating parties must write in a
nonblocking manner and make sure it is always ready to read.

The remaining problem is flow control to prevent write buffer flooding.

All of that is easier to get right than the intricacies of
multithreading.


Marko

Emile van Sebille

unread,
Feb 24, 2015, 12:58:35 PM2/24/15
to pytho...@python.org
On 2/22/2015 9:22 AM, Laura Creighton wrote:

> There was, and still is, an enormous amount of resentment about this.
> For a lot of people, the perception was, and still is that the benefits
> of Python 3.x over Python 2.x was not worth breaking backwards
> compatibility. And there still are plenty of places whose plan is
> to use Python 2.7 indefinitely into the far future. I've got 15
> years worth of commercial python code out there in the world, and
> nobody wants it converted enough to pay me to do so. Their position
> is that it runs quite well enough as it is. I'm sure not
> going to convert the stuff for fun. Practically every Python consultant on
> the planet is in the same boat.

+1

As a glue language, it's simply impractical to reapply the glue.

Emile


Paul Rubin

unread,
Feb 24, 2015, 6:54:10 PM2/24/15
to
Marko Rauhamaa <ma...@pacujo.net> writes:
> So why lock categorically like that? Java has a handy "synchronized"
> keyword that wraps the whole method in "with self.lock". ...
> Unfortunately, that deep locking analysis *is* required at every turn,
> and mistakes *are* bound to happen.

I wonder if synchronized was a mistake in Java. It confused me a lot
when I tried to use it, but I never had to mess with it that much. I
can't quite tell what your code is doing (why is it attempting a socket
read with a lock held) and I'd be interested to see what an STM version
would look like.

Ryan Stuart

unread,
Feb 24, 2015, 8:00:03 PM2/24/15
to pytho...@python.org
On Tue Feb 24 2015 at 3:32:47 PM Paul Rubin <no.e...@nospam.invalid> wrote:

Ryan Stuart <ryan.st...@gmail.com> writes:
Sure, the shared memory introduces the possibility of some bad errors,
I'm just saying that I've found that by staying with a certain
straightforward style, it doesn't seem difficult in practice to avoid
those errors.

And all I'm saying is that it doesn't matter how careful you are, all it takes is someone up the stack to not be as careful and you will be bitten. And even if they are careful, it's still very easy to be bitten because everything runs in shared memory.
 
I don't understand what you mean about malloc. 

My point is malloc, something further up (down?) the stack, is making modifications to shared state when threads are involved. Modifying shared state makes it infinitely more difficult to reason about the correctness of your software.
 
That article was interesting in some ways but confused in others.  One
way it was interesting is it said various non-thread approaches (such as
coroutines) had about the same problems as threads.  Some ways it was confused were:

We clearly got completely different things from the article. My interpretation was that it was making *the exact opposite* point to what you stated mainly because non-threading approaches don't share state. It states that quite clearly. For example "it is – literally – exponentially more difficult to reason about a routine that may be executed from an arbitrary number of threads concurrently".
 
  1) thinking Haskell threads were like processes with separate address
  spaces.  In fact they are in the same address space and programming
  with them isn't all that different from Python threads, though the
  synchronization primitives are a bit different.  There is also an STM
  library available that is ingenious though apparently somewhat slow.

I don't know Haskell, so I can't comment too much, except to say that by default Haskell looks to use lightweight threads where only 1 thread can be executing at a time [1] (sounds similar to the GIL to me). That doesn't seem to be shared state multithreading, which is what the article is referring to. But again, they will undoubtedly be someone sharing state higher up the stack.
 
  2) it has a weird story about the brass cockroach, that basically
  signified that they didn't have a robust enough testing system to be
  able to reproduce the bug.  That is what they should have worked on.

The point was that it wasn't feasible to have a robust testing suite because, you guessed it, shared state and concurrent threads producing n*n complexity.
 
  3) It goes into various hazards of the balance transfer example not
  mentioning that STM (available in Haskell and Clojure) completely
  solves it.

This is probably correct. Is there any STM implementations out that that don't significantly compromise performance?

Anyway, I got one thing out of this, which is that the multiprocessing
module looks pretty nice and I should try it even when I don't need
multicore parallelism, so thanks for that.

It's 1 real advantage is that it side-steps the GIL. So, if you need to utilise multiple cores for CPU bound tasks, then it might well be the only option.

Cheers
 
In reality though, Python is still my most productive language for
throwaway, non-concurrent scripting, but for more complicated concurrent
programs, alternatives like Haskell, Erlang, and Go all have significant
attractions.

Marko Rauhamaa

unread,
Feb 25, 2015, 12:25:24 AM2/25/15
to
Paul Rubin <no.e...@nospam.invalid>:
Synchronized methods are actually quite a powerful idea, but a bit
underappreciated by many Java developers.

The main point of my example is this:

* Effective thread-safe programming would require some guidelines, a
routine, a methodology so the code would be free of locking anomalies
as a matter of course.

* The industry hasn't formed such guidelines that are generally
followed. That makes it difficult to glue together systems from
components that come from different sources.

* Even the best of guidelines have significant, surprising corner cases
that require special treatment and are easy to get wrong.

* As a corollary, you cannot implement thread-safety locally in a
class. Instead, your locking decisions must be made application-wide.
That violates encapsulation, which is a cornerstone of
object-oriented programming (and large-system manageability).


Marko

Marcos Almeida Azevedo

unread,
Feb 25, 2015, 12:34:49 AM2/25/15
to Marko Rauhamaa, pytho...@python.org
On Wed, Feb 25, 2015 at 1:25 PM, Marko Rauhamaa <ma...@pacujo.net> wrote:
Paul Rubin <no.e...@nospam.invalid>:

> Marko Rauhamaa <ma...@pacujo.net> writes:
>> So why lock categorically like that? Java has a handy "synchronized"
>> keyword that wraps the whole method in "with self.lock". ...
>> Unfortunately, that deep locking analysis *is* required at every turn,
>> and mistakes *are* bound to happen.
>
> I wonder if synchronized was a mistake in Java. It confused me a lot
> when I tried to use it, but I never had to mess with it that much. I
> can't quite tell what your code is doing (why is it attempting a
> socket read with a lock held) and I'd be interested to see what an STM
> version would look like.

Synchronized methods are actually quite a powerful idea, but a bit
underappreciated by many Java developers.


Synchronized methods in Java really makes programming life simpler.  But I think it is standard practice to avoid this if there is a lighter alternative as synchronized methods are slow.  Worse case I used double checked locking.
 


Marko
--
https://mail.python.org/mailman/listinfo/python-list



--
Marcos | I love PHP, Linux, and Java

Marko Rauhamaa

unread,
Feb 25, 2015, 12:47:00 AM2/25/15
to
Marcos Almeida Azevedo <marcos.a...@gmail.com>:

> Synchronized methods in Java really makes programming life simpler.
> But I think it is standard practice to avoid this if there is a
> lighter alternative as synchronized methods are slow. Worse case I
> used double checked locking.

I have yet to see code whose performance suffers from too much locking.
However, I have seen plenty of code that suffers from anomalies caused
by incorrect locking.


Marko

Chris Angelico

unread,
Feb 25, 2015, 12:54:23 AM2/25/15
to pytho...@python.org
Uhh, I have seen *heaps* of code whose performance suffers from too
much locking. At the coarsest and least intelligent level, a database
program that couldn't handle concurrency at all, so I wrote an
application-level semaphore that stopped two people from running it at
once. You want to use that program? Ask the other guy to close it.
THAT is a performance problem. And there are plenty of narrower cases,
where it ends up being a transactions-per-second throughput limiter.

ChrisA

Marcos Almeida Azevedo

unread,
Feb 25, 2015, 12:58:32 AM2/25/15
to Marko Rauhamaa, pytho...@python.org
Of course code with locking is slower than one without. The locking mechanism itself is the overhead.  So use it only when necessary.  There is a reason why double checked locking was invented by clever programmers.

 
Marko
--
https://mail.python.org/mailman/listinfo/python-list

Ian Kelly

unread,
Feb 25, 2015, 1:03:31 AM2/25/15
to Python
On Tue, Feb 24, 2015 at 10:54 PM, Chris Angelico <ros...@gmail.com> wrote:
> Uhh, I have seen *heaps* of code whose performance suffers from too
> much locking. At the coarsest and least intelligent level, a database
> program that couldn't handle concurrency at all, so I wrote an
> application-level semaphore that stopped two people from running it at
> once. You want to use that program? Ask the other guy to close it.
> THAT is a performance problem. And there are plenty of narrower cases,
> where it ends up being a transactions-per-second throughput limiter.

Is the name of that database program "Microsoft Access" perchance?

Chris Angelico

unread,
Feb 25, 2015, 1:08:31 AM2/25/15
to Python
On Wed, Feb 25, 2015 at 5:02 PM, Ian Kelly <ian.g...@gmail.com> wrote:
>> Uhh, I have seen *heaps* of code whose performance suffers from too
>> much locking. At the coarsest and least intelligent level, a database
>> program that couldn't handle concurrency at all, so I wrote an
>> application-level semaphore that stopped two people from running it at
>> once. You want to use that program? Ask the other guy to close it.
>> THAT is a performance problem. And there are plenty of narrower cases,
>> where it ends up being a transactions-per-second throughput limiter.
>
> Is the name of that database program "Microsoft Access" perchance?

No, though it wouldn't surprise me if it had the same issue. No, the
program was a DBase-backed one of my own development; it was the DBase
engine itself that couldn't handle concurrency, so I added some
startup code that checked to see if anyone else had the file open, and
popped up a "retry or cancel" prompt until the semaphore cleared.

Later on, I was able to shift the entire content into DB2 (once we no
longer needed compatibility with an even older DBase-backed program),
and voila, we had concurrency. I still needed to make use of
record-level locking (if you open a record for editing, it holds a
lock for as long as you have the window open; chances are, anyone else
who wants the same record is actually doing the same job, so erroring
out is the best option), but no more database-level locks.

ChrisA

Mark Lawrence

unread,
Feb 25, 2015, 11:38:12 AM2/25/15
to pytho...@python.org
On 25/02/2015 06:02, Ian Kelly wrote:
>
> Is the name of that database program "Microsoft Access" perchance?
>

Are you referring to the GUI, the underlying database engine, both, or what?

--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

Ian Kelly

unread,
Feb 25, 2015, 12:02:02 PM2/25/15
to Python
On Wed, Feb 25, 2015 at 9:37 AM, Mark Lawrence <bream...@yahoo.co.uk> wrote:
> On 25/02/2015 06:02, Ian Kelly wrote:
>>
>>
>> Is the name of that database program "Microsoft Access" perchance?
>>
>
> Are you referring to the GUI, the underlying database engine, both, or what?

The engine. In theory it supports concurrent access. In practice, it
seems that access is frequently blocked by locks generated by some
other user who was working on it hours ago and is now out of the
office. Not to mention the times when the lock file gets so badly
corrupted that the resolution is to just delete it.

I haven't used it in some time though, so maybe it's gotten better.

Mark Lawrence

unread,
Feb 25, 2015, 12:17:40 PM2/25/15
to pytho...@python.org
IIRC the underlying JET engine was replaced by SQL Server years ago.
Maybe not the best technlogy in the world, but you'd be hard pushed to
do worse than JET :)

Chris Angelico

unread,
Feb 25, 2015, 12:23:03 PM2/25/15
to pytho...@python.org
On Thu, Feb 26, 2015 at 4:16 AM, Mark Lawrence <bream...@yahoo.co.uk> wrote:
> IIRC the underlying JET engine was replaced by SQL Server years ago. Maybe
> not the best technlogy in the world, but you'd be hard pushed to do worse
> than JET :)

The way I understood it, MS Access could connect to a variety of
database backends (SQL Server, or anything that uses ODBC, or whatever
else), but the only inbuilt engine - and therefore the one that you
get by default if you aren't running some other server - was MS Jet.
If they're incorporating SQL Server into MS Office, that would make it
huge... wait, I'm not sure we could tell the difference. But still,
that'd be a whopping great slab of database engine. It'd be like
Python incorporating PostgreSQL. I've at times said that the Python
stdlib ought to include a Postgres *client* (on par with psycopg2),
but not the full *server* :)

ChrisA
Message has been deleted

Paul Rubin

unread,
Feb 27, 2015, 12:56:07 AM2/27/15
to
Ryan Stuart <ryan.st...@gmail.com> writes:
> My point is malloc, something further up (down?) the stack, is making
> modifications to shared state when threads are involved. Modifying
> shared state makes it infinitely more difficult to reason about the
> correctness of your software.

If you're saying the libc malloc might have bugs that affect
multithreaded apps but not single threaded ones, then sure, but the
Linux kernel might also have such bugs and it's inherently
multithreaded, so there's no escape. Even if your app is threaded
you're still susceptible to threading bugs in the kernel.

If malloc works properly then it's thread-safe and you can use it
without worrying about how your app's state interacts with malloc's
internal state.

> We clearly got completely different things from the article. My
> interpretation was that it was making *the exact opposite* point to
> what you stated mainly because non-threading approaches don't share
> state.

It gave the example of asyncio, which is non-threaded but (according to
the article) was susceptible to shared state bugs because you could
accidentally insert yield points in critical sections, by doing things
like logging.

> It states that quite clearly. For example "it is – literally –
> exponentially more difficult to reason about a routine that may be
> executed from an arbitrary number of threads concurrently".

I didn't understand what it was getting at with that n**n claim. Of
course arbitrary code (even single threaded) is incalculably difficult
to reason about (halting problem, Rice's theorem). But we're talking
about code following a particular set of conventions, not arbitrary
code. The conventions are supposed to facilitate reasoning and
verification. Again there's tons of solid theory in the OS literature
about this stuff.

> by default Haskell looks to use lightweight threads where only 1
> thread can be executing at a time [1]... That doesn't seem to be
> shared state multithreading, which is what the article is referring to.

Haskell uses lightweight, shared state threads with synchronization
primitives that do the usual things (the API is somewhat different than
Posix threads though). You have to use the +RTS command line option to
run on multiple cores: I don't know why the default is to stay on a
single core. There might be a performance hit if you use the multicore
runtime with a single-threaded program, or something like that.

There is a book about Haskell concurrency and parallelism that I've been
wanting to read (full text online):

http://chimera.labs.oreilly.com/books/1230000000929/index.html

> 2) it has a weird story about the brass cockroach, that basically
> signified that they didn't have a robust enough testing system to
> be able to reproduce the bug.
>
> The point was that it wasn't feasible to have a robust testing suite
> because, you guessed it,

No really, they observed this bug happening repeatedly under what
sounded like fairly light load with real users. So a stress testing
framework should have been able to reproduce it. Do you really think
it's impossible to debug this kind of problem? OS developers do it all
the time. There is no getting around it.

> This is probably correct. Is there any STM implementations out that
> that don't significantly compromise performance?

STM is fast as long as there's not much contention for shared data
between threads. In the "account balance" example that should almost
always be the case. The slowdown is when multiple threads are fighting
over the same data and transactions keep having to be rolled back and
restarted.

> multiprocessing module looks pretty nice and I should try it
> It's 1 real advantage is that it side-steps the GIL. So, if you need
> to utilise multiple cores for CPU bound tasks, then it might well be
> the only option.

It's 1 real advantage compared to what? I thought you were saying it
avoids shared data hazards of threads. The 4 alternatives in that
article were threads, multiprocessing, old-fashioned async (callback
hell), and asyncio (still contorted and relies on Python 3 coroutines).
If you eliminate threads because of data sharing and asyncio because you
need Python 2 compatibility, you're left with multiprocessing if you
want to avoid the control inversion of callback style.

It's true though, this started out about the GIL in PyPy (was Laura
going to post about that?) so using multicores is indeed maybe relevant.

Paul Rubin

unread,
Feb 27, 2015, 4:40:30 PM2/27/15
to
Steven D'Aprano <steve+comp....@pearwood.info> writes:
> An interesting point of view: threading is harmful because it removes
> determinism from your program.
> http://radar.oreilly.com/2007/01/threads-considered-harmful.html

Concurrent programs are inherently nondeterministic because they respond
to i/o events that can happen in any order. I looked at the paper cited
in that article and it seemed like handwaving. Then it talks about
threaded programs being equivalent if they are the same over all
interleavings of input, and then goes on about that being horribly
difficult to establish. It talked about program inputs as infinite
sequences of bits. OK, a standard conceit in mathematical logic is to
call an infinite sequence of bits a "real number". So it seems to me
that such a proof would just be a theorem about real numbers or sets of
real numbers, and freshman calculus classes are already full of proofs
like that. The presence of large sets doesn't necessarily make math all
that much harder. The test suite for HOL Light actually uses an
inaccessible cardinal, if that means anything to you.

IOW he says it's difficult and maybe it is, but he doesn't make any
attempt to explain why it's difficult, at least once there's some tools
(synchronization primitives etc.) to control the concurrency. He seems
instead to ignore decades of work going back to Dijkstra and Wirth and
those guys. It would be a lot more convincing if he addressed that
existing literature and said why it wasn't good enough to help write
real programs that work.

He then advocates something he calls the "PN model" (processes
communicating by message passing) but that seems about the same as what
I've heard called communicating sequential processes (CSP), which are
the execution model of Erlang and is what I've been using with Python
threads and queues. Maybe there's some subtle difference. Anyway
there's again plenty of theory about CSP, which are modelled with
Pi-calculus (process calculus) which can be interpreted in lambda
calculus, so sequential verification techniques are still useful on it.

Hmm, I see there's a Wikipedia article "Kahn process networks" about PN
networks as mentioned, so I guess I'll look at it. I see it claims a
KPN is deterministic on its inputs, while I think CSP's might not be.

> Some discussion of the pros and cons of threading:
> http://c2.com/cgi/wiki?ThreadsConsideredHarmful

This wasn't very informative either.
Message has been deleted
0 new messages