[CCF Archive] Store object type eviction policy submission #3

Closed
kancel wants to merge 382 commits from kancel:ccf-archive-pr2746 into main
6 changed files with 569 additions and 109 deletions
Showing only changes of commit af26a48c03 - Show all commits

View File

@ -860,6 +860,18 @@ jobs:
uses: ./.github/workflows/ci_cu13.yml
secrets: inherit
build-wheel-efa:
needs: [spell-check, clang-format, check-paths]
if: >-
(needs.check-paths.outputs.should-run-downstream == 'true' ||
github.event_name == 'workflow_dispatch') &&
(github.event_name == 'push' ||
github.event_name == 'workflow_dispatch' ||
github.event.action == 'opened' ||
contains(github.event.pull_request.labels.*.name, 'run-ci'))
uses: ./.github/workflows/ci_efa.yml
secrets: inherit
ascend-test:
needs: [build, check-paths]
if: needs.check-paths.outputs.should-run-downstream == 'true'
@ -885,6 +897,7 @@ jobs:
- build-docker
- test-wheel-ubuntu
- build-wheel-cu13
- build-wheel-efa
- ascend-test
- integration-test
runs-on: ubuntu-latest

154
.github/workflows/ci_efa.yml vendored Normal file
View File

@ -0,0 +1,154 @@
name: 'Build Wheel (AWS EFA)'
on:
workflow_call: {}
# Builds the AWS EFA (libfabric) wheel variants on a stock ubuntu runner.
# No EFA hardware is required to *build*: USE_EFA only needs the libfabric
# headers/lib to compile and link. auditwheel later excludes libfabric/libefa
# from the wheel so they resolve to the user's system EFA install
# (/opt/amazon/efa/lib) at runtime. The distro libfabric (1.x) is ABI-forward-
# compatible with the AWS EFA libfabric (2.x) that loads at runtime; the EFA
# transport only uses long-stable fi_* core APIs.
#
# Two variants, since the EFA transport's memory path is CUDA-aware
# (FI_HMEM_CUDA / GPUDirect under USE_CUDA=ON, FI_HMEM=system otherwise):
# efa USE_CUDA=ON (GPU)
# efa-non-cuda USE_CUDA=OFF (CPU/DRAM)
# PR validation builds one python version per variant to keep CI cheap;
# the release workflow builds the full python matrix.
jobs:
build-wheel-efa:
runs-on: ubuntu-22.04
strategy:
matrix:
include:
- variant: cuda
use_cuda: "ON"
build_env: "EFA_BUILD"
python-version: "3.12"
- variant: non-cuda
use_cuda: "OFF"
build_env: "EFA_NON_CUDA_BUILD"
python-version: "3.10"
env:
TORCH_CUDA_ARCH_LIST: "8.0;9.0"
SCCACHE_GHA_ENABLED: "true"
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Free up disk space
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo rm -rf /usr/local/lib/android
df -h
- name: Install CUDA Toolkit
if: matrix.use_cuda == 'ON'
uses: Jimver/cuda-toolkit@v0.2.24
with:
cuda: '12.8.1'
method: 'network'
sub-packages: '["nvcc", "nvrtc-dev"]'
non-cuda-sub-packages: '["libcusparse-dev", "libcublas-dev", "libcusolver-dev"]'
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.9
- name: Configure sccache
uses: actions/github-script@v7
with:
script: |
core.exportVariable('ACTIONS_RESULTS_URL', process.env.ACTIONS_RESULTS_URL || '');
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Install dependencies
run: |
sudo apt update -y
sudo apt install -y ninja-build libfabric-dev libfabric1
sudo bash -x dependencies.sh -y
df -h
shell: bash
- name: Configure project
run: |
mkdir build
cd build
EXTRA_FLAGS=""
if [ "${{ matrix.use_cuda }}" = "ON" ]; then
EXTRA_FLAGS="-DCMAKE_EXE_LINKER_FLAGS=-L/usr/local/cuda/lib64/stubs"
fi
cmake -G Ninja .. \
-DUSE_ETCD=ON \
-DUSE_HTTP=ON \
-DWITH_STORE=ON \
-DWITH_METRICS=ON \
-DBUILD_UNIT_TESTS=OFF \
-DBUILD_EXAMPLES=ON \
-DENABLE_SCCACHE=ON \
-DBUILD_BENCHMARK=ON \
-DUSE_EFA=ON \
-DUSE_CUDA=${{ matrix.use_cuda }} \
-DLIBFABRIC_INCLUDE_DIR=/usr/include \
-DLIBFABRIC_LIBRARY=/usr/lib/x86_64-linux-gnu/libfabric.so \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_DEBUG_SYMBOLS=OFF \
$EXTRA_FLAGS
shell: bash
- name: Build project
run: |
if [ "${{ matrix.use_cuda }}" = "ON" ]; then
export LD_LIBRARY_PATH=/usr/local/cuda/lib64/stubs:$LD_LIBRARY_PATH
export LIBRARY_PATH=/usr/local/cuda/lib64/stubs:$LIBRARY_PATH
fi
cd build
cmake --build .
sudo cmake --install .
df -h
shell: bash
- name: Run sccache stat for check
if: ${{ env.SCCACHE_PATH != '' }}
shell: bash
run: ${SCCACHE_PATH} --show-stats
- name: Generate Python version tag
id: generate_tag
run: |
echo "python_version_tag=$(echo ${{ matrix.python-version }} | tr -d '.')" >> $GITHUB_OUTPUT
shell: bash
- name: Build Python wheel
run: |
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
export ${{ matrix.build_env }}=1
PYTHON_VERSION=${{ matrix.python-version }} OUTPUT_DIR=dist-py${{ steps.generate_tag.outputs.python_version_tag }} ./scripts/build_wheel.sh
shell: bash
- name: Verify libfabric is excluded from the wheel
run: |
WHL=$(ls mooncake-wheel/dist-py${{ steps.generate_tag.outputs.python_version_tag }}/*.whl | head -1)
echo "Inspecting $WHL"
if unzip -l "$WHL" | grep -iE 'libfabric|libefa'; then
echo "::error::libfabric/libefa must NOT be bundled in the EFA wheel"
exit 1
fi
echo "OK: libfabric/libefa correctly excluded (resolve to system EFA at runtime)"
shell: bash
- name: Upload Python wheel artifact
uses: actions/upload-artifact@v4
with:
name: mooncake-wheel-efa-${{ matrix.variant }}-ubuntu-py${{ steps.generate_tag.outputs.python_version_tag }}
path: mooncake-wheel/dist-py${{ steps.generate_tag.outputs.python_version_tag }}/*.whl

View File

@ -0,0 +1,155 @@
name: Release EFA Non-CUDA
on:
push:
tags:
- 'v*'
# Publishes the AWS EFA (libfabric) non-CUDA wheel variant:
# mooncake-transfer-engine-efa-non-cuda USE_EFA=ON USE_CUDA=OFF (CPU/DRAM only)
# The CUDA variant (mooncake-transfer-engine-efa) is built by release-efa.yaml
# — split into its own workflow because each PyPI package publishes from a
# dedicated release workflow (trusted publisher / artifact pattern is
# per-package), mirroring release.yaml vs release-non-cuda.yaml.
#
# No EFA hardware is needed to build: USE_EFA only needs libfabric headers/lib
# to compile/link. auditwheel excludes libfabric/libefa from the wheel so they
# resolve to the user's system AWS EFA install (/opt/amazon/efa/lib) at
# runtime. The distro libfabric (1.x) used to build is ABI-forward-compatible
# with the AWS EFA libfabric (2.x) loaded at runtime; the EFA transport uses
# only long-stable fi_* core APIs.
env:
SCCACHE_GHA_ENABLED: "true"
jobs:
build:
runs-on: ubuntu-22.04
permissions:
contents: write
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13']
env:
EFA_NON_CUDA_BUILD: "1"
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Free up disk space
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo rm -rf /usr/local/lib/android
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.9
- name: Configure sccache
uses: actions/github-script@v7
with:
script: |
core.exportVariable('ACTIONS_RESULTS_URL', process.env.ACTIONS_RESULTS_URL || '');
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Configure project
run: |
sudo apt update -y
sudo apt install -y libfabric-dev libfabric1
sudo bash -x dependencies.sh -y
mkdir build
cd build
cmake .. \
-DUSE_HTTP=ON \
-DUSE_ETCD=ON \
-DUSE_CUDA=OFF \
-DWITH_EP=OFF \
-DSTORE_USE_ETCD=ON \
-DUSE_EFA=ON \
-DLIBFABRIC_INCLUDE_DIR=/usr/include \
-DLIBFABRIC_LIBRARY=/usr/lib/x86_64-linux-gnu/libfabric.so \
-DENABLE_SCCACHE=ON \
-DCMAKE_BUILD_TYPE=Release
shell: bash
- name: Build project
run: |
cd build
make -j
sudo make install
shell: bash
- name: Run sccache stat for check
if: ${{ env.SCCACHE_PATH != '' }}
shell: bash
run: ${SCCACHE_PATH} --show-stats
- name: Generate Python version tag
id: generate_tag_release
run: |
echo "python_version_tag=$(echo ${{ matrix.python-version }} | tr -d '.')" >> $GITHUB_OUTPUT
shell: bash
- name: Build Python wheel
run: |
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
PYTHON_VERSION=${{ matrix.python-version }} OUTPUT_DIR=dist-py${{ steps.generate_tag_release.outputs.python_version_tag }} ./scripts/build_wheel.sh
env:
VERSION: ${{ env.VERSION }}
- name: Verify libfabric is excluded from the wheel
run: |
WHL=$(ls mooncake-wheel/dist-py${{ steps.generate_tag_release.outputs.python_version_tag }}/*.whl | head -1)
echo "Inspecting $WHL"
if unzip -l "$WHL" | grep -iE 'libfabric|libefa'; then
echo "::error::libfabric/libefa must NOT be bundled in the EFA wheel"
exit 1
fi
echo "OK: libfabric/libefa correctly excluded"
shell: bash
- name: Upload Python wheel artifact
uses: actions/upload-artifact@v4
with:
name: mooncake-wheel-efa-non-cuda-py${{ steps.generate_tag_release.outputs.python_version_tag }}
path: mooncake-wheel/dist-py${{ steps.generate_tag_release.outputs.python_version_tag }}/*.whl
publish-release:
if: ${{ !contains(github.ref_name, '-') }}
needs: build
runs-on: ubuntu-22.04
permissions:
contents: write
id-token: write
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Download all wheel artifacts
uses: actions/download-artifact@v4
with:
path: mooncake-wheel/dist-all
pattern: mooncake-wheel-efa-non-cuda-py*
- name: Prepare wheels for release
run: |
mkdir -p mooncake-wheel/dist-release
find mooncake-wheel/dist-all -name "*.whl" -exec cp {} mooncake-wheel/dist-release/ \;
echo "Collected wheels for release:"
ls -la mooncake-wheel/dist-release/
- name: Upload wheels to GitHub Release
uses: softprops/action-gh-release@v1
with:
files: mooncake-wheel/dist-release/*.whl
- name: Publish package to PyPI
if: github.repository == 'kvcache-ai/Mooncake'
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: mooncake-wheel/dist-release/
password: ${{ secrets.PYPI_API_TOKEN }}

166
.github/workflows/release-efa.yaml vendored Normal file
View File

@ -0,0 +1,166 @@
name: Release EFA
on:
push:
tags:
- 'v*'
# Publishes the AWS EFA (libfabric) CUDA wheel variant:
# mooncake-transfer-engine-efa USE_EFA=ON USE_CUDA=ON (GPU, GPUDirect/FI_HMEM_CUDA)
# The non-CUDA variant (mooncake-transfer-engine-efa-non-cuda) is built by
# release-efa-non-cuda.yaml — split into its own workflow because each PyPI
# package publishes from a dedicated release workflow (trusted publisher /
# artifact pattern is per-package), mirroring release.yaml vs release-non-cuda.yaml.
#
# No EFA hardware is needed to build: USE_EFA only needs libfabric headers/lib
# to compile/link. auditwheel excludes libfabric/libefa from the wheel so they
# resolve to the user's system AWS EFA install (/opt/amazon/efa/lib) at
# runtime. The distro libfabric (1.x) used to build is ABI-forward-compatible
# with the AWS EFA libfabric (2.x) loaded at runtime; the EFA transport uses
# only long-stable fi_* core APIs.
env:
SCCACHE_GHA_ENABLED: "true"
jobs:
build:
runs-on: ubuntu-22.04
permissions:
contents: write
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13']
env:
EFA_BUILD: "1"
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Free up disk space
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo rm -rf /usr/local/lib/android
- name: Install CUDA Toolkit
uses: Jimver/cuda-toolkit@v0.2.24
with:
cuda: '12.8.1'
method: 'network'
sub-packages: '["nvcc", "nvrtc-dev"]'
non-cuda-sub-packages: '["libcusparse-dev", "libcublas-dev", "libcusolver-dev"]'
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.9
- name: Configure sccache
uses: actions/github-script@v7
with:
script: |
core.exportVariable('ACTIONS_RESULTS_URL', process.env.ACTIONS_RESULTS_URL || '');
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Configure project
run: |
sudo apt update -y
sudo apt install -y libfabric-dev libfabric1
sudo bash -x dependencies.sh -y
mkdir build
cd build
cmake .. \
-DUSE_HTTP=ON \
-DUSE_ETCD=ON \
-DUSE_CUDA=ON \
-DWITH_EP=OFF \
-DSTORE_USE_ETCD=ON \
-DUSE_EFA=ON \
-DLIBFABRIC_INCLUDE_DIR=/usr/include \
-DLIBFABRIC_LIBRARY=/usr/lib/x86_64-linux-gnu/libfabric.so \
-DENABLE_SCCACHE=ON \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_EXE_LINKER_FLAGS=-L/usr/local/cuda/lib64/stubs
shell: bash
- name: Build project
run: |
export LD_LIBRARY_PATH=/usr/local/cuda/lib64/stubs:$LD_LIBRARY_PATH
export LIBRARY_PATH=/usr/local/cuda/lib64/stubs:$LIBRARY_PATH
cd build
make -j
sudo make install
shell: bash
- name: Run sccache stat for check
if: ${{ env.SCCACHE_PATH != '' }}
shell: bash
run: ${SCCACHE_PATH} --show-stats
- name: Generate Python version tag
id: generate_tag_release
run: |
echo "python_version_tag=$(echo ${{ matrix.python-version }} | tr -d '.')" >> $GITHUB_OUTPUT
shell: bash
- name: Build Python wheel
run: |
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
PYTHON_VERSION=${{ matrix.python-version }} OUTPUT_DIR=dist-py${{ steps.generate_tag_release.outputs.python_version_tag }} ./scripts/build_wheel.sh
env:
VERSION: ${{ env.VERSION }}
- name: Verify libfabric is excluded from the wheel
run: |
WHL=$(ls mooncake-wheel/dist-py${{ steps.generate_tag_release.outputs.python_version_tag }}/*.whl | head -1)
echo "Inspecting $WHL"
if unzip -l "$WHL" | grep -iE 'libfabric|libefa'; then
echo "::error::libfabric/libefa must NOT be bundled in the EFA wheel"
exit 1
fi
echo "OK: libfabric/libefa correctly excluded"
shell: bash
- name: Upload Python wheel artifact
uses: actions/upload-artifact@v4
with:
name: mooncake-wheel-efa-py${{ steps.generate_tag_release.outputs.python_version_tag }}
path: mooncake-wheel/dist-py${{ steps.generate_tag_release.outputs.python_version_tag }}/*.whl
publish-release:
if: ${{ !contains(github.ref_name, '-') }}
needs: build
runs-on: ubuntu-22.04
permissions:
contents: write
id-token: write
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Download all wheel artifacts
uses: actions/download-artifact@v4
with:
path: mooncake-wheel/dist-all
pattern: mooncake-wheel-efa-py*
- name: Prepare wheels for release
run: |
mkdir -p mooncake-wheel/dist-release
find mooncake-wheel/dist-all -name "*.whl" -exec cp {} mooncake-wheel/dist-release/ \;
echo "Collected wheels for release:"
ls -la mooncake-wheel/dist-release/
- name: Upload wheels to GitHub Release
uses: softprops/action-gh-release@v1
with:
files: mooncake-wheel/dist-release/*.whl
- name: Publish package to PyPI
if: github.repository == 'kvcache-ai/Mooncake'
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: mooncake-wheel/dist-release/
password: ${{ secrets.PYPI_API_TOKEN }}

View File

@ -4,6 +4,7 @@ This document describes how to build and use Mooncake with AWS Elastic Fabric Ad
## Prerequisites
(efa-prerequisites-driver)=
### 1. AWS EFA Driver and libfabric
EFA driver and libfabric should be pre-installed on AWS instances with EFA support (e.g., p6-b300.48xlarge, p6-b200.48xlarge, p5en.48xlarge, p5e.48xlarge, p5.48xlarge).
@ -34,6 +35,22 @@ This installs all system packages, git submodules (including pybind11 and yalant
> **Note:** The EFA driver and libfabric are **not** installed by `dependencies.sh`. They must be pre-installed on the instance (see section 1 above).
## Installing from PyPI (recommended)
Pre-built EFA wheels are published to PyPI by the official release pipeline, so most users do not need to build from source. The EFA transport's memory path is CUDA-aware, so two variants are published:
```bash
# GPU memory transfers (e.g., KV cache in vLLM) — built with USE_CUDA=ON
pip install mooncake-transfer-engine-efa
# CPU/DRAM-only transfers — built with USE_CUDA=OFF
pip install mooncake-transfer-engine-efa-non-cuda
```
> **Note:** These wheels deliberately do **not** bundle `libfabric`/`libefa` (see the runtime note in [Building a Distributable Wheel](#efa-distributable-wheel)). They resolve to the system AWS EFA installation at runtime, so the EFA driver and libfabric from the [Prerequisites](#efa-prerequisites-driver) must still be present on the instance. Make sure `/opt/amazon/efa/lib` is on `LD_LIBRARY_PATH`.
To build from source instead (for development, an unreleased revision, or a custom configuration), follow the sections below.
## Building Mooncake with EFA Support
### 1. Build with EFA Enabled
@ -80,11 +97,10 @@ cp mooncake-common/libasio.so ../mooncake-wheel/mooncake/
pip install -e ../mooncake-wheel --no-build-isolation
```
(efa-distributable-wheel)=
### 3. Building a Distributable Wheel (optional)
To produce a relocatable wheel for distribution (instead of the editable
install above), use `scripts/build_wheel.sh`, which runs `auditwheel
repair` to bundle non-system dependencies:
To produce a relocatable wheel for distribution (instead of the editable install above), use `scripts/build_wheel.sh`, which runs `auditwheel repair` to bundle non-system dependencies:
```bash
# After the cmake/make build above completes:
@ -92,21 +108,19 @@ PYTHON_VERSION=3.13 BUILD_DIR=build bash scripts/build_wheel.sh 3.13 dist
pip install dist/mooncake_transfer_engine-*.whl
```
> **Important (EFA builds):** `auditwheel repair` excludes `libfabric`
> and `libefa` from the wheel so they resolve to the system EFA
> installation (`/opt/amazon/efa/lib`) at runtime. This is required
> because the in-process `aws-ofi-nccl` plugin (loaded by NCCL) links the
> **same** system `libfabric`. If the wheel bundled its own copy, the
> process would load two independent libfabric instances — Mooncake's
> bundled one and NCCL's system one — and whichever initializes first
> claims the EFA device, leaving the other with an empty provider list
> (`fi_getinfo: provider efa output empty list`). NCCL then silently
> falls back to the TCP provider and cross-node collectives such as
> `all_gather_object` hang. Excluding libfabric/libefa (see
> `scripts/build_wheel.sh`) keeps a single shared libfabric in the
> process. If you are on an older Mooncake build whose wheel still bundles
> libfabric, force the system copy with
> `export LD_PRELOAD=/opt/amazon/efa/lib/libfabric.so.1` as a workaround.
To produce a wheel whose package name matches the published variants (`mooncake-transfer-engine-efa` / `-efa-non-cuda`), set the corresponding build-variant environment variable — this is exactly what the release pipeline does:
```bash
# GPU build (cmake was configured with USE_CUDA=ON):
EFA_BUILD=1 PYTHON_VERSION=3.13 BUILD_DIR=build bash scripts/build_wheel.sh 3.13 dist
# CPU build (cmake was configured with USE_CUDA=OFF):
EFA_NON_CUDA_BUILD=1 PYTHON_VERSION=3.13 BUILD_DIR=build bash scripts/build_wheel.sh 3.13 dist
```
> **CI/CD:** EFA wheels are built and published automatically — see `.github/workflows/ci_efa.yml` (per-PR build validation) and `.github/workflows/release-efa.yaml` (tagged release to GitHub Release + PyPI). No EFA hardware is required to *build* the wheel: only the libfabric headers/library are needed to compile and link, which the CI runner obtains from the distro `libfabric-dev` package.
> **Important (EFA builds):** `auditwheel repair` excludes `libfabric` and `libefa` from the wheel so they resolve to the system EFA installation (`/opt/amazon/efa/lib`) at runtime. This is required because the in-process `aws-ofi-nccl` plugin (loaded by NCCL) links the **same** system `libfabric`. If the wheel bundled its own copy, the process would load two independent libfabric instances — Mooncake's bundled one and NCCL's system one — and whichever initializes first claims the EFA device, leaving the other with an empty provider list (`fi_getinfo: provider efa output empty list`). NCCL then silently falls back to the TCP provider and cross-node collectives such as `all_gather_object` hang. Excluding libfabric/libefa (see `scripts/build_wheel.sh`) keeps a single shared libfabric in the process. If you are on an older Mooncake build whose wheel still bundles libfabric, force the system copy with `export LD_PRELOAD=/opt/amazon/efa/lib/libfabric.so.1` as a workaround.
## Verification
@ -158,18 +172,10 @@ cd build && ctest --output-on-failure -R 'efa'
Use `transfer_engine_bench` to measure EFA transport throughput between two nodes.
The following commands are the GPU-to-GPU configuration that produces
the headline numbers in the [Benchmark Results](#benchmark-results)
tables (≈ 350 GB/s write on a p5en.48xlarge pair, ≈ 302 GB/s on
p6-b200.48xlarge). Two things matter the most:
The following commands are the GPU-to-GPU configuration that produces the headline numbers in the [Benchmark Results](#benchmark-results) tables (≈ 350 GB/s write on a p5en.48xlarge pair, ≈ 302 GB/s on p6-b200.48xlarge). Two things matter the most:
- `--gpu_id=-1` on **both** sides — this fans buffers across every GPU,
which in turn lets both NUMA nodes' NICs saturate. Pinning a single
GPU (the default `--gpu_id=0`) halves throughput because half the
NICs end up cross-NUMA.
- `--block_size=1048576` (1MB, not the 64 KB default) — each block
becomes one `fi_write` / `fi_read`, so larger blocks amortize
per-op overhead and are the main knob for hitting line rate.
- `--gpu_id=-1` on **both** sides — this fans buffers across every GPU, which in turn lets both NUMA nodes' NICs saturate. Pinning a single GPU (the default `--gpu_id=0`) halves throughput because half the NICs end up cross-NUMA.
- `--block_size=1048576` (1MB, not the 64 KB default) — each block becomes one `fi_write` / `fi_read`, so larger blocks amortize per-op overhead and are the main knob for hitting line rate.
### 1. Target Node (receiver)
@ -182,9 +188,7 @@ p6-b200.48xlarge). Two things matter the most:
--gpu_id=-1
```
`--buffer_size` must be at least as large as the initiator's
`--buffer_size` — the initiator writes into offsets `[0, buffer_size)`
on the target, so keep these in sync.
`--buffer_size` must be at least as large as the initiator's `--buffer_size` — the initiator writes into offsets `[0, buffer_size)` on the target, so keep these in sync.
### 2. Initiator Node (sender)
@ -204,8 +208,7 @@ on the target, so keep these in sync.
--report_unit=GB
```
Replace `<target_hostname>:<target_port>` with the target node's
address shown in the target's startup log (e.g., `ip-172-31-29-226:12345`).
Replace `<target_hostname>:<target_port>` with the target node's address shown in the target's startup log (e.g., `ip-172-31-29-226:12345`).
> **CPU-to-CPU** (no GPUs): build with `-DUSE_CUDA=OFF`, **or** pass `--use_vram=false` to a CUDA-enabled binary. Drop `--gpu_id=-1` in that case — the bench will spread buffers across NUMA nodes instead.
@ -231,11 +234,7 @@ address shown in the target's startup log (e.g., `ip-172-31-29-226:12345`).
| `--init_mem` | true | Zero-fill the allocated buffer; rarely needs to change |
| `--auto_discovery` | false | Auto-discover topology on init; off for reproducible runs |
> **Note on EFA slicing:** EFA transport does not split each transfer
> into fixed-size slices the way RDMA transport does — each transfer
> is sent as a single `fi_write` / `fi_read` whose size equals
> `block_size`, round-robin'd across NICs per request. **`block_size`
> is the key tuning parameter** for EFA throughput.
> **Note on EFA slicing:** EFA transport does not split each transfer into fixed-size slices the way RDMA transport does — each transfer is sent as a single `fi_write` / `fi_read` whose size equals `block_size`, round-robin'd across NICs per request. **`block_size` is the key tuning parameter** for EFA throughput.
> **Note:** `buffer_size` must be >= `block_size * batch_size * threads`. The benchmark auto-adjusts if too small.
@ -271,10 +270,7 @@ Tested on two p6-b300.48xlarge instances (Intel Xeon Platinum 8559C, 8× B300, 1
Tested on two p6-b200.48xlarge instances in the same AWS placement group.
> **Note:** numbers below predate the SRD shared-endpoint refactor (#1944) and
> current EFA tuning work. They are a lower bound for the current
> code; we will re-sweep and update when a B200 pair is available
> again.
> **Note:** numbers below predate the SRD shared-endpoint refactor (#1944) and current EFA tuning work. They are a lower bound for the current code; we will re-sweep and update when a B200 pair is available again.
**GPU-to-GPU** (build with `-DUSE_CUDA=ON`, `--gpu_id=-1` for all 8 GPUs):
@ -374,20 +370,10 @@ Tested on two p5.48xlarge instances (AMD EPYC 7R13, 8× H100 80GB, 32 EFA device
### Single-host loopback
EFA NICs have no hardware loopback short-circuit: when a transfer's source and
destination resolve to the same host, the data does not go out on the wire as
GPUDirect/device RDMA. libfabric handles the same-host case in software, and
there are **two distinct provider knobs** that select how:
EFA NICs have no hardware loopback short-circuit: when a transfer's source and destination resolve to the same host, the data does not go out on the wire as GPUDirect/device RDMA. libfabric handles the same-host case in software, and there are **two distinct provider knobs** that select how:
- **`FI_EFA_ENABLE_SHM_TRANSFER`** (default `1`, on): when on, the EFA
provider routes same-host peers through the **`shm` provider** — verifiable
at runtime, where libfabric reports `Opened fabric: shm` alongside
`Opened fabric: efa` even on a default (device-RDMA-enabled) configuration.
This SHM path is the one that supplies the same-host memcpy fast path; it is
active **by default**, independent of `FI_EFA_USE_DEVICE_RDMA`.
- **`FI_EFA_USE_DEVICE_RDMA`** (default `1` after #2041): controls whether the
EFA RDM data path uses device RDMA vs libfabric's emulated RDM path. It is a
provider-level flag resolved at `fi_getinfo` time; Mooncake does not wrap it.
- **`FI_EFA_ENABLE_SHM_TRANSFER`** (default `1`, on): when on, the EFA provider routes same-host peers through the **`shm` provider** — verifiable at runtime, where libfabric reports `Opened fabric: shm` alongside `Opened fabric: efa` even on a default (device-RDMA-enabled) configuration. This SHM path is the one that supplies the same-host memcpy fast path; it is active **by default**, independent of `FI_EFA_USE_DEVICE_RDMA`.
- **`FI_EFA_USE_DEVICE_RDMA`** (default `1` after #2041): controls whether the EFA RDM data path uses device RDMA vs libfabric's emulated RDM path. It is a provider-level flag resolved at `fi_getinfo` time; Mooncake does not wrap it.
```{warning}
**GPU (FI_HMEM_CUDA) buffers — known segfault.** The default same-host **SHM**
@ -411,23 +397,16 @@ transfer — `__memcpy_avx_unaligned` ← `ofi_copy_to_mr_iov` ← `smr_copy_fro
then fall back to device RDMA, which is GPU-aware and correct.
```
For **host (DRAM) buffers** the SHM memcpy path is safe (host→host copy) and is
the same-host fast path the measurements below exercise.
For **host (DRAM) buffers** the SHM memcpy path is safe (host→host copy) and is the same-host fast path the measurements below exercise.
Measured on p5.48xlarge (1 NIC, ~1.2 GiB per `put_from` call, host DRAM buffer,
same-host producer/consumer in **separate processes**):
Measured on p5.48xlarge (1 NIC, ~1.2 GiB per `put_from` call, host DRAM buffer, same-host producer/consumer in **separate processes**):
| same-host path | per-write latency |
|---|---:|
| device RDMA (NIC round-trip, no fast-path for loopback) | ~830 ms |
| SHM memcpy fast path (default) | ~390 ms |
For reference, a cross-host `put_from` of the same payload (device RDMA, 1 NIC)
is ~340 ms — i.e., driving a same-host loopback through the NIC is *slower* than
going over the wire to another host, because the NIC has no fast-path for
loopback. Cross-host transfers always use device RDMA and are unaffected by
`FI_EFA_ENABLE_SHM_TRANSFER`: leave it at its default on any process that also
talks to remote peers.
For reference, a cross-host `put_from` of the same payload (device RDMA, 1 NIC) is ~340 ms — i.e., driving a same-host loopback through the NIC is *slower* than going over the wire to another host, because the NIC has no fast-path for loopback. Cross-host transfers always use device RDMA and are unaffected by `FI_EFA_ENABLE_SHM_TRANSFER`: leave it at its default on any process that also talks to remote peers.
### Tuning Tips
@ -436,9 +415,7 @@ talks to remote peers.
- **Write vs read:** write benefits from larger batches (peak at `batch=128`); on 16-NIC p5en read prefers smaller queues (peak at `batch=32`), but on 32-NIC p5 reads scale up to `batch=128` because the wider fabric absorbs larger in-flight queues.
- For **GPU-to-GPU**: pass `--gpu_id=-1` on **both** sides so buffers fan out across every GPU. Pinning a single GPU halves throughput because half the NICs end up cross-NUMA.
- For **CPU-to-CPU**: DRAM bandwidth is the ceiling. NUMA-split (separate initiator/target instances per NUMA node) can help reduce contention when one instance can't saturate both nodes.
- `--buffer_size` only needs `≥ block × batch × threads`; larger
values do not improve throughput. The example commands use 4 GB
because that is safe for any reasonable config.
- `--buffer_size` only needs `≥ block × batch × threads`; larger values do not improve throughput. The example commands use 4 GB because that is safe for any reasonable config.
### First-request latency
@ -461,7 +438,7 @@ The SRD shared-endpoint refactor (#1944) speeds up first-request latency two dif
- Rust: `TransferEngine::warmup_efa_segment(name: &str)`
- Python: `engine.warmup_efa_segment(segment_name)`
Call once per peer right after `openSegment`. The call is idempotent. Under this refactor `warmupSegment` itself is ~15× faster than the pre-#1944 code (1.1 s vs 17 s), bounded by the peer's single-threaded handshake RPC daemon (`accept` + JSON parse serialized on one thread), so it scales linearly with the number of fresh NIC pairs.
Call once per peer right after `openSegment`. The call is idempotent. Under this refactor `warmupSegment` itself is ~15× faster than the pre-#1944 code (1.1 s vs 17 s), bounded by the peer's single-threaded handshake RPC daemon (`accept` + JSON parse serialized on one thread), so it scales linearly with the number of fresh NIC pairs.
vLLM and SGLang do not currently call `warmupSegment` — they go through the generic `TransferEngine` interface and pick up the 4× cold-submit speedup automatically. The API is there for direct Mooncake callers that want the larger win.
@ -556,12 +533,7 @@ vllm-router --policy round_robin \
--host 0.0.0.0 --port 30000
```
> **Do not add `--intra-node-data-parallel-size` here.** The prefill / decode
> instances above are launched with `-tp 8` (pure tensor parallelism, data
> parallel size = 1), so there is no intra-node DP to advertise. Only pass
> `--intra-node-data-parallel-size N` when your instances actually run `N`-way
> data parallelism per node (e.g. you launched them with `--data-parallel-size N`);
> setting it to match `-tp 8` is wrong and will misroute requests.
> **Do not add `--intra-node-data-parallel-size` here.** The prefill / decode instances above are launched with `-tp 8` (pure tensor parallelism, data parallel size = 1), so there is no intra-node DP to advertise. Only pass `--intra-node-data-parallel-size N` when your instances actually run `N`-way data parallelism per node (e.g. you launched them with `--data-parallel-size N`); setting it to match `-tp 8` is wrong and will misroute requests.
## Usage with SGLang
@ -651,18 +623,9 @@ The trailing `8998` after `--prefill` must match the prefill's `--disaggregation
### Why libfabric instead of ibverbs?
AWS EFA exposes an RDMA-capable device through the ibverbs interface, but it does
**not** implement the full ibverbs API. In particular, EFA only supports
**SRD** (Scalable Reliable Datagram) and **UD** (Unreliable Datagram) queue
pairs — it does **not** support the **RC** (Reliable Connection) queue pairs
that Mooncake's RDMA (`rdma`) transport is built on. Attempting to create an RC
QP on an EFA device fails (`EOPNOTSUPP`), and SRD has no one-sided RC-style
`ibv_post_send(RDMA_WRITE)` verb in the public ibverbs API.
AWS EFA exposes an RDMA-capable device through the ibverbs interface, but it does **not** implement the full ibverbs API. In particular, EFA only supports **SRD** (Scalable Reliable Datagram) and **UD** (Unreliable Datagram) queue pairs — it does **not** support the **RC** (Reliable Connection) queue pairs that Mooncake's RDMA (`rdma`) transport is built on. Attempting to create an RC QP on an EFA device fails (`EOPNOTSUPP`), and SRD has no one-sided RC-style `ibv_post_send(RDMA_WRITE)` verb in the public ibverbs API.
The portable way to drive EFA's SRD transport is libfabric, whose EFA provider
exposes SRD through the `FI_EP_RDM` (Reliable Datagram Message) endpoint type and
implements `fi_write` / `fi_read` (one-sided RMA) on top of it. Mooncake's EFA
transport therefore targets libfabric directly rather than ibverbs.
The portable way to drive EFA's SRD transport is libfabric, whose EFA provider exposes SRD through the `FI_EP_RDM` (Reliable Datagram Message) endpoint type and implements `fi_write` / `fi_read` (one-sided RMA) on top of it. Mooncake's EFA transport therefore targets libfabric directly rather than ibverbs.
### EFA Transport Architecture
@ -691,15 +654,7 @@ Under the SRD shared-endpoint model every peer is addressed through one `fid_ep`
└───────────────────────────────────────────────────────────┘
```
> **Peer-map keying.** `peer_map_` is keyed by the **full** `host:port@nic`
> path, *not* a port-stripped form. Under SGLang DP > 1 each DP worker on a
> peer host is a separate process with its own Mooncake `TransferEngine` and
> its own P2PHANDSHAKE RPC port; they share host + NIC but have distinct EFA
> addresses. Normalizing the port away would collapse every DP worker on that
> host onto one `EfaEndPoint`, so each arriving handshake would look like a
> "peer reconnected" to the previous holder and trigger `fi_av_remove` +
> `fi_av_insert` churn on every KV transfer. Keeping the port in the key costs
> nothing in steady state (the port is stable for a worker's lifetime).
> **Peer-map keying.** `peer_map_` is keyed by the **full** `host:port@nic` path, *not* a port-stripped form. Under SGLang DP > 1 each DP worker on a peer host is a separate process with its own Mooncake `TransferEngine` and its own P2PHANDSHAKE RPC port; they share host + NIC but have distinct EFA addresses. Normalizing the port away would collapse every DP worker on that host onto one `EfaEndPoint`, so each arriving handshake would look like a "peer reconnected" to the previous holder and trigger `fi_av_remove` + `fi_av_insert` churn on every KV transfer. Keeping the port in the key costs nothing in steady state (the port is stable for a worker's lifetime).
### Thread Safety
@ -708,12 +663,7 @@ The EFA transport requests `FI_THREAD_SAFE` at the domain level and guards the s
- Multiple submission threads may route slices through the same shared endpoint concurrently.
- libfabric's EFA RDM endpoints are not thread-safe for concurrent `fi_write`/`fi_read` even under `FI_THREAD_SAFE` at the domain level — concurrent posts corrupt provider internals and completions silently vanish.
CQ completion queues are polled by dedicated worker threads that run
independently of submission threads. The poller count is `min(MC_EFA_CQ_THREADS,
num_EFA_devices)`; `MC_EFA_CQ_THREADS` defaults to `1`, so a single poller
round-robins every context's CQ (which already reaches ~99.9% of peak — see the
SGLang env-var note above). Set `MC_EFA_CQ_THREADS=0` to lift the cap and spawn
one poller per EFA device (the legacy behavior).
CQ completion queues are polled by dedicated worker threads that run independently of submission threads. The poller count is `min(MC_EFA_CQ_THREADS, num_EFA_devices)`; `MC_EFA_CQ_THREADS` defaults to `1`, so a single poller round-robins every context's CQ (which already reaches ~99.9% of peak — see the SGLang env-var note above). Set `MC_EFA_CQ_THREADS=0` to lift the cap and spawn one poller per EFA device (the legacy behavior).
### EFA vs RoCE RDMA
@ -728,12 +678,7 @@ one poller per EFA device (the legacy behavior).
| Throughput CPU-to-CPU (16×200G, p5en) | 213 GB/s (tuned) | — |
| AWS availability | All EFA-enabled instances | Not available on AWS |
> Mooncake requests libfabric API ≥ 1.18 at `fi_getinfo`, which makes
> `FI_EFA_USE_DEVICE_RDMA=1` the default on every supported EFA generation
> (p5/p5e included). On this path `fi_write` / `fi_read` are hardware-offloaded
> one-sided RMA over SRD — the host CPU is not in the data path. The
> software-emulated RMA path only applies if you explicitly set
> `FI_EFA_USE_DEVICE_RDMA=0`.
> Mooncake requests libfabric API ≥ 1.18 at `fi_getinfo`, which makes `FI_EFA_USE_DEVICE_RDMA=1` the default on every supported EFA generation (p5/p5e included). On this path `fi_write` / `fi_read` are hardware-offloaded one-sided RMA over SRD — the host CPU is not in the data path. The software-emulated RMA path only applies if you explicitly set `FI_EFA_USE_DEVICE_RDMA=0`.
### Supported AWS Instance Types

View File

@ -160,7 +160,7 @@ echo "Building wheel package..."
# Build the wheel package
cd mooncake-wheel
BUILD_VARIANTS="NON_CUDA_BUILD CU13_BUILD NPU_BUILD MUSA_BUILD"
BUILD_VARIANTS="NON_CUDA_BUILD CU13_BUILD NPU_BUILD EFA_BUILD EFA_NON_CUDA_BUILD MUSA_BUILD"
BUILD_VARIANT_COUNT=0
for build_variant in $BUILD_VARIANTS; do
if [ "${!build_variant}" = "1" ]; then
@ -172,6 +172,15 @@ if [ "$BUILD_VARIANT_COUNT" -gt 1 ]; then
exit 1
fi
# If a previous run was interrupted before the trailing restore (line ~481),
# pyproject.toml is left in a renamed state and pyproject.toml.backup holds the
# pristine original. Restore it first so the variant rename below always starts
# from the clean file and the backup is never overwritten with modified content.
if [ -f pyproject.toml.backup ]; then
echo "Restoring pyproject.toml from leftover backup of a previous run"
mv pyproject.toml.backup pyproject.toml
fi
# Handle package name modification for release build variants
if [ "$NON_CUDA_BUILD" = "1" ]; then
echo "Modifying package name for non-CUDA build"
@ -200,6 +209,24 @@ elif [ "$NPU_BUILD" = "1" ]; then
sed -i 's/description = "Python binding of a Mooncake library using pybind11"/description = "Python binding of a Mooncake library using pybind11 (Ascend NPU version)"/' pyproject.toml
sed -i 's/keywords = \["mooncake", "data transfer", "kv cache", "llm inference"\]/keywords = ["mooncake", "data transfer", "kv cache", "llm inference", "ascend", "npu"]/' pyproject.toml
echo "Package name modified to: mooncake-transfer-engine-npu"
elif [ "$EFA_BUILD" = "1" ]; then
echo "Modifying package name for AWS EFA build (CUDA)"
# Backup original pyproject.toml
cp pyproject.toml pyproject.toml.backup
# Replace package name and description
sed -i 's/name = "mooncake-transfer-engine"/name = "mooncake-transfer-engine-efa"/' pyproject.toml
sed -i 's/description = "Python binding of a Mooncake library using pybind11"/description = "Python binding of a Mooncake library using pybind11 (AWS EFA, CUDA version)"/' pyproject.toml
sed -i 's/keywords = \["mooncake", "data transfer", "kv cache", "llm inference"\]/keywords = ["mooncake", "data transfer", "kv cache", "llm inference", "aws", "efa", "libfabric", "cuda"]/' pyproject.toml
echo "Package name modified to: mooncake-transfer-engine-efa"
elif [ "$EFA_NON_CUDA_BUILD" = "1" ]; then
echo "Modifying package name for AWS EFA build (non-CUDA)"
# Backup original pyproject.toml
cp pyproject.toml pyproject.toml.backup
# Replace package name and description
sed -i 's/name = "mooncake-transfer-engine"/name = "mooncake-transfer-engine-efa-non-cuda"/' pyproject.toml
sed -i 's/description = "Python binding of a Mooncake library using pybind11"/description = "Python binding of a Mooncake library using pybind11 (AWS EFA, Non-CUDA version)"/' pyproject.toml
sed -i 's/keywords = \["mooncake", "data transfer", "kv cache", "llm inference"\]/keywords = ["mooncake", "data transfer", "kv cache", "llm inference", "aws", "efa", "libfabric", "non-cuda"]/' pyproject.toml
echo "Package name modified to: mooncake-transfer-engine-efa-non-cuda"
elif [ "$MUSA_BUILD" = "1" ]; then
echo "Modifying package name for MUSA build"
# Backup original pyproject.toml