ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About

Plate 17

  1. Blog

subprocess.run vs Popen: Localhost Lab

A measured Linux localhost lab comparing subprocess.run with Popen and communicate for tiny process spawns and captured stdout.

Aditya Challa·30 September 2026·4 min read

Summary
On this page
  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table (p50 launches/s)
  5. Reading it
  6. Convenience vs control
  7. Why seq flipped slightly
  8. Capture cost reminder
  9. Overlap and streaming
  10. Security baseline
  11. Pitfalls
  12. Reproduce
  13. Limits
  14. Batch when possible
  15. Takeaway

Intro — what this post promises

Spawn a tiny process many times: subprocess.run vs Popen + communicate. This lab reports launches/s for /bin/true, captured echo, and a short seq stdout capture on Linux localhost.

Related links:

  • futures as completed vs wait localhost lab
  • threadpoolexecutor vs sequential localhost lab
  • asyncio gather vs taskgroup localhost lab
  • shelve vs pickle dict localhost lab
  • secrets vs urandom localhost lab
  • scandir vs listdir localhost lab
  • tarfile vs zipfile localhost lab
  • textwrap fill vs manual localhost lab

Lab honesty (1 Oct 2026 IST): Python 3.13.5. Affiliates: 0. No Docker. No shell=True.

Verdict up front: Popen+/bin/true ~1745.5 launches/s vs run ~1610.6. With pipes: Popen ~1499.8 vs run(capture_output=True) ~1309.5. Echo capture: run ~1136.3 ≈ Popen ~1177.9.


Arms

ArmPattern
run(["/bin/true"])high-level wait
Popen + communicatelower-level
run(..., capture_output=True)pipes via run
Popen with PIPEsexplicit pipes
echo / seqsmall stdout capture

Lab topology

true: 200 launches × 5 rounds · p50
echo: 100 · seq 1..500: 50
metric: launches/s = n / p50_wall

Script: lab-evidence/95-subprocess-run-vs-popen/results/run_lab.py.


Lead table (p50 launches/s)

Armlaunches/s
Popen /bin/true1745.5
run /bin/true1610.6
Popen true + pipes1499.8
run true capture1309.5
Popen echo communicate1177.9
run echo capture1136.3
run seq 1..5001065.0
Popen seq 1..500998.8

Reading it

  • run ≈ Popen+communicate for these tiny commands — convenience tax is small (~1.08× at best for bare true).
  • capture_output=True / PIPEs cost more than a no-pipe spawn (true: ~1610.6 → ~1309.5 launches/s).
  • Prefer run for one-shot commands; use Popen when you need streaming, overlap, or custom pipe wiring.
  • Spawning /bin/true 1000× is still an anti-pattern — batch work inside one process when you can.

Convenience vs control

subprocess.run is the recommended API for most cases. This lab’s numbers support that: you are not leaving meaningful launches/s on the table versus hand-rolled Popen for fire-and-wait. Reach for Popen when run cannot express the lifecycle.


Why seq flipped slightly

On seq 1 500, run edged Popen (~1065.0 vs ~998.8 launches/s). Noise and helper overhead dominate at this scale; treat them as tied. The durable lesson is pipe setup cost, not a permanent ranking.


Capture cost reminder

Opening pipes for a command that writes nothing (/bin/true) still pays pipe setup. Only request capture_output / PIPEs when you will read stdout/stderr. For status-only helpers, bare run/Popen.wait stays leaner (~1611 vs ~1309 launches/s on this box).


Overlap and streaming

run always waits for the child to exit before returning. If you must read partial stdout while the child still runs, or multiplex several children, Popen is the API. This microbench never overlaps children — it measures spawn+wait convenience — so treat streaming designs as a separate profile.


Security baseline

Pass argument lists, not interpolated shell strings. Both run and Popen are safe when shell=False (default) and argv is controlled. Throughput rankings do not change that rule.


Pitfalls

  • shell=True with untrusted strings.
  • Deadlocks if you use PIPEs without communicate / threads.
  • Ignoring check=True / return codes.
  • Measuring spawn microbenches and concluding “Python is slow” when the kernel exec dominates.

Reproduce

python3 lab-evidence/95-subprocess-run-vs-popen/results/run_lab.py

Evidence: summary.json, summary.txt.


Limits

One Linux box. /bin/true, /bin/echo, seq only. Not posix_spawn tuning, not shell pipelines.


Batch when possible

If a loop calls run hundreds of times for tiny helpers, consider one long-lived worker process or an in-process function instead — spawn overhead will dominate any run vs Popen delta.


Takeaway

Popen ~1745.5 /s vs run ~1610.6 /s for /bin/true; capture paths sit lower (~1309.5–1499.8). Default to subprocess.run; use Popen when you need lower-level control — not for a few percent on tiny spawns.

subprocess.runpopencommunicateprocess spawncapture_outputlocalhost labsrepython

Lab evidence

What I found running this

Lab 1 Oct 2026 IST. Python 3.13.5. /bin/true: Popen 1745.5/s vs run 1610.6/s; capture run 1309.5/s vs Popen pipes 1499.8/s. Echo≈tied. Affiliates: 0. Evidence: lab-evidence/95-subprocess-run-vs-popen/.

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

Related links

  • Plate 17

    platform vs os.uname Inventory: Localhost Lab

    Hands-on platform.platform vs os.uname host inventory lab: real ops/s plus cache notes, measured on Linux localhost today in this hands-on lab for SREs.

    1 Oct 2026

  • Plate 50

    signal vs threading.Event Wakeup: Localhost Lab

    Hands-on signal SIGUSR1 vs threading.Event wakeup lab: real p50 latency in microseconds, measured on Linux localhost today in this hands-on lab for SREs.

    1 Oct 2026

  • Plate 76

    cmath vs math.hypot Magnitudes: Localhost Lab

    Hands-on cmath vs math.hypot magnitude ops lab: real ops/s for abs, polar, and phase, measured on Linux localhost today in this hands-on lab for SREs.

    1 Oct 2026

On this page

  1. Intro — what this post promises
  2. Arms
  3. Lab topology
  4. Lead table (p50 launches/s)
  5. Reading it
  6. Convenience vs control
  7. Why seq flipped slightly
  8. Capture cost reminder
  9. Overlap and streaming
  10. Security baseline
  11. Pitfalls
  12. Reproduce
  13. Limits
  14. Batch when possible
  15. Takeaway
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove