Skip to main content

Parallel map for CPU-bound numeric Python via Rust workers and per-worker Python sub-interpreters

Project description

gilmap

gilmap is a Python + Rust parallel map runtime for numeric, single-argument Python functions.

It combines:

  • An auto-router that picks the fastest backend per callable (numba native, pyarrow.compute kernels, Cranelift JIT, or sub-interpreter pool)
  • Rust worker threads + Python sub-interpreters (PEP 684, one per worker) for the general fallback
  • Apache Arrow arrays for efficient Python <-> Rust transfer

Public API: gilmap.map(func, iterable, *, debug=False) and gilmap.explain(func).

See docs/ARCHITECTURE.md for the layered design and docs/BACKENDS.md for the per-backend contract.

What it is optimized for

gilmap.map is best for CPU-bound functions where each element does enough work to amortize scheduling/conversion overhead.

It is usually a poor fit for:

  • tiny per-element work (overhead dominates)
  • I/O-bound callables
  • lambdas/local functions/not-importable functions

Install

pip install gilmap

Prebuilt wheels are published for CPython 3.12 and 3.13 on Linux (x86_64, aarch64), macOS (x86_64, arm64), and Windows (x64). No Rust toolchain required.

Note: gilmap requires a standard GIL-enabled CPython build. Free-threaded builds (cp313t) are not supported because per-worker sub-interpreters with their own GIL are incompatible with PEP 703.

Requirements

  • CPython >= 3.12 (GIL-enabled build)
  • pyarrow >= 14.0.0 (installed automatically)

Quick start

Integer input

# tasks.py
def square(x: int) -> int:
    return x * x
# app.py
import gilmap
from tasks import square

out = gilmap.map(square, [1, 2, 3, 4])
print(out)  # [1, 4, 9, 16]

Float input

def affine(x: float) -> float:
    return x * 1.5 + 0.5

print(gilmap.map(affine, [1.0, 2.0, 3.0]))

Arrow input (Arrow output)

import pyarrow as pa
import gilmap
from tasks import square

arr = pa.array([1, 2, 3, 4], type=pa.int64())
out = gilmap.map(square, arr)
print(type(out))  # pyarrow.Array

API reference

gilmap.map(func, iterable, *, debug=False) -> list | pyarrow.Array

Executes func over iterable in parallel while preserving element order.

Parameters

  • func: callable accepting one numeric argument and returning one numeric value
  • iterable: either
    • a Python iterable of values castable to int64 or float64, or
    • a pyarrow.Array
  • debug: if True (or env GILMAP_DEBUG=1), prints the chosen backend per call

Return behavior

  • If input is a Python iterable: returns a Python list
  • If input is a pyarrow.Array: returns a pyarrow.Array

Type behavior

  • Float input (float32/float64) is cast to float64
  • Non-float input is cast to int64
  • Backend owns the output dtype. Most backends preserve the input lane; JIT may return int64 for an f64 input when the body does return int(...).

gilmap.explain(func) -> dict

Returns the router's decision without executing — {"backend", "reason", "has_fast_path"}. Useful to verify which path will fire.

gilmap.explain(lambda x: x * 2.0 + 1.0)
# {'backend': 'arrow_kernel', 'reason': '...', 'has_fast_path': True}

Callable constraints

Constraints depend on the backend the router picks:

  • numba_native / arrow_kernel / jit fast paths: lambdas, __main__ functions, and locals are all fine — the body is lowered to native or pyarrow.compute and never imported into a worker.
  • subinterp fallback: workers import by module + function name inside sub-interpreters, so lambdas, <locals>, and __main__ functions are rejected. Move them to an importable module.

Use gilmap.explain(func) to see which backend will run.

Error model

Exception Condition
TypeError func is not callable
ValueError lambda/local/__main__ function routed to the subinterp fallback
TypeError input cannot be cast to supported numeric Arrow type
RuntimeError execution/import failure in worker sub-interpreters; or subinterp fallback hit on a free-threaded (Py_GIL_DISABLED) build

If any worker fails, the whole call fails and no partial result is returned.

Architecture

gilmap.map runs an auto-router (gilmap/_router.py) that picks one of four backends per callable; the decision is cached on a weakref so repeat calls are O(1).

Backend Selected when Where work runs
numba_native func exposes numba dispatcher metadata or a cfunc address numpy round-trip + cached numba dispatcher
arrow_kernel body is a single return <expr> of supported binops/unops/math.* one or more pyarrow.compute C calls — whole array at once
jit same shape as arrow_kernel plus %////ternary, where arrow_kernel rejects Cranelift-compiled extern "C" fn(*const T, *mut T, len), hash-cached
subinterp nothing else matches Rust worker pool, one PEP 684 sub-interpreter per thread, interned (module, name) cache, adaptive chunk scheduling

