Sitelet https://www.parallelpython.com/
Python 3.10+ Pure Python Zero Dependencies Apache-2.0

Parallel Python

Effortless parallel and distributed computing in Python. Scale seamlessly from every core on your local workstation to large compute clusters with a clean, job-based API, zero third-party dependencies, and zero build configuration.

Two glowing cybernetic Python snakes moving in parallel, representing high-performance parallel computing
Quick Start Guide Explore API Reference

Overview

Bypass the GIL with Worker Processes

Parallel Python (pp) provides a clean, robust framework for executing Python code concurrently across multi-core systems (SMP) and networked compute clusters. It is lightweight, zero-dependency, and designed to integrate smoothly into any Python application.

While multi-threading is standard for I/O-bound tasks in Python, CPython's Global Interpreter Lock (GIL) prevents CPU-bound threads from executing in true parallel fashion. Because only one bytecode instruction runs at any given moment, multi-threaded computational workloads remain serialized regardless of how many CPU cores your hardware provides.

Parallel Python breaks through the GIL by orchestrating isolated worker processes over high-performance inter-process communication (IPC). All low-level process lifecycles, bidirectional pipes, network sockets, serialization, and scheduling are handled automatically. Your application simply submits jobs and retrieves results. The exact same tasks run interchangeably on local CPU cores or on remote ppserver cluster nodes, with intelligent, dynamic load balancing across all available resources.

SMP & Cluster Scaling

Scale identically across multi-core workstations and distributed cluster networks using a single unified API.

Effortless Parallelization

A minimalist job-based model makes transforming existing sequential code into parallel tasks intuitive and fast.

Auto-Tuned Workers

Automatically detects available CPU cores and provisions the optimal number of worker processes out of the box.

Dynamic Pool Resizing

Adjust worker process pools on the fly with set_ncpus() at runtime without stopping or restarting the server.

Content-Addressed Caching

Functions and dependencies are transmitted once and cached by content hash, eliminating serialization overhead for repeated tasks.

Dynamic Load Balancing

Real-time scheduling dynamically dispatches jobs to the next available local or remote worker to maximize cluster throughput.

Resilient & Fault Tolerant

Worker subprocess crashes and remote network disconnections are handled gracefully with automatic task rescheduling.

Zero-Config Auto-Discovery

Cluster nodes announce availability via UDP broadcast, letting clients automatically discover and utilize compute pools.

Secure Network Protocol

Remote cluster communication requires SHA-1 challenge-response authentication using a shared secret passphrase.

Cross-Platform & Multi-Arch

Runs smoothly across Linux, macOS, and Windows on x86_64, ARM64, and heterogeneous multi-node environments.

Zero Dependencies

Pure Python implementation built exclusively on the standard library. Runs anywhere Python 3.10+ is installed.

Permissive Open Source

Licensed under Apache 2.0 with explicit patent protection and full commercial readiness.

Architecture

How It Works

Your application interacts with a single pp.Server instance. Under the hood, the server manages an in-memory job queue and an intelligent FIFO/load-balanced scheduler, dispatching tasks across local worker subprocesses (via stdio IPC pipes) and remote cluster nodes (via TCP sockets to ppserver) without exposing any low-level networking or process plumbing to your code.

Isometric architecture diagram of Parallel Python showing Client Application with Job Queue and Scheduler dispatching tasks to Local SMP Workers via stdio pipes and Remote Clusters via TCP with SHA-1 auth and UDP auto-discovery
Figure 1: Architecture overview of Parallel Python. A central pp.Server dispatches jobs concurrently to local stdio-piped worker processes and remote TCP cluster nodes running ppserver.

Quick Start

Get Started in Five Lines

Installation
pip install pp

Requires Python 3.10+ — pure Python, zero dependencies. Automatically provides the pp module and the ppserver command-line executable.

1. Multi-Core Execution on One Machine (SMP)

Python
import pp

def square(x):
    return x * x

# Initialize the server (defaults to 1 worker per CPU core)
job_server = pp.Server()

