fast-ulysses
Ulysses sequence-parallel all-to-all as a torch custom op, moved by the GPU copy engines over NVSHMEM symmetric memory.
Ulysses sequence parallelism shards a long sequence across GPUs: one all-to-all before attention trades the sequence shard for a head shard, a second trades it back. For long-sequence video DiT workloads those two collectives are the critical communication.
This ships that 4D all-to-all as torch.ops.fast_ulysses.all_to_all_single_4d. The transfer is
pitched cudaMemcpy2D/3DAsync straight into peers' symmetric-heap addresses, so it uses no SMs
and runs while compute kernels hold every slot, and the sequence/head relayout is expressed as
copy strides instead of two separate permute kernels.
What it does
- Zero-SM transfer. Remote copies are serialised on one stream; this rank's own share goes on the caller's stream.
- Relayout for free. The addressing is a host-side plan (
a2a_plan.cpp), testable without a GPU, so there is no launch config and no autotune. - Copy by default, borrow explicitly.
all_to_all_single_4dreturns a tensor the caller owns.all_to_all_single_4d_borrowedreturns the symmetric window itself — one copy faster, valid only until the next call with that tag. - Async. Returns an
AsyncCollectiveTensor; the first aten op on it waits by itself.barrier=Falselets several borrowed calls (one layer's q/k/v) share one closing handshake. - Uneven shards. Per-rank
seq_splits/head_splits, which is what lets a caller drop sequence padding. Even splits are the special case, not a separate path. - Reserve, then seal.
reserve()pre-sizes every window the process will use; afterwards an undeclared call raises instead of allocating in the middle of a collective.
Limits: single node, world_size ∈ [1, 8] including odd sizes; float16 / bfloat16;
d * elem_size 16-byte aligned; every pair of GPUs in a group must be P2P-mappable
(fast-ulysses doctor prints the matrix, and an unreachable pair is refused at construction).
Fusion examples (QK RMSNorm + RoPE inside the scatter kernel) live on the
examples/qk-norm-rope-fusion branch.
Results
8 GPUs, one exclusively allocated node per row, same container and the same .so for all four
architectures. base is torch.distributed permute + all_to_all_single + permute; raw is
all_to_all_single alone, without the relayout that makes its result usable. Wan 720p, bf16, ms.
| GPU | fabric | base | ours | vs base | transfer | vs raw |
|---|---|---|---|---|---|---|
| A100-SXM4-80GB | NVLink | 2.865 | 1.670 | 1.72× | 1.227 | 1.12× |
| H200 | NVSwitch | 1.575 | 0.855 | 1.84× | 0.683 | 1.22× |
| B200 | NVLink | 1.193 | 0.554 | 2.15× | 0.402 | 1.22× |
Across three generations the shape is the same: 47–60% of the baseline is relayout that costs us
nothing, and the transfer alone beats a bare all_to_all_single by 1.12–1.37×. The collective
hides essentially completely under a concurrent GEMM chain (86% on B200, ~105% on A100). Dropping
the sequence padding is free (1.00×); the baseline pays 5–8% for the same change.
Full stage-by-stage tables, five machines: docs/BENCHMARK.md.
Where it does not win
A group spanning two CPU sockets on a PCIe machine. Inside one socket the operator is
1.4–2.2×, as on NVLink. Across a socket boundary it is about 0.62× of torch.distributed —
not because our transfer is slow, but because all_to_all_single does not use direct GPU P2P
there. It routes around the socket boundary through the InfiniBand NICs or through host shared
memory; we always write peer memory directly. Deny NCCL that bypass and we are 3.8–4.9× faster on
the same path. fast-ulysses doctor reports when a group spans sockets;
docs/BENCHMARK.md has the measurements.
If you have to run on both kinds of machine, make_group picks for you:
from fast_ulysses import make_group
group = make_group(process_group=dist.group.WORLD, initial_pool_bytes=1 << 30)
# group.fallback is True when it chose torch.distributed because the GPUs span sockets
It returns a TorchUlyssesGroup — the same four entry points on torch.distributed — when the
group spans sockets, and UlyssesGroup otherwise. prefer="fast" / prefer="torch" force it.
The two are bit-exact on every entry point and both shape families, so the caller keeps one code
path; the fallback simply has no overlap to gain and no lifetime rule on its results.
Install
Requires PyTorch 2.10+, CUDA 12.8+ or 13, and sm80 / sm90 / sm100 / sm120. NVSHMEM 3.4.5+
comes from the nvidia-nvshmem-cu1x wheel torch already depends on, so there is nothing to install
separately.
pip install fast-ulysses # newest torch, from PyPI
pip install -e . --no-build-isolation # from source, all four arches
FAST_ULYSSES_CUDA_ARCH=90 pip install -e . --no-build-isolation # one arch, much faster
Wheels for other torch versions, and what to do when the import fails: docs/INSTALL.md.
Quick start
torchrun --nproc_per_node=2 example.py:
import os
import torch
import torch.distributed as dist
from fast_ulysses import UlyssesGroup
dist.init_process_group("nccl")
rank, ws = dist.get_rank(), dist.get_world_size()
lr = int(os.environ.get("LOCAL_RANK", rank))
torch.cuda.set_device(lr)
group = UlyssesGroup(process_group=dist.group.WORLD, initial_pool_bytes=1 << 30)
# mode 0: (b, s_local, n_global, d) -> (b, s_global, n_local, d)
b, s_local, d = 2, 16, 128
x = torch.randn(b, s_local, 4 * ws, d, dtype=torch.bfloat16, device=f"cuda:{lr}")
# Every rank must issue the same (shape, mode, tag) sequence.
out = group.all_to_all_single_4d(x, mode=0, tag="demo")
assert out.shape == (b, s_local * ws, 4, d)
group.destroy()
dist.destroy_process_group()
API
| API | Summary |
|---|---|
UlyssesGroup(process_group=None, device=None, initial_pool_bytes=2<<30) |
Collective: NVSHMEM init + symmetric-heap pool. |
group.reserve(calls, *, allow_growth=False) |
Pre-size every window, then seal. |
group.all_to_all_single_4d(x, *, mode=0, tag="", out=None) |
The default. Returns a tensor the caller owns. |
group.all_to_all_single_4d_borrowed(x, *, mode=0, tag="") |
No copy-out; the result IS the window. |
group.all_to_all_single_4d_async(...) |
The default op on a high-priority comm stream. |
group.all_to_all_single_4d_borrowed_async(..., barrier=True) |
The borrowed op on that stream. |
group.destroy() |
Release symmetric-heap resources (collective). |
fast-ulysses doctor |
Build, devices, P2P matrix, socket layout. |
Why the code is shaped this way, and what it rests on that is not guaranteed: docs/DESIGN.md.
Shapes, tag semantics, the barrier ordering contract, and the collective hard constraints — violating the rank-uniform call sequence hangs the group: docs/API.md.
Testing
pytest # auto-skips below 2 GPUs
torchrun --nproc_per_node=8 tests/distributed/a2a_correctness.py # one worker directly
Development setup: docs/DEVELOP.md.
License
Apache-2.0. See LICENSE.
Metadata
Release files for fast-ulysses 0.1.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 | |
|---|---|---|---|
| fast_ulysses-0.1.0.tar.gz | 86.8 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| fast_ulysses-0.1.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl | CPython 3.13 | CPython 3.13 | Linux glibc 2.24+ x86-64, Linux glibc 2.28+ x86-64 | Details |
| fast_ulysses-0.1.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl | CPython 3.12 | CPython 3.12 | Linux glibc 2.28+ x86-64, Linux glibc 2.24+ x86-64 | Details |
| fast_ulysses-0.1.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl | CPython 3.11 | CPython 3.11 | Linux glibc 2.24+ x86-64, Linux glibc 2.28+ x86-64 | Details |
| fast_ulysses-0.1.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl | CPython 3.10 | CPython 3.10 | Linux glibc 2.24+ x86-64, Linux glibc 2.28+ x86-64 | Details |
Total release size: 1.2 MB
Release files / fast_ulysses-0.1.0.tar.gz
| Download URL | fast_ulysses-0.1.0.tar.gz |
|---|---|
| Size | 86.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b71802de269918b45fb10169e49cd185e8f32724af694c4a67689d3c4c030d27
|
|
BLAKE2b-256 checksum How to use checksums |
90db112d1ede21d8d2ac4d0d69b70222dcce81a8ac9f5dd642d8dd1ced54f3c4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 8, 2026.
Transparency logRelease files / fast_ulysses-0.1.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
| Download URL | fast_ulysses-0.1.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 281.4 kB |
| Tags | CPython 3.13 Linux glibc 2.24+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
59ebb062513162f169c670aae395e3769d49706a3a7fd8ce14935e5cfc0e559c
|
|
BLAKE2b-256 checksum How to use checksums |
93d19132c172e0bdd36e42594d55989b4c0d69ed33e535404d4b99dfdc0ef39b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 8, 2026.
Transparency logRelease files / fast_ulysses-0.1.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
| Download URL | fast_ulysses-0.1.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 281.3 kB |
| Tags | CPython 3.12 Linux glibc 2.24+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
8b4643cf8424e94673f814c1611b09f4384c87d7dcfab212e9392df70f0f32ff
|
|
BLAKE2b-256 checksum How to use checksums |
a41aa2ff274b0d95f0c374942c04e94f64330c9250402e6c902ab431f95561d5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 8, 2026.
Transparency logRelease files / fast_ulysses-0.1.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
| Download URL | fast_ulysses-0.1.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 280.6 kB |
| Tags | CPython 3.11 Linux glibc 2.24+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
8118fd9ae8408e69cf5ecd07eb3e6e5b6a95508c0928481e964c08ab2b763130
|
|
BLAKE2b-256 checksum How to use checksums |
6e02519419ed9691cbf773a485fef94fe5841ebd4819306cd0aa6018644b2ca1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 8, 2026.
Transparency logRelease files / fast_ulysses-0.1.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
| Download URL | fast_ulysses-0.1.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 279.5 kB |
| Tags | CPython 3.10 Linux glibc 2.24+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
89489db719377d7e2145622d4057c200714b5fda2494bb81da0554f43469d595
|
|
BLAKE2b-256 checksum How to use checksums |
3914d59e6a04d11c9d2557552b2627f96a6a87b70099ce52f49d8c3be0270f82
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 8, 2026.
Transparency log