mpsify
Run CUDA-hardcoded PyTorch repos on Apple Silicon (MPS) — with zero source edits.
You inherit someone's training script. It's full of .cuda(), device='cuda',
torch.cuda.amp, map_location='cuda'. PyTorch-MPS could run the actual math
fine on your Mac — but the code dies before it gets the chance. mpsify patches
torch at import time so all of that transparently retargets to MPS.
pip install mpsify
Usage
Point it at any script — no edits to their code:
python -m mpsify train.py --epochs 10 --lr 1e-3
# or, after install, the console script:
mpsify train.py --epochs 10 --lr 1e-3
Or drop one line at the top of your entry file:
import mpsify # patches torch on import
The wrapper (python -m mpsify) is preferred: it sets
PYTORCH_ENABLE_MPS_FALLBACK=1 before torch loads, which the import form
can't always guarantee.
What it does
| CUDA thing | Retargeted to |
|---|---|
torch.cuda.is_available() |
True |
.cuda(), .to('cuda'), device='cuda' |
mps |
torch.device('cuda') |
mps |
torch.load(map_location='cuda') |
mps |
DataLoader(pin_memory=True) |
pin_memory=False (no pinned memory on MPS) |
torch.cuda.amp autocast / GradScaler |
no-op, fp32 (see knobs) |
nn.DataParallel |
identity (single device) |
nccl backend |
gloo |
Ops with no Metal kernel fall back to CPU automatically. mpsify catches those
fallbacks, warns once per op live, and prints a summary at exit — so you can
see exactly which ops are your latency hot spots.
Libraries with no Metal backend at all (bitsandbytes, apex, deepspeed,
flash_attn, triton) are detected and reported loudly instead of crashing
cryptically.
Pre-flight check
See what mpsify will do to a script without running it:
python -m mpsify doctor train.py
Reports every CUDA call it will remap and any library it can't fix (flash-attention, bitsandbytes, ...). Exit code is non-zero if something is unshimmable — handy in CI.
Loading a CUDA-trained checkpoint
Weights are just numbers — a model trained on CUDA runs on MPS unchanged. The
only snag is torch.load restoring tensors to their saved cuda device.
mpsify.load handles that (and upcasts fp16 → fp32, since MPS half-precision
has rough edges):
import mpsify
state = mpsify.load("model_trained_on_a100.pth") # lands on MPS
model.load_state_dict(state)
Diagnosing slow ops
python -m mpsify --profile train.py
Runs a dispatch-level profiler that counts calls and times ops. This adds per-op overhead — use it for a diagnostic pass, not production.
Knobs
| Env var | Effect |
|---|---|
MPSIFY_AMP=1 |
Re-enable AMP/autocast (default off = fp32, correct but slower). AMP on MPS is where correctness gets dicey. |
MPSIFY_QUIET=1 |
Suppress live fallback warnings; keep the exit summary. |
Tested on
Real pretrained models, output verified numerically identical to CPU (max abs diff ~1e-6) on macOS + torch 2.5.1:
- torchvision
resnet18,efficientnet_b0,vit_b_16 - timm
mobilenetv3_small_100 - HuggingFace
transformers— DistilBERT sentiment:.to('cuda'), forward, andpipeline(device=0)all run on MPS - A 3-step fine-tuning loop (loss decreases, params on
mps:0) - Loading fp16/CUDA checkpoints via
mpsify.load
Note:
transformers5.x refuses totorch.loada.bincheckpoint unless torch ≥ 2.6 (a CVE guard). Use models that shipsafetensors(use_safetensors=True) — unrelated to mpsify.
Scope
Handles pure-PyTorch repos (torchvision / timm models — ResNet, EfficientNet,
ViT, etc.). It does not translate custom .cu/Triton kernels or make
CUDA-only libraries (flash-attention, DeepSpeed, apex) actually work — those are
detected and reported, not fixed.
License
MIT
Metadata
Release files for mpsify 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mpsify-0.2.0.tar.gz | 12.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mpsify-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.7 kB
Release files / mpsify-0.2.0.tar.gz
| Download URL | mpsify-0.2.0.tar.gz |
|---|---|
| Size | 12.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7ee0d7c1088249073f2467f89035cabbaebc393298abff26f3d8e92ed73c4b73
|
|
BLAKE2b-256 checksum How to use checksums |
dbe14801f64f0203704a4fa50de7e2821be0faee6d2addff390dce034be69652
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / mpsify-0.2.0-py3-none-any.whl
| Download URL | mpsify-0.2.0-py3-none-any.whl |
|---|---|
| Size | 10.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0eac782accfbb8b26431315cd369e4f9f93e89b03b764d740e9615f8abaa0bda
|
|
BLAKE2b-256 checksum How to use checksums |
e640813c70e2f057deacba701b905c3cb4a4cf210bedcd224c954cb3d410e9d9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|