# Submit tasks asynchronously for parallel execution
f1 = job_server.submit(square, (10,))
f2 = job_server.submit(square, (20,))
f3 = job_server.submit(square, (30,))

# Retrieve results (calling f() blocks until the task completes)
print(f1(), f2(), f3())

# Clean up worker processes and pipes when finished
job_server.destroy()

2. Distributed Execution on a Cluster

Launch the ppserver daemon on each remote computational node:

On each cluster node
node-1$ ppserver
node-2$ ppserver
node-3$ ppserver

Connect from your client script by supplying the node hostnames or IP addresses:

Client Application
import pp

ppservers = ("node-1", "node-2", "node-3:60000")
job_server = pp.Server(ppservers=ppservers)

# Submit jobs with helper functions and imported modules
f1 = job_server.submit(func1, args1, depfuncs=(helper1,), modules=("math",))
f2 = job_server.submit(func2, args2, depfuncs=(helper2,), modules=("numpy",))

# Retrieve results from whichever nodes finish first
print(f1(), f2())

job_server.destroy()

3. Cluster with UDP Auto-Discovery

Instead of hardcoding a static list of IP addresses, let cluster nodes announce themselves automatically. Start each node with -a and use a wildcard on the client:

On each cluster node
ppserver -a
Client Application
import pp

# Auto-discover all available nodes on the local network
job_server = pp.Server(ppservers=("*",))

# On subnets where broadcast is restricted, specify the broadcast target:
# job_server = pp.Server(ppservers=("*",), broadcast="192.168.1.255")

4. High-Throughput Batch Submissions with Templates

Use pp.Template to compile and package a function and its dependencies once, eliminating redundant serialization overhead when submitting large volumes of tasks:

Python
import pp

def multiply(a, b):
    return a * b

job_server = pp.Server()
template = pp.Template(job_server, multiply)

# Submit 1,000 tasks with minimal serialization overhead
tasks = [template.submit(i, i * 2) for i in range(1000)]
results = [t() for t in tasks]
print("Sum of results:", sum(results))

job_server.destroy()

5. Asynchronous Callbacks and Job Groups

Python
import pp

job_server = pp.Server()

def on_complete(job_id, result):
    print(f"[{job_id}] completed with result: {result}")

# The callback is invoked as on_complete(*callbackargs, result)
job = job_server.submit(my_task, (data,),
                        callback=on_complete,
                        callbackargs=("job-42",),
                        group="batch-1")

# Wait synchronously for all jobs in a specific group
job_server.wait("batch-1")
job_server.destroy()

Reference

Module API Reference

pp.Server

pp.Server(ncpus="autodetect", ppservers=(), secret=None, restart=False, proto=4, socket_timeout=3600, loglevel=None, broadcast=None)

Instantiates a Parallel Python execution server managing local worker processes and/or remote cluster connections.

ParameterDefaultDescription
ncpus"autodetect" Number of local worker processes. Defaults to "autodetect", which queries os.cpu_count() to spawn one worker per logical CPU core. Set to 0 for cluster-only execution or specify a custom integer count.
ppservers() Tuple of remote ppserver addresses (e.g. "node-1", "192.168.1.10:60000"). Wildcard strings (e.g. "*", "192.168.1.*") activate background UDP auto-discovery.
secretNone Shared secret passphrase for authenticating network connections to remote ppserver nodes. Always specify a custom secret on shared or production networks.
restartFalse When True, worker subprocesses are cleanly recycled after completing each task, preventing memory accumulation in tasks with leaks.
proto4 Pickle protocol version used for serializing jobs, arguments, and return values on the wire.
socket_timeout3600 Socket timeout in seconds (default: 1 hour). Bounds the maximum allowable duration for remote job execution and network round-trips.
loglevelNone Logging level for the internal pp logger (e.g. logging.DEBUG, logging.INFO, logging.WARNING).
broadcastNone Target broadcast or unicast address for UDP auto-discovery packets (defaults to 255.255.255.255 for "*"). Useful when broadcast traffic is restricted across subnets.

pp.Server Methods

