Open-source project
openucx/ucx avatar
openucx/ucx

UCX and the four layers between MPI and InfiniBand

Unified Communication X (mailing list - https://elist.ornl.gov/mailman/listinfo/ucx-group)

1,743 stars636 forksCNOASSERTION

At a glance

What is it?
UCX splits communication into a protocol layer, a transport layer, a services layer and a memory layer, so one set of tags can run over InfiniBand, RoCE, CUDA, shared memory or TCP. The trade-off is a large C codebase with failure modes documented down to the glibc version that breaks it.
Who is it for?
Adopt UCX when you are wiring an MPI, OpenSHMEM or PGAS runtime onto RDMA hardware and want one API across fabrics instead of per-fabric code. Skip it for a two-node job on shared memory, for any processor outside x86_64, Power8/9 and Arm v8, or for a cluster whose rdma-core is older than 28.0, because 1.12.0 and above drop InfiniBand and RoCE there entirely.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The four layers that turn tags into fabric traffic

UCX is a C framework that exposes abstract communication primitives so the layers above it can use whatever the machine underneath offers. Two wiki pages cover installing OpenMPI and OpenSHMEM with UCX, and a third covers MPICH, which places the library under a messaging runtime instead of beside it.

The work is split across four components. UCP is the protocol layer, implementing high-level abstractions such as tag-matching, streams, connection negotiation and establishment, multi-rail, and handling different memory types. UCT is the transport layer, implementing low-level communication primitives such as active messages, remote memory access, and atomic operations. UCS is a collection of data structures, algorithms, and system utilities for common use. UCM intercepts memory allocation and deallocation events, and those events drive the memory registration cache.

The example directory makes the split visible rather than theoretical. `ucp_hello_world.c` and `ucp_client_server.c` drive the protocol layer, while `uct_hello_world.c` talks to a transport on its own, which is the fastest way to see what a fabric can do without a message layer in between.

Eight transports behind one set of abstract primitives

A runtime written against UCP does not carry an InfiniBand code path and a separate TCP code path. The transport list spans Infiniband, Omni-Path, RoCE, Cray Gemini and Aries, CUDA, ROCm, shared memory and TCP/IP, and the choice between them happens during connection negotiation and establishment, after configure has already decided which transports are compiled in.

Shared memory alone has five backends, posix, sysv, cma, knem and xpmem, and xpmem lives in its own repository. CUDA and ROCm are named as transports in their own right, right next to UCP's remit of handling different memory types, so a GPU buffer is not a special case bolted on afterwards.

The processor list is much shorter than the fabric list: x86_64, Power8/9 and Arm v8, with nothing else named. That asymmetry is the first filter for anyone planning a port, since the network side is broader than the CPU side by a wide margin.

Three commands install UCX from an unpacked tarball

From the top of an unpacked tree, installing the system is a configure run with a prefix, a parallel build, and an install step.

sh
$ ./contrib/configure-release --prefix=/where/to/install
$ make -j8
$ make install

Replace the prefix with the install directory your MPI build is linked against. A clone of the git repository needs one line in front, because the configure script is generated rather than shipped in the tarball:

sh
$ ./autogen.sh
$ ./contrib/configure-release --prefix=/where/to/install
$ make -j8
$ make install

Compiling support for various networks or other specific hardware may require additional command line flags when running configure, so a build that lacks a fabric is a configure decision rather than a compile error later. The repository layout follows that sequence: `configure.ac`, `buildlib/`, `contrib/` and `Makefile.am` sit at the top level, with `src/` holding the libraries and `test/` the gtest suite.

Developer builds and package builds take different scripts

A developer build configures into a prefix inside the checkout instead of a system directory, and the directory name gives the purpose away:

bash
$ ./autogen.sh
$ ./contrib/configure-devel --prefix=$PWD/install-debug

The warning attached to it matters more than the path. Developer builds of UCX typically include a large performance penalty at run-time because of extra debugging code, and no transport list is given for that build, so any measurement taken from it describes the debug path and nothing else.

Packaging runs off the same tree, through a shell script for RPMs, the Debian rebuild path, and a top-level Doxygen target:

bash
$ contrib/buildrpm.sh -s -b
$ dpkg-buildpackage -us -uc
$ make docs

Run one packaging path or the other for a given node image, since both are driven from the same checkout and no separate staging step is mentioned for either.

ucx_perftest needs two processes on two different cores

The first thing to run after install is the bundled performance test, and it is a two-process job. One side is the server:

sh
$ ./src/tools/perf/ucx_perftest -c 0

The other side connects and asks for tag latency:

sh
$ ./src/tools/perf/ucx_perftest <server-hostname> -t tag_lat -c 1

The `-c` flag sets CPU affinity, and that is the detail people trip over. If both commands run on one host, set the affinity to different CPU cores, because two processes sharing a core measure the core and not the transport.

Internal checks come from the same build tree and need no arguments:

sh
$ make -C test/gtest test

A glibc version window that hangs UCX threads in ucs_rcache_get

Hangs are harder to read than corruption, and UCX names its own: UCX may hang with glibc versions 2.25-2.29 due to known bugs in the pthread_rwlock functions. When such hangs occur, one of the UCX threads gets stuck in pthread_rwlock_rdlock, which is called by ucs_rcache_get, even though no other thread holds the lock.

That final clause is the useful part for anyone with a wedged job. No writer exists, the holder list is empty, and the stack still names a lock acquisition inside the register cache, so a hang there points at the runtime library rather than at a UCX data structure.

Both remedies stay outside UCX: use the glibc version provided with your OS distribution, or build glibc from source using versions less than 2.25 or greater than 2.29. The related glibc report is filed as Bug 23844.

Where 1.8.0 corrupts data and rdma-core v22 removes DC

UCX version 1.8.0 has a bug that may cause data corruption when the TCP transport is used in conjunction with the shared memory transport. Versions released before 1.8.0 do not have it, and the advice is to move to 1.9.0 and above, which leaves 1.8.x as the narrow band where running both transports together is unsafe rather than absent.

A second failure comes from the RDMA stack rather than from UCX. Due to a compatibility flaw with rdma-core v22, the setting below is unsupported and makes the DC transport unavailable, taking it out of service instead of degrading it:

bash
UCX_DC_MLX5_RX_INLINE=0

That was fixed in rdma-core v24 and backported to the rdma-core-22.4-2.el7 rpm, tracked in ucx issue 5749. Routing misreads produce the other classic symptom, peers reported as unreachable, and the documented response is again an environment variable rather than a code change:

bash
UCX_IB_ROCE_REACHABILITY_MODE=all

Where UCX is the wrong tool, and what to check first

UCX is a library you compile into something, not a program you launch, so the first question is whether you have a component that speaks UCP or UCT. The repository points at three integration targets: OpenMPI with OpenSHMEM, and MPICH.

The second question is your fabric stack, and here the answer can be a hard stop. UCX 1.12.0 and above require rdma-core 28.0 or later, or MLNX_OFED 5.0 or later, for InfiniBand and RoCE support, so an old RDMA stack on the node rules out current builds rather than something you configure around. CPU coverage outside x86_64, Power8/9 and Arm v8 is unlisted.

Upgrade cost runs in one direction only. The 1.8.0 corruption note points forward to 1.9.0 and above, and no downgrade or rollback procedure appears anywhere in the README. Check the licence text before shipping anything built on it: GitHub's licence field reads Other, a custom licence the platform cannot classify, and points to the LICENSE file, while the README lists the project as BSD3.

Editorial conclusion

Adopt UCX when you are wiring an MPI, OpenSHMEM or PGAS runtime onto RDMA hardware and want one API across fabrics instead of per-fabric code. Skip it for a two-node job on shared memory, for any processor outside x86_64, Power8/9 and Arm v8, or for a cluster whose rdma-core is older than 28.0, because 1.12.0 and above drop InfiniBand and RoCE there entirely. Before committing, confirm the node's glibc sits outside the 2.25 to 2.29 hang window, and run the perftest with both processes pinned to separate cores so the number means something.

Frequently asked questions

What is UCX and who builds on top of it?

UCX exposes abstract communication primitives that use the best of the available hardware resources and offloads, including RDMA (InfiniBand and RoCE), TCP, GPUs, shared memory, and network atomic operations. Integration wiki pages cover OpenMPI with OpenSHMEM and MPICH, so it sits under a messaging runtime rather than beside one.

How do I install UCX from source?

From the top of the tree, run the release configure script with a prefix, then a parallel make, then make install. A git clone needs the autogen script first, because the configure script is generated. Extra command line flags are often needed at configure time to enable support for a given network or piece of hardware.

Which InfiniBand stack does UCX 1.12.0 require?

UCX 1.12.0 and above require rdma-core 28.0 or later, or MLNX_OFED 5.0 or later, for InfiniBand and RoCE transports. No support matrix pairing older UCX versions with older RDMA stacks is given, so that combination has to be worked out separately.

Can UCX run over TCP on a cluster without InfiniBand?

TCP/IP is a supported transport, and shared memory has five backends of its own: posix, sysv, cma, knem and xpmem. The warning is about combining TCP with shared memory on version 1.8.0, where that pair may cause data corruption.

When was UCX last changed, and is the repository archived?

The last push was on 2026-09-20, and the repository is not archived. The most recent tags listed are v1.22.0 on 2026-08-03, with v1.22.0-rc3 and v1.22.0-rc2 in the weeks before it.

What licence governs code built on UCX?

The README lists BSD3 and links to the LICENSE file, while GitHub's licence field reads Other, described as a custom licence the platform cannot classify. Read the LICENSE file itself rather than trusting either summary, and check the two UCF Consortium policies on membership and export compliant contributions before sending patches.

Official sources

  1. Issues
  2. openucx/ucx on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/openucx-ucx.svg)](https://hysenlabs.com/projects/openucx-ucx)

Was this useful?