AMD's modular, composable GPU communication framework providing RDMA+GPU building blocks (IBGDA, P2P, SDMA collectives) plus turnkey MoE dispatch/combine (MORI-EP), point-to-point IO, and symmetric memory with device-linkable bitcode — deployed across SGLang, vLLM, DeepSpeed, and RTP-LLM on MI300X/MI355X.
| Field | Value |
|---|---|
| Repo URL | https://github.com/ROCm/mori |
| Primary language | C++ (core + HIP kernels) |
| Secondary | Python (bindings, JIT, tuning), CMake (build) |
| License | MIT |
| Stars | 124 |
| Version analyzed | v1.1.1 (2026-04-24) |
| Maintainer | AMD ROCm team |
| First release | v0.1.0 (2026-03-30) |
GPU-side RDMA communication (IBGDA, GPUDirect Async) is hard to adopt: existing libraries are either monolithic (NCCL/RCCL — no composability) or vendor-locked (DeepEP — NVIDIA only). MoE expert-parallel dispatch/combine needs both intra-node XGMI and inter-node RDMA in a single fused kernel, but existing stacks force users to choose one transport. No existing library offers GPU-initiated shmem primitives linkable from Triton or custom HIP kernels.
MORI takes an MLIR-inspired layered approach: composable building blocks (transport backends, topology detection, shmem management) that applications assemble. Five subsystems serve distinct needs:
| Subsystem | Role | Transport |
|---|---|---|
| MORI-EP | MoE dispatch/combine | XGMI + RDMA (5 kernel types) |
| MORI-IO | Point-to-point KV cache transfer | RDMA / XGMI / TCP |
| MORI-CCL | Lightweight collectives | SDMA copy engines |
| MORI-SHMEM | Symmetric memory (OpenSHMEM-style) | All |
| MORI-IR | Device bitcode (50+ extern "C" functions) | N/A (linkable library) |
核心技术壁垒: The device-bitcode layer (MORI-IR) that exposes libmori_shmem_device.bc with 50+ device functions for GPU-initiated communication. This enables any kernel framework (Triton, FlyDSL, raw HIP) to call shmem primitives from within device code, eliminating the host-device round-trip that plagues traditional communication libraries. No competitor offers this level of device-side composability.
Top-level module responsibilities:
SymmMemObjPtr lifecycle — the fundamental symmetric memory allocation visible across all PEs
import mori
# Lazy-loaded submodules:
mori.ops # EP dispatch/combine (EpDispatchCombineConfig, EpDispatchCombineOp)
mori.io # IOEngine, IOEngineSession, IOEngineConfig
mori.shmem # shmem_init, malloc, barrier, ptr_p2p
mori.ir # find_bitcode(), MORI_DEVICE_FUNCTIONS (50+ ABI entries)
mori.ccl # All2allSdma, AllgatherSdma, AllreduceSdma
mori.jax # XLA FFI custom calls
mori.cpp # Raw C++ bindings (AllGatherIntoTensor, DataType)
mori CLI via python/mori/cli.py — registered in pyproject.toml.
| Mechanism | Key | Purpose |
|---|---|---|
| Env var | MORI_PRECOMPILE=1 | AOT-compile all JIT kernels |
| Env var | MORI_EP_LAUNCH_CONFIG_MODE=AUTO | Use pre-tuned launch params |
| Env var | MORI_JIT_CACHE_DIR | Override JIT cache location (default: ~/.mori/jit/) |
| CMake | BUILD_EXAMPLES | Build C++ examples |
| CMake | BUILD_UMBP | Build UMBP subsystem (pulls SPDK) |
| CMake | MORI_WITH_MPI | Enable MPI bootstrap |
| CMake | ENABLE_STANDARD_MOE_ADAPT | DeepEP-compatible API wrappers |
| Config class | EpDispatchCombineConfig | 19-field kernel launch config |
| Config class | IOEngineConfig | IO engine parameters |
| JSON | tuning_configs/*.json | Pre-tuned params keyed by (arch, model, kernel, ep_size, dtype, hidden_dim, tokens) |
IOEngine.create_backend(type) — add new NIC support via backend interfaceEpDispatchCombineKernelType enum — extensible with new kernel implementationsMORI_DEVICE_FUNCTIONS dict — new shmem primitives added as extern "C" entries in bitcodetuning_configs/ for new hardware/model combinationscompile_genco() in jit/core.py accepts arbitrary .cpp → .hsacoEpDispatchCombineConfig (hot path) #include/mori/ops/dispatch_combine/dispatch_combine.hpp + python/mori/ops/dispatch_combine.pykPackedI32Len = 19), serializable to int32_t[19] via ToPackedI32Array()MaxNumTokensToSendPerRank() = maxNumInpTokenPerRank * numExpertPerToken — this bounds all symmetric memory allocationsEpDispatchCombineHandle #include/mori/ops/dispatch_combine/dispatch_combine.hppstd::variant + signal/barrier/index-map buffersSymmMemObjPtr — each visible across all PEs in the group. IntraNode needs 3 buffers; InterNodeV1 needs 5 (adds staging and dispatch input).EpDispatchCombineOp owns the C++ handle via pybind shared_ptrSymmMemObjPtr (foundation) #include/mori/application/application.hppshmem_malloc → registered across all PEs → freed via shmem_freeshmem_ptr_p2p(ptr, my_pe, dest_pe) returns a direct GPU pointer for intra-node (XGMI), or 0 for inter-node (forces RDMA path)IOEngine / IOEngineSession #python/mori/io/engine.py + C++ src/io/allocate_transfer_uid(), used for completion tracking
Python dispatch() call
│
├─ [1] _resolve_launch_params() ← JSON config lookup: (arch, kernel_type, ep_size, dtype, hidden_dim, tokens) → (block_num, rdma_block_num, warp_per_block)
│
├─ [2] mori_cpp.prepare_inference_args() ← C++ binding: sets input/weight/scale/index pointers in handle
│
├─ [3] mori_cpp.build_args() ← Type-erased EpDispatchCombineArgsRaw construction for kernel launch
│
├─ [4] _launch() or _launch_multi() ← HIP kernel launch via JIT-compiled .hsaco module
│ │
│ ├─ IntraNode: 1 kernel (EpDispatchIntraNodeKernel)
│ │ └─ XGMI P2P writes to remote symmetric memory
│ │
│ ├─ InterNodeV1: 2 kernels launched atomically
│ │ ├─ EpDispatchCopyToStaging (XGMI → staging buffer)
│ │ └─ EpDispatchInterNodeV1Kernel (staging → RDMA)
│ │
│ └─ AsyncLL: 3 kernels launched atomically
│ ├─ SlotAssign (token-to-slot mapping)
│ ├─ CopyMultiBlock (multi-block copy to staging)
│ └─ SendTransfer (RDMA transfer via IBGDA)
│
└─ [5] from_gpu_ptr() wraps pre-allocated symmetric memory as torch.Tensor (zero-copy)
Latency budget (EP8 IntraNode, MI355X, 128 tokens):
Memory traffic (EP8, 128 tokens, hidden=7168, FP8):
_ensure_jit_kernels() in EpDispatchCombineOp.__init__
│
├─ detect_gpu_arch() → "gfx942" / "gfx950"
├─ Check ~/.mori/jit/ cache for matching .hsaco
├─ [cache miss] compile_genco(): hipcc .cpp → .hsaco
│ └─ ~5-15s per kernel file
└─ hipModuleLoad() + hipModuleGetFunction() via ctypes
IOEngine.read(local_mem, l_off, remote_mem, r_off, size, uid)
│
├─ Backend dispatch (RDMA / XGMI / TCP)
├─ [RDMA] Post WQE via IBGDA (GPU-initiated, no host involvement)
├─ [XGMI] JIT scatter/gather kernel for non-contiguous buffers
└─ TransferStatus returned (polled via pop_inbound_transfer_status)
Why the dispatch/combine split (not a single all-to-all)?
MoE expert parallelism has asymmetric phases: dispatch fans out tokens to experts (1-to-K), combine collects results back (K-to-1). A generic all-to-all would waste bandwidth on the routing metadata and miss optimization opportunities for each direction (e.g., FP8 dispatch + BF16 combine).
Why 5 kernel types instead of a single adaptive kernel?
The IntraNode→InterNode→InterNodeV1→InterNodeV1LL→AsyncLL hierarchy reflects fundamentally different hardware paths. IntraNode uses direct XGMI P2P writes (no staging). InterNode adds RDMA via staging buffers. InterNodeV1 optimizes RDMA bandwidth. V1LL trades bandwidth for latency. AsyncLL pipelines all three stages. A single kernel cannot efficiently handle all topologies because the synchronization and memory access patterns differ structurally.
Why OpenSHMEM-style API for shmem?
OpenSHMEM is a well-understood PGAS model with clear semantics for symmetric memory allocation and one-sided communication. By matching this API, MORI enables porting from rocshmem with minimal changes and provides familiar semantics for HPC developers.
Why device bitcode (IR) instead of host-side API?
Host-side communication APIs require kernel launch → host callback → communication → kernel relaunch, adding 10-50 μs per round-trip. Device-linkable bitcode enables GPU-initiated communication within a running kernel, which is essential for latency-sensitive MoE dispatch where the entire operation must complete in <40 μs.
Why runtime dlopen for NIC libraries?
A single MORI binary must run on clusters with Mellanox CX7, Broadcom Thor2, or AMD Pollara NICs. Compile-time NIC selection would require separate builds per NIC vendor. Runtime dlopen of libmlx5.so / libbnxt_re.so / libionic.so with auto-detection solves this, enabling universal wheel distribution via PyPI.
MORI requires no hipcc at install time. The C++ host code compiles with a standard compiler (g++/clang++). GPU kernels are .cpp files with HIP annotations that are JIT-compiled to .hsaco (AMD's GPU binary format) at first use via python/mori/jit/core.py:compile_genco(). Results cache to ~/.mori/jit/ keyed by (source hash, arch, compiler flags). This enables pip install amd_mori to work without any GPU SDK on the build machine — a rarity for GPU communication libraries.
Inter-node EP kernels launch 2-4 cooperative GPU kernels in a single _launch_multi() call. For AsyncLL, three kernels run concurrently:
These kernels coordinate through symmetric memory signals and atomic counters without host synchronization.
A two-phase auto-tuning pipeline:
Results stored as JSON in python/mori/ops/tuning_configs/, loaded at runtime via TuningConfigManager. The key space is (gpu_arch, gpu_model, kernel_type, ep_size, phase, dtype, hidden_dim, num_tokens).
EpDispatchCombineConfig serializes to int32_t[19] via ToPackedI32Array(). This allows the entire config to be passed to GPU kernels as a flat argument without pointer chasing or struct layout concerns across host/device ABI boundaries.
shmem_ptr_p2p() returns 0 for cross-node: This is the routing mechanism — kernels check the return value to decide between XGMI direct write (non-zero) and RDMA staging path (zero). A subtle but load-bearing invariant.from_gpu_ptr() zero-copy output wrapping: Dispatch/combine results are not copied to new allocations. Instead, pre-allocated symmetric memory buffers are wrapped as PyTorch tensors via raw pointer. This eliminates a full-tensor copy on every MoE layer forward pass, but means the user must consume results before the next dispatch call overwrites them.mori_shmem_signal_wait_until() (spin-wait on symmetric memory location) and mori_shmem_barrier_all_on_stream() (stream-ordered barrier).shmem_malloc / shmem_malloc_align). Default heaps: 4 GB static, 16 GB VMM. All allocations are registered across all PEs at allocation time. Freed explicitly via shmem_free. No GC; user manages lifetime.MaxNumTokensToRecv()). Buffers are reused across dispatch/combine calls — no per-call allocation.| Platform | Kernel | Dispatch XGMI | Dispatch RDMA | Combine XGMI | Combine RDMA |
|---|---|---|---|---|---|
| MI355X + AINIC | EP8 | 345 GB/s | — | 420 GB/s | — |
| MI355X + AINIC | EP16-V1 | 179 GB/s | 54 GB/s | 234 GB/s | 71 GB/s |
| MI355X + AINIC | EP32-V1 | 85 GB/s | 46 GB/s | 110 GB/s | 61 GB/s |
| MI300X + CX7 | EP8 | 307 GB/s | — | 330 GB/s | — |
| MI300X + CX7 | EP16-V1 | 171 GB/s | 52 GB/s | 219 GB/s | 67 GB/s |
| Platform | Kernel | Dispatch | Combine |
|---|---|---|---|
| MI355X + AINIC | EP8 | 31 μs | 36 μs |
| MI355X + AINIC | EP16-V1-LL | 84 μs | 108 μs |
| MI300X + CX7 | EP8 | 35 μs | 47 μs |
| MI300X + CX7 | EP16-V1-LL | 76 μs | 122 μs |
| Message size | Avg BW | Avg latency |
|---|---|---|
| 1 KB | 3.53 GB/s | 37.1 μs |
| 64 KB | 41.4 GB/s | 202.7 μs |
| 1 MB | 48.3 GB/s | 2777.8 μs |
tests/cpp/; no published coverage numbersBUILD_UMBP=ON| Dimension | MORI | DeepEP | NCCL/RCCL |
|---|---|---|---|
| Vendor | AMD (ROCm) | DeepSeek (NVIDIA) | NVIDIA / AMD |
| GPU support | MI300X, MI325X, MI355X | H800, A100, H100 | All NVIDIA + AMD |
| NIC support | CX7, Thor2, Pollara (runtime dlopen) | CX7 only | CX7 (compiled in) |
| EP dispatch/combine | 5 kernel types, auto-tuned | 3 kernel types (normal, low-latency, low-latency-LL) | N/A (collective-only) |
| Device-linkable bitcode | Yes (50+ functions) | No | No |
| P2P IO engine | Yes (multi-backend sessions) | No | No |
| SDMA collectives | Yes (offloads from CUs) | No | No |
| API style | OpenSHMEM + Python | Python-only EP | C/C++ collective |
| Install | pip install (no hipcc) | pip install (needs CUDA) | System package |
| JIT compilation | .cpp → .hsaco at first use | Precompiled | Precompiled |
| Composability | Modular building blocks | Monolithic EP lib | Monolithic collective lib |
| Framework integrations | SGLang, vLLM, DeepSpeed, RTP-LLM, Triton-distributed | SGLang, vLLM | All major frameworks |
| License | MIT | Apache-2.0 | BSD-3 |
| Maturity | ~2 months public | ~6 months public | ~8 years |
Bold = winner per row.
MORI wins on composability, multi-vendor support, and breadth (EP + IO + CCL + IR in one package). DeepEP wins on NVIDIA ecosystem maturity. RCCL wins on broad collective coverage but lacks EP-specific kernels.
| Step | Claim | Evidence | Validity |
|---|---|---|---|
| 1 | Monolithic communication libraries (NCCL) are suboptimal for MoE EP | EP requires asymmetric dispatch/combine with topology-aware kernel selection; NCCL's AllToAll is symmetric | Valid — MoE token routing is fundamentally 1-to-K, not N-to-N |
| 2 | MLIR-inspired composable building blocks enable better specialization | 5 subsystems (EP, IO, CCL, SHMEM, IR) with independent APIs composable via shared shmem layer | Valid — demonstrated by diverse downstream integrations requiring different subsystem combinations |
| 3 | Device-linkable bitcode eliminates host-device round-trips | MORI-IR provides 50+ extern "C" device functions in .bc format, linkable by Triton/HIP | Valid — Triton-distributed integration demonstrates device-initiated shmem from within Triton kernels |
| 4 | Runtime NIC detection via dlopen enables universal packaging | Single wheel supports CX7+Thor2+Pollara without recompilation | Valid — pip install amd_mori works across NIC vendors; NIC auto-detected at runtime |
| 5 | Multi-kernel atomic launch achieves SOTA inter-node EP | InterNodeV1: 2 kernels (staging copy + RDMA); AsyncLL: 3 kernels (slot assign + copy + transfer) | Valid — 345/420 GB/s dispatch/combine on MI355X EP8 is competitive with DeepEP on H100 |
| Component | Key file | Purpose |
|---|---|---|
| EP Python API | python/mori/ops/dispatch_combine.py | EpDispatchCombineOp.dispatch() / combine() — the user-facing entry points |
| EP C++ handle | include/mori/ops/dispatch_combine/dispatch_combine.hpp | EpDispatchCombineHandle, config, symmetric buffer structs |
| IntraNode kernel | src/ops/dispatch_combine/dispatch_combine.cpp | Core dispatch logic for single-node XGMI |
| InterNodeV1 kernel | src/ops/dispatch_combine/internode_v1.cpp | RDMA + XGMI inter-node dispatch/combine |
| AsyncLL kernel | src/ops/dispatch_combine/low_latency_async.cpp | 3-kernel pipelined transfer |
| JIT pipeline | python/mori/jit/core.py | compile_genco(): .cpp → .hsaco JIT compilation |
| Tuning configs | python/mori/ops/tuning_configs/ | Pre-tuned JSON params per (arch, model, kernel) |
| IO engine | python/mori/io/engine.py | IOEngine / IOEngineSession Python wrappers |
| Shmem API | python/mori/shmem/api.py | OpenSHMEM-style Python bindings |
| IR bitcode | python/mori/ir/bitcode.py | find_bitcode() — locates/JIT-compiles libmori_shmem_device.bc |
| Device functions | python/mori/ir/ops.py | MORI_DEVICE_FUNCTIONS — ABI metadata for 50+ device functions |
| CCL collectives | python/mori/ccl/collective.py | SDMA-based All2All, AllGather, AllReduce |
| Profiler | python/mori/kernel_profiler.py | export_to_perfetto() for MORI-VIZ warp-level traces |
| HIP driver | python/mori/jit/hip_driver.py | Low-level HIP API via ctypes (hipModuleLoad, etc.) |
| Precompile | python/mori/jit/precompile.py | MORI_PRECOMPILE=1 AOT compilation path |
| Pybind entry | src/pybind/pybind.cpp | Python module entry point |
| NIC env tools | tools/env_check.sh, tools/env_setup.sh | AINIC environment validation and RDMA configuration |