MethodDescription
submit(func, args=(), depfuncs=(), modules=(), callback=None, callbackargs=(), group='default', globals=None) Submits a task asynchronously to the execution queue and returns a callable task handle. Parameters: args (arguments tuple), depfuncs (tuple of helper functions called by func), modules (tuple of module names to import in the worker), callback (function invoked on completion as callback(*callbackargs, result)), group (string tag for batch synchronization), and globals (dictionary, e.g. globals(), to resolve module-level functions/classes). Invoking the returned task handle task() blocks until completion and returns the evaluated result.
wait(group=None) Blocks until all queued and executing jobs in the specified group finish. If group is None, blocks until all jobs across all groups complete.
set_ncpus(ncpus="autodetect") Dynamically adjusts the local worker process pool size at runtime without restarting the server or dropping queued tasks.
get_ncpus() Returns the current count of configured local worker processes.
get_active_nodes() Returns a dictionary mapping active compute nodes to their worker counts (e.g. {"local": 8, "192.168.1.10:60000": 16}).
get_stats() Returns a dictionary mapping node names to statistics objects with attributes ncpus (worker count), njobs (completed job count), and time (cumulative CPU execution time).
print_stats() Prints a formatted tabular breakdown of execution statistics and cluster throughput to standard output.
destroy() Gracefully terminates local worker processes, closes network transports, and releases system resources. Pending jobs that are interrupted raise DestroyedServerError.

pp.Template

pp.Template(job_server, func, depfuncs=(), modules=(), callback=None, callbackargs=(), group='default', globals=None)

Pre-compiles and serializes a function definition, dependency hierarchy, and import list once. Subsequent invocations via template.submit(*args) dispatch tasks with minimal overhead, maximizing throughput when fanning out large batches of identical computations.

pp.DestroyedServerError

Exception raised when attempting to retrieve the result of a task (task()) if the underlying pp.Server instance was destroyed before the computation finished.

Job Semantics & Best Practices

Remote Compute

ppserver Command-Line Daemon

The ppserver command turns any machine into a high-performance Parallel Python compute node. Start it on remote hosts, then reference them directly via pp.Server(ppservers=...) or let UDP auto-discovery connect them automatically.

Example Invocations
# Start with 8 workers and UDP auto-discovery enabled
ppserver -w 8 -a

# Listen on a specific network interface and port with a custom secret
ppserver -i 192.168.1.50 -p 60000 -s "SuperSecretPassphrase"

# Write a PID file and inspect real-time stats using SIGUSR1
ppserver -P /var/run/ppserver.pid
kill -USR1 "$(cat /var/run/ppserver.pid)"
OptionDescription
-h, --helpShow the command-line help message and exit.
-aEnable the UDP auto-discovery service (announces availability to clients).
-b BROADCASTBroadcast address for auto-discovery announcements (default: 255.255.255.255).
-c PATHLoad configuration parameters from an INI file ([general] and [network] sections).
-dEnable debug logging level for verbose diagnostic output.
-f FORMATCustom format string for log messages.
-i INTERFACENetwork IP interface to bind and listen on (e.g. 0.0.0.0 or 192.168.1.50).
-k SECONDSSocket timeout in seconds (also bounds the maximum execution time of any single remote job).
-n PROTOPickle protocol version used on the wire (default: 4).
-p PORTTCP port to listen on (default: 60000).
-P PID_FILEPath to write the server process PID file.
-qEnable quiet mode: suppresses startup banner and sets log level to error (only errors are printed).
-rRestart worker processes after completing each task to maintain clean state.
-s SECRETSecret passphrase for client authentication. Overrides the PP_SECRET environment variable.
-t SECONDSAutomatic idle timeout: exit if no client connections remain active for this duration.
-w NWORKERSNumber of worker subprocesses to start (defaults to the number of detected CPU cores).

On Unix-like systems, sending SIGUSR1 to the server process (kill -USR1 <pid>) prints real-time job execution statistics to standard output on the next event loop iteration without interrupting active connections.

Examples

A Complete Distributed Example

