Skip to main content

A lock-free, high-concurrency task broker for Python, powered by Rust.

Project description


Logo

Pyroxide

A lock-free, high-concurrency background task broker for Python, powered by Rust.

Rust Python License: MIT/Apache-2.0/Coffee

Explore the Docs »

API Reference · See Examples · Report Bug · Request Feature


Pyroxide (pyro3) is a lightweight, ultra-high-performance background task broker designed to bridge Python and Rust. It allows CPU-bound or blocking workloads to bypass the Python Global Interpreter Lock (GIL) with minimal memory overhead and zero CPU-sleep polling.

Why Pyroxide?

  • 🚀 Bypass the GIL (GIL-Free): Execute CPU-intensive compiled tasks on background OS threads without holding the Python GIL.
  • Microsecond Latency: Utilizes OS-level signaling (Condvar) rather than CPU-burning thread polling, dispatching and completing tasks in under 25 microseconds.
  • 📦 Zero Infrastructure: Runs completely in-process. No Redis, RabbitMQ, or Celery worker daemons to configure or maintain.
  • 💾 Zero-Copy Serialization: Pass large byte arrays, memoryviews, or columnar buffers across the C-ABI boundary without copy or pickle overhead.
  • 🛠️ On-the-Fly Native Compilers: Write code as Python strings and compile them to dynamic libraries on-the-fly (Rust, C, and Zig supported!).
  • 🛡️ Isolated Worker Processes: Opt-in isolated=True to run tasks in separate processes via cross-platform Named Pipes / Domain Sockets. Features bidirectional Zero-Copy Shared Memory (SHM) routing for payloads >= 1MB, and an auto-scaling Scale-to-Zero pool to reclaim memory.
  • 🔗 Task Groups & Workflows: Bundle multiple task handles into parallel groups (group) to await or cancel them as a single logical unit.

Pyroxide vs. Alternatives

Feature / Metric Pyroxide Threading (std) Multiprocessing Celery / RQ
GIL Bypass ✅ Yes (WASM/dylib) ❌ No ✅ Yes ✅ Yes
IPC / Serialization ✅ None (Shared Memory) ✅ None ❌ High (Pickling) ❌ High (Network/Redis)
Infrastructure ✅ None (Embedded) ✅ None ⚠️ Low (Spawns processes) ❌ High (Redis/RabbitMQ)
Best For 🔥 High-perf in-process pipelines I/O-bound Python CPU-heavy Python Distributed tasks

For a detailed analysis, check out the Library Comparison Guide.


Installation

From PyPI

pip install pyro3

Build Locally

Ensure you have Rust, Python (3.8+), and maturin installed:

git clone https://github.com/emivvvvv/pyroxide.git
cd pyroxide
pip install maturin
maturin develop

Quick Start

1. Offload Python Callables

from pyroxide import task

@task
def calculate_square(x: int) -> int:
    return x * x # Runs in background OS threads

# Submit and get a handle immediately
handle = calculate_square(12)
result = handle.result() # Blocks natively (0% CPU) until complete
print(result) # 144

# Pure Python tasks can fully bypass the GIL with `isolated=True`
@task(isolated=True)
def heavy_computation(x: int) -> int:
    return sum(i * i for i in range(x))

2. Batch Submission & Task Groups

Submit tasks in bulk under a single lock acquisition to avoid thread contention, and manage them concurrently:

from pyroxide import task, group

@task
def calculate_square(x: int) -> int:
    return x * x

payloads = [10, 20, 30, 40]

# 1. Batch submit payloads
handles = calculate_square.batch(payloads)

# 2. Bundle into a parallel TaskGroup
tg = group(handles)
print(tg.status) # "Running"

# 3. Retrieve results (consume=False preserves status metadata)
results = tg.result(consume=False)
print(results)   # [100, 400, 900, 1600]
print(tg.status) # "Completed"

3. Sandboxed WebAssembly (GIL-Free)

Run computations GIL-free in a secure, virtual sandbox without compiling native code:

from pyroxide import register_wasm, wasm_task

# 1. Register WebAssembly bytecode
with open("rot13.wasm", "rb") as f:
    register_wasm("rot13", f.read())

# 2. Decorate stub function
@wasm_task("rot13")
def rot13_cipher(payload: str) -> str:
    pass

# 3. Execute GIL-free on the Rust worker pool!
print(rot13_cipher("hello").result()) # "uryyb"

4. Dynamic Shared Libraries (On-the-Fly Compilation)

Compile and load native code strings on-the-fly. Rust (compile_dylib), C (compile_c), and Zig (compile_zig) are supported:

from pyroxide import compile_dylib, dylib_task

RUST_SRC = """
#[no_mangle]
pub unsafe extern "C" fn pyroxide_plugin_run(ptr: *const u8, len: usize, out_len: *mut usize) -> *mut u8 {
    let input = std::slice::from_raw_parts(ptr, len);
    let s = std::str::from_utf8(input).unwrap_or("");
    let result = s.to_uppercase().into_bytes();
    *out_len = result.len();
    let boxed = result.into_boxed_slice();
    Box::into_raw(boxed) as *mut u8
}

#[no_mangle]
pub unsafe extern "C" fn pyroxide_plugin_free(ptr: *mut u8, len: usize) {
    let _ = Box::from_raw(std::slice::from_raw_parts_mut(ptr, len));
}
"""

# Compile, register and load the Rust library on-the-fly!
compile_dylib("rust_upper", RUST_SRC)

@dylib_task("rust_upper")
def to_upper_rust(payload: str) -> str:
    pass

print(to_upper_rust("hello from rust").result())  # "HELLO FROM RUST"

Dive Deeper (Documentation Book)

Detailed documentation, guides, and implementation examples are available in our Documentation Book:

  • Asynchronous Event Loops: Non-blockingly await tasks using await handle.result_async() in FastAPI/asyncio. Read Chapter.
  • Isolated Worker Processes: Sandbox tasks in separate OS processes for crash safety and GIL bypass. Read Chapter.
  • Batch Submissions: Submit multiple tasks under a single lock acquisition to avoid thread contention. Read Chapter.
  • Task Cancellation: Gracefully abort long-running background tasks mid-flight. Read Chapter.
  • Traceback Preservation: Capture stack traces on background worker threads and propagate them to the main thread. Read Chapter.
  • Memory Footprint & GC: Learn how Slab memory is reclaimed automatically using GC destructors. Read Chapter.

Performance At-a-Glance

We benchmarked Pyroxide against CPython's standard concurrency pools using identical compute payloads (recursive Fibonacci 20 workload) on Apple M1 Pro (8 cores, 16GB RAM):

Metric (500 Tasks) Pyroxide @dylib_task Pyroxide @task(isolated=True) Pyroxide @task Threading (std) Multiprocessing
Execution Time 0.0200 s 0.0769 s 0.3878 s 0.3742 s 2.0786 s
GIL Bypass ✅ Yes (GIL-Free) ✅ Yes ❌ No ❌ No ✅ Yes
IPC / Serialization ✅ None (Shared Memory) ✅ Zero-Copy SHM ✅ None ✅ None ❌ High (pickle cost)
Relative Speedup 🔥 100x faster 🔥 27x faster 5x faster 5x faster Baseline (1x)
  • Bypassing the Multiprocessing Bottleneck: While Python's ProcessPoolExecutor takes over 2 seconds due to slow process spawning and heavy pickle IPC serialization, Pyroxide's @dylib_task runs native compiled plugins in just 20 milliseconds—offering a 100x speedup with zero-copy shared memory.

Real-World Odoo Enterprise Arrow Ledger Audit Benchmark