Full per-backend contract is in docs/BACKENDS.md; the layered execution flow (Python entry → router → fast-path / Rust pool, plus the JIT pipeline) is in docs/ARCHITECTURE.md.

Worker lifecycle and shutdown

  • Sub-interpreter worker threads are long-lived after first use and reused across calls
  • shutdown_workers sends one shutdown message per worker and joins threads
  • shutdown_workers is automatically registered with atexit
  • JIT-compiled kernels are cached by IR hash for the lifetime of the process

Testing

# Rust checks
cargo clippy --all-targets --all-features
cargo test

# Python tests (after maturin develop)
python -m pytest -q

Benchmarking

Numbers below are regenerated from a real harness run — see docs/BENCHMARKS.md for the full report (per-workload tables, charts, and a mandatory "where gilmap loses" section). The block between the markers is overwritten by python -m benchmarks.report <results.json>; do not hand-edit it.

On quick_collatz at N=1,000,000, gilmap (gilmap_arrow@arrow) is 170.22× faster than the best non-gilmap runner (numba) — measured on Apple M3 Max.

Summary across 6 workloads:

workload best gilmap variant speedup vs runner-up runner-up
quick_collatz N=1,000,000 gilmap_arrow@arrow 170.22× numba
float_math N=1,000,000 gilmap_arrow@arrow 85.72× numba
float_math N=100,000 gilmap_arrow@arrow 65.80× numba
quick_collatz N=100,000 gilmap_arrow@arrow 46.96× numba
count_primes N=128 gilmap_arrow@arrow 43.21× mp_pool

⚠ rows are workloads where gilmap loses to a faster runner — included so this table is honest, not cherry-picked. Full breakdown in docs/BENCHMARKS.md.

What the suite measures

benchmarks/ runs a sweep across input size, lane (int64/float64), and container (list/Arrow) and compares gilmap.map against:

  • map (single-thread baseline)
  • multiprocessing.Pool and concurrent.futures.ProcessPoolExecutor
  • concurrent.futures.ThreadPoolExecutor (GIL-bound; included to show why naive threading fails for CPU work)
  • joblib.Parallel (loky), ray, dask — when installed
  • numpy vector form and numba @njit(parallel=True) for vectorizable workloads — included so the report can honestly show where gilmap loses

Each cell is warmed up once and timed N=3 times by default (--repeats flag to override); results are byte-equivalent to the map baseline (1e-6 float tolerance) or the cell fails. First-call warmup cost and per-runner setup cost are reported separately, not amortized into steady-state numbers.

Reproduce

pip install -e ".[bench]"
python -m benchmarks.run --out benchmarks/results/$(hostname)-$(date +%Y%m%d).json
python -m benchmarks.report benchmarks/results/<file>.json

The report generator refuses to publish if no losses are recorded — the "Where gilmap loses" section is mandatory so the marketing stays honest.

Known limitations

  • Single-argument callables only
  • Numeric lanes only (int64 / float64)
  • Callable must be importable by module + name (no lambda/local/__main__)
  • Null handling in input arrays is not currently modeled as nullable output semantics

Repository layout

.
├── Cargo.toml
├── pyproject.toml
├── gilmap/
│   ├── __init__.py         # public API: map(), explain()
│   ├── _router.py          # backend detectors + weakref decision cache
│   ├── _jit.py             # Python AST → typed JSON IR for the JIT path
│   └── _ast_utils.py       # shared AST helpers
├── src/
│   ├── lib.rs              # sub-interpreter worker pool + PyO3 bindings
│   ├── ast_ir.rs           # typed IR shared with _jit.py
│   └── jit.rs              # Cranelift codegen + per-kernel registry
├── tests/
│   ├── tasks.py
│   ├── test_router.py
│   ├── test_jit.py
│   ├── test_numba_bridge.py
│   ├── test_free_threaded.py
│   ├── test_parallel.py
│   ├── test_safety.py
│   └── test_heavy.py
├── benchmarks/             # dev-only benchmark suite
│   ├── workloads.py
│   ├── runners.py
│   ├── harness.py
│   ├── suite.py
│   ├── run.py
│   └── report.py
└── docs/
    ├── ARCHITECTURE.md     # layered design, JIT pipeline
    ├── BACKENDS.md         # per-backend contract
    └── BENCHMARKS.md       # generated; full benchmark report

CI and packaging

The GitHub Actions workflow builds wheels/sdists for multiple platforms using maturin.

Build from source

Only needed for development on gilmap itself, or to install on a platform without a prebuilt wheel.

Prerequisites:

  • Rust toolchain (cargo, rustc)
  • maturin
python -m venv .venv
source .venv/bin/activate
pip install -U pip maturin pytest
maturin develop

