More CPUs doen't equal more speed

Cameron Simpson cs at cskk.id.au
Thu May 23 21:32:13 EDT 2019


On 23May2019 17:04, bvdp <bob at mellowood.ca> wrote:
>Anyway, yes the problem is that I was naively using command.getoutput()
>which blocks until the command is finished. So, of course, only one process
>was being run at one time! Bad me!
>
>I guess I should be looking at subprocess.Popen(). Now, a more relevant
>question ... if I do it this way I then need to poll though a list of saved
>process IDs to see which have finished? Right? My initial thought is to
>batch them up in small groups (say CPU_COUNT-1) and wait for that batch to
>finish, etc. Would it be foolish to send send a large number (1200 in this
>case since this is the number of files) and let the OS worry about
>scheduling and have my program poll 1200 IDs?
>
>Someone mentioned the GIL. If I launch separate processes then I don't
>encounter this issue? Right?

Yes, but it becomes more painful to manage. If you're issues distinct 
separate commands anyway, dispatch many or all and then wait for them as 
a distinct step.  If the commands start thrashing the rest of the OS 
resources (such as the disc) then you may want to do some capacity 
limitation, such as a counter or semaphore to limit how many go at once.

Now, waiting for a subcommand can be done in a few ways.

If you're then parent of all the processes you can keep a set() of the 
issued process ids and then call os.wait() repeatedly, which returns the 
pid of a completed child process. Check it against your set. If you need 
to act on the specific process, use a dict to map pids to some record of 
the subprocess.

Alternatively, you can spawn a Python Thread for each subcommand, have 
the Thread dispatch the subcommand _and_ wait for it (i.e. keep your 
command.getoutput() method, but in a Thread). Main programme waits for 
the Threads by join()ing them.

Because a thread waiting for something external (the subprocess) doesn't 
hold the GIL, other stuff can proceed. Basicly, if something is handed 
off the to OS and then Python waits for that (via an os.* call or a 
Popen.wait() call etc etc) then it will release the GIL while it is 
blocked, so other Threads _will_ get to work.

This is all efficient, and there's any number of variations on the wait 
step depending what your needs are.

The GIL isn't the disaster most people seem think. It can be a 
bottleneck for pure Python compute intensive work. But Python's 
interpreted - if you _really_ want performance the core compute will be 
compiled to something more efficient (eg a C extension) or handed to 
another process (transcode video in pure Python - argh! - but call the 
ffmpeg command as a subprocess - yes!); handed off, the GIL should be 
released, allowing other Python side work to continue.

Cheers,
Cameron Simpson <cs at cskk.id.au>



More information about the Python-list mailing list