This example calculates the sum of prime numbers below multiple thresholds concurrently. The helper function isprime is passed via depfuncs, and the math module is imported in worker namespaces via modules:

sum_primes.py
import math
import pp

def isprime(n):
    """Returns True if n is prime, False otherwise."""
    if n < 2:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False
    for i in range(3, int(math.isqrt(n)) + 1, 2):
        if n % i == 0:
            return False
    return True

def sum_primes(n):
    """Calculates the sum of all prime numbers below n."""
    return sum(x for x in range(2, n) if isprime(x))

# Start the execution server with one worker per CPU core
job_server = pp.Server()

# Submit one job per input; isprime and math are shipped automatically
inputs = (100_000, 100_100, 100_200, 100_300)
jobs = [
    job_server.submit(sum_primes, (i,), depfuncs=(isprime,), modules=("math",))
    for i in inputs
]

# Collect and display results as they complete
for i, job in zip(inputs, jobs):
    print(f"Sum of primes below {i:,} is {job():,}")

# Print execution breakdown across all workers
job_server.print_stats()
job_server.destroy()

The examples/ directory in the source repository contains complete runnable scripts covering prime summation, brute-force MD5 cracking, dynamic worker pool resizing, asynchronous completion callbacks, automatic differentiation, and cluster throughput benchmarking.

Compatibility

Tested Platforms & Runtimes

Parallel Python is pure Python with zero external dependencies and no compiled C-extensions. Built strictly on standard library primitives (subprocess, socket, pickle, threading, and struct), it installs effortlessly and runs reliably across an exceptionally broad spectrum of Python versions, operating systems, hardware architectures, and endiannesses.

Zero C-Extensions

Pure Python standard library implementation. No compilation toolchains, platform-specific wheels, or third-party packages required.

Endian-Neutral Framing

Fixed 8-byte big-endian framing (!Q) ensures seamless interoperability between little-endian (x86, ARM, RISC-V) and big-endian (s390x) nodes.

Heterogeneous Clusters

Cluster clients, local workers, and remote ppserver daemons running across different OSes and CPU architectures interoperate smoothly.

1. Python Interpreters & Runtimes

Runtime Versions Tested Status
CPython (Standard) 3.10, 3.11, 3.12, 3.13, 3.14 ✔ CI Verified
CPython Free-Threaded (No-GIL) 3.13t, 3.14t ✔ CI Verified
PyPy PyPy 3.10 ✔ CI Verified

2. Operating Systems & C Libraries

Operating System Distributions & Environments Status
Linux (glibc) Ubuntu, Debian, Fedora, RHEL, CentOS, Arch ✔ CI Verified
Linux (musl libc) Alpine Linux, minimal scratch containers ✔ CI Verified
macOS Apple Silicon (ARM64) & Intel (x86_64) ✔ CI Verified
Windows Windows 10, Windows 11, Windows Server ✔ CI Verified
FreeBSD FreeBSD 14.1+ ✔ CI Verified
OpenBSD OpenBSD 7.5+ ✔ CI Verified
NetBSD NetBSD 10.0 / 11.0 ✔ CI Verified

3. Hardware Architectures & Endianness

Architecture Common Platforms & Silicon Endianness Status
x86_64 / AMD64 Intel Core/Xeon, AMD Ryzen/EPYC workstations & cloud VMs Little-Endian ✔ CI Verified
AArch64 / ARM64 Apple M-Series, AWS Graviton, Ampere Altra, Raspberry Pi 4/5 Little-Endian ✔ CI Verified
i686 / i386 32-bit x86 legacy servers, embedded systems, 32-bit Docker Little-Endian ✔ CI Verified
ARMv7 / armhf 32-bit ARM, Raspberry Pi 2/3, BeagleBone, edge IoT hardware Little-Endian ✔ CI Verified
RISC-V 64 (riscv64) RISC-V development boards, emulators, server silicon Little-Endian ✔ CI Verified
IBM Z (s390x) IBM z/Architecture mainframe compute infrastructure Big-Endian ✔ CI Verified