To test performance under realistic enterprise data movement workloads, we ran a simulated Odoo Ledger Audit benchmark processing a 9.62 MB Apache Arrow serialized transaction recordset (200,000 journal items) across 10 concurrent requests comparing different concurrency strategies:

  • CPython ThreadPoolExecutor (GIL-Locked): 0.3221 s
  • Pyroxide Threaded @task (GIL-Locked): 0.3298 s (matches Python's native scheduling overhead perfectly)
  • ProcessPoolExecutor (Python, Pickled Pipes): 0.2758 s
  • Pyroxide SHM Isolated @task (Zero-Copy SHM): 0.3272 s
  • Pyroxide @dylib_task (C-compiled, GIL-Free): 0.0091 s (bypasses GIL entirely)

Key Takeaway: By offloading the audit logic to a dynamically compiled C/Rust plugin running on Pyroxide's background thread pool, we achieve a 35.3x speedup over CPython's standard ThreadPoolExecutor by completely bypassing the GIL.

To run the Odoo simulation suite locally:

python examples/odoo_poc/odoo_complex_simulation.py

To run the comparative and basic benchmark suites locally:

# 1. Run detailed comparative benchmarks against CPython concurrency pools
python examples/benchmarks/benchmark_vs_alternatives.py

# 2. Run basic scheduling latency and asyncio benchmarks
python examples/benchmarks/benchmark.py

Contributing

Contributions are welcome! If you'd like to improve Pyroxide or add support for additional features, feel free to open an issue or submit a pull request on GitHub.

License

Pyroxide is licensed under any of:

at your option.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyro3-0.5.0.tar.gz (85.1 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pyro3-0.5.0-cp38-abi3-win_amd64.whl (3.3 MB view details)

Uploaded CPython 3.8+Windows x86-64

pyro3-0.5.0-cp38-abi3-manylinux_2_39_x86_64.whl (4.2 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.39+ x86-64

pyro3-0.5.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (4.0 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARM64

pyro3-0.5.0-cp38-abi3-macosx_11_0_arm64.whl (3.7 MB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

pyro3-0.5.0-cp38-abi3-macosx_10_12_x86_64.whl (3.8 MB view details)

Uploaded CPython 3.8+macOS 10.12+ x86-64

File details

Details for the file pyro3-0.5.0.tar.gz.

File metadata

  • Download URL: pyro3-0.5.0.tar.gz
  • Upload date:
  • Size: 85.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: maturin/1.14.1

File hashes

Hashes for pyro3-0.5.0.tar.gz
Algorithm Hash digest
SHA256 8f525327daae0fe8f2fd2ce6bdc64c92a96caff05d645292a7a4d51df3101796
MD5 5507017ed5d96e40a0df72f7e7f511b5
BLAKE2b-256 3875668bb62b9acd48ad25c6c20343f262cb0b92df64fffcf8213e07f3e9121c

See more details on using hashes here.

File details

Details for the file pyro3-0.5.0-cp38-abi3-win_amd64.whl.

File metadata

  • Download URL: pyro3-0.5.0-cp38-abi3-win_amd64.whl
  • Upload date:
  • Size: 3.3 MB
  • Tags: CPython 3.8+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: maturin/1.14.1

File hashes

Hashes for pyro3-0.5.0-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 65daa5a63d688c0d5fdc48007d3351c79239b5a61f25b554c493092a3f26466b
MD5 381492ebc08beb19fa23d493dec6ff6d
BLAKE2b-256 a0baf621b086d55faa968cbdb8272f29e7d7c00eee1993278d4c24190abec8b5

See more details on using hashes here.

File details

Details for the file pyro3-0.5.0-cp38-abi3-manylinux_2_39_x86_64.whl.

File metadata

File hashes

Hashes for pyro3-0.5.0-cp38-abi3-manylinux_2_39_x86_64.whl
Algorithm Hash digest
SHA256 76f79e88c51bf65fdf6df2604c2fe38410a72e69bbcd201c724758c42444ad7b
MD5 cf1837c853fbeb86c5a1cf8fdb62b030
BLAKE2b-256 bee36b4431e8713f384f6c52c4439408d5dc5590680012fdcd46cd540c02261b

See more details on using hashes here.

File details

Details for the file pyro3-0.5.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for pyro3-0.5.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 b7a23ba61bb0a0bf4d540e8f490c3adcc5399a4b13f0e4ad1d2cf71ba3e54e27
MD5 9dd64efb54c61bc78434ad1c02111781
BLAKE2b-256 e43f986d75d49f9f17e7bb03534c27c219e7b4e313e5c0d21610b580712c1414

See more details on using hashes here.

File details

Details for the file pyro3-0.5.0-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pyro3-0.5.0-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 8f8fbf6b7d72fd57cbec28acbd8e1427451a604c5e8cff8680c87c96243635ef
MD5 9b69477b6da1a5d0a3841c89eb0f82fd
BLAKE2b-256 7f189fe5def722182b1509e43188f4a0906c9e06401c052f98ac5849c8d63464

See more details on using hashes here.

File details

Details for the file pyro3-0.5.0-cp38-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pyro3-0.5.0-cp38-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 5bed3909ef4847ee586860e3501120e0fee2de311ce99210ad93307e98533fb3
MD5 1e9146e0b8e7480a1258418d4d34797b
BLAKE2b-256 850059afbe4fc2a7ccae326ffc5a9bb50f8208ec8fda30cfdf5b915d2e35dcd3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page