maturin develop builds and installs the _gilmap extension for the active environment.

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

gilmap-0.1.0.tar.gz (51.8 kB view details)

Uploaded Source

Built Distributions

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

gilmap-0.1.0-cp313-cp313-win_amd64.whl (2.0 MB view details)

Uploaded CPython 3.13Windows x86-64

gilmap-0.1.0-cp313-cp313-musllinux_1_2_x86_64.whl (2.6 MB view details)

Uploaded CPython 3.13musllinux: musl 1.2+ x86-64

gilmap-0.1.0-cp313-cp313-musllinux_1_2_aarch64.whl (2.0 MB view details)

Uploaded CPython 3.13musllinux: musl 1.2+ ARM64

gilmap-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.4 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

gilmap-0.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.8 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ ARM64

gilmap-0.1.0-cp313-cp313-macosx_11_0_arm64.whl (1.6 MB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

gilmap-0.1.0-cp312-cp312-win_amd64.whl (2.0 MB view details)

Uploaded CPython 3.12Windows x86-64

gilmap-0.1.0-cp312-cp312-musllinux_1_2_x86_64.whl (2.6 MB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ x86-64

gilmap-0.1.0-cp312-cp312-musllinux_1_2_aarch64.whl (2.0 MB view details)

Uploaded CPython 3.12musllinux: musl 1.2+ ARM64

gilmap-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.4 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

gilmap-0.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.8 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ ARM64

gilmap-0.1.0-cp312-cp312-macosx_11_0_arm64.whl (1.6 MB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

File details

Details for the file gilmap-0.1.0.tar.gz.

File metadata

  • Download URL: gilmap-0.1.0.tar.gz
  • Upload date:
  • Size: 51.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for gilmap-0.1.0.tar.gz
Algorithm Hash digest
SHA256 022593fe613f492a969c52b5ca510a22814bce23512b6d9b6af653b7eac9fd3d
MD5 1988ace94528b3dc5cadbb6fba3da474
BLAKE2b-256 59f72fdd78565249283718c6f571e4a46c1309e7e0af17cb60f06bdda6c7438f

See more details on using hashes here.

Provenance

The following attestation bundles were made for gilmap-0.1.0.tar.gz:

Publisher: CI.yml on pratikbhadane24/gilmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gilmap-0.1.0-cp313-cp313-win_amd64.whl.

File metadata

  • Download URL: gilmap-0.1.0-cp313-cp313-win_amd64.whl
  • Upload date:
  • Size: 2.0 MB
  • Tags: CPython 3.13, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for gilmap-0.1.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 ae67b19780d4d8b787a685911df3868af0ee6330d0f481de66139e04b38fc855
MD5 0ef33f055fb8f0394e5b731d36e22142
BLAKE2b-256 7b4a2b9943869a9db5ccd532ccd008e114609ee85c60f96eb70a1a385c8e81ec

See more details on using hashes here.

Provenance

The following attestation bundles were made for gilmap-0.1.0-cp313-cp313-win_amd64.whl:

Publisher: CI.yml on pratikbhadane24/gilmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gilmap-0.1.0-cp313-cp313-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for gilmap-0.1.0-cp313-cp313-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 1ad24b3992ad4561ec8077261bfb6d2d4b2b55ec18279f13a2d6fa05ab88ad0c
MD5 34ac145e3a4250e1206fa8cfbb629a68
BLAKE2b-256 6939334b9b3efb063fc2a42bfeab115e7c367d2809b8e58e0610680c02664e1b

See more details on using hashes here.

Provenance

The following attestation bundles were made for gilmap-0.1.0-cp313-cp313-musllinux_1_2_x86_64.whl:

Publisher: CI.yml on pratikbhadane24/gilmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gilmap-0.1.0-cp313-cp313-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for gilmap-0.1.0-cp313-cp313-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 f0797c177a7927c8f275de907bfb3d0442df834dedefb851a28993b0bee31aa1
MD5 e413aea4c62d3677c454ac295ef17398
BLAKE2b-256 861ca004c0ee9ad2cbb7a869c305e26534edf5163237918793cd473dbc9049ba

See more details on using hashes here.

Provenance

The following attestation bundles were made for gilmap-0.1.0-cp313-cp313-musllinux_1_2_aarch64.whl:

Publisher: CI.yml on pratikbhadane24/gilmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gilmap-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for gilmap-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 bcd173f5d3850a01d42859a50f38acd681722154150a6622ed8a481dabc12bd0
MD5 0264bdf263d5a80441d22f8388c3837c
BLAKE2b-256 800e54877e0c8eaeb5a96b64ea0598af1507af9088b9ce505ed0399edbb66592

See more details on using hashes here.

Provenance

The following attestation bundles were made for gilmap-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: CI.yml on pratikbhadane24/gilmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gilmap-0.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for gilmap-0.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 9740f1531867d4a51a3c4e6c426b746cbf8a1ed13584d983c6cf75fb884db9d8
MD5 f651178b1918a46af2e41aa3e4290074
BLAKE2b-256 1713d9cdf2dcf26b3c2b1e5d9dcdc76295006788e0bb45a85ada591a28d3cf7e

See more details on using hashes here.

Provenance

The following attestation bundles were made for gilmap-0.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: CI.yml on pratikbhadane24/gilmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gilmap-0.1.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for gilmap-0.1.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 1ff43297fc7b9d524d9049bd2d58da2a6825a7a55b328d1fba6354f27b0febd9
MD5 e625a9fb5a53fb79a7e605d311729ef8
BLAKE2b-256 4b6210ce6fe29408b0c90e87aad5b090fd6780ebdd8c83bbaa926cac24945f9c

See more details on using hashes here.

Provenance

The following attestation bundles were made for gilmap-0.1.0-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: CI.yml on pratikbhadane24/gilmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gilmap-0.1.0-cp312-cp312-win_amd64.whl.

File metadata

  • Download URL: gilmap-0.1.0-cp312-cp312-win_amd64.whl
  • Upload date:
  • Size: 2.0 MB
  • Tags: CPython 3.12, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for gilmap-0.1.0-cp312-cp312-win_amd64.whl
Algorithm Hash digest
SHA256 47f38297968f596eb836320a0cadc4324a5ed3538a71a3fc9ab7cb80c98b0cb7
MD5 66864a4227e7550f3b5e7a35a7df9c24
BLAKE2b-256 01ec2cd0be38ff5a563b239905d39f3359d7a7b92583b3821645524c41ee61f4

See more details on using hashes here.

Provenance

The following attestation bundles were made for gilmap-0.1.0-cp312-cp312-win_amd64.whl:

Publisher: CI.yml on pratikbhadane24/gilmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gilmap-0.1.0-cp312-cp312-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for gilmap-0.1.0-cp312-cp312-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 0450a4f7bde52bc44deb58a1334c7ceee42dccdd5e27a39bcd47cf16dc9f35c9
MD5 593f88450707f90e0c8df34e8e19c226
BLAKE2b-256 da68d7e4974927e1e1633e9f82bdea201d319b7a328aa6e33d2b133c28504013

See more details on using hashes here.

Provenance

The following attestation bundles were made for gilmap-0.1.0-cp312-cp312-musllinux_1_2_x86_64.whl:

Publisher: CI.yml on pratikbhadane24/gilmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gilmap-0.1.0-cp312-cp312-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for gilmap-0.1.0-cp312-cp312-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 9e0d1be3f7c21439ec7d3326db25ad6f7d47c3adae6b48bab886e792104a80ee
MD5 234a8cb31dabf5c33697854475462635
BLAKE2b-256 dddfc864cf2b757138d58f5306532ac376aa1d0bc365e20f5138e7296817305f

See more details on using hashes here.

Provenance

The following attestation bundles were made for gilmap-0.1.0-cp312-cp312-musllinux_1_2_aarch64.whl:

Publisher: CI.yml on pratikbhadane24/gilmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gilmap-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for gilmap-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7d91d9201d7384eca97a10ca28da74069e633bbf0cf83e01a9fe866fb5d24036
MD5 af10fcb2ed35320120af8c176d0c915e
BLAKE2b-256 873e277b058201095ff96a665f6c57a582925ccf86d1de25394ae67b68830b10

See more details on using hashes here.

Provenance

The following attestation bundles were made for gilmap-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: CI.yml on pratikbhadane24/gilmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gilmap-0.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for gilmap-0.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 fd7d23b71a9bbdffc91931b25903375d89998be3305e39045b615c4cefb5b38b
MD5 be89410b2de3037deaadede9a1e8f431
BLAKE2b-256 13515fa51b99e0810de378c21545c135e5efef98472bd5386da64522e43ca10d

See more details on using hashes here.

Provenance

The following attestation bundles were made for gilmap-0.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: CI.yml on pratikbhadane24/gilmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gilmap-0.1.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for gilmap-0.1.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 46c8e47300388c35010a44bcfb96a9a355dd5229fd1d6d105a8ab0136411eaf5
MD5 9930cb994a46d46ccba7b669196a734a
BLAKE2b-256 c6754a1fcbc9de8df87d6c5ef96bd7db26fc989429e6c3350c6391187fbdfccf

See more details on using hashes here.

Provenance

The following attestation bundles were made for gilmap-0.1.0-cp312-cp312-macosx_11_0_arm64.whl:

Publisher: CI.yml on pratikbhadane24/gilmap

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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