Skip to main content

Peer-to-peer RDMA zero-copy L3 KV-cache backend for SGLang HiCache

Project description

PeerCache

English · 简体中文

CI Docs License

A lightweight, peer-to-peer L3 storage backend for SGLang HiCache, built for PD-disaggregated (prefill/decode) inference: prefill workers publish KV pages, decode workers read them back over RDMA with zero CPU copies.

Docs: https://flymysql.github.io/PeerCache/

PeerCache gives you Mooncake-style RDMA zero-copy KV-cache sharing across nodes, but without the centralized master + metadata services. Instead it uses:

  • Embedded service discovery — no separate meta process. One node (chosen by discovery_addr) auto-hosts the discovery service in-process; nodes register their endpoint, heartbeat, and pull the live membership list.
  • A consistent-hash distributed directory (DHT) — the mapping key -> {data_node, remote_addr, rkey, length} is sharded across all nodes by hashing the key. There is no central metadata store.
  • Data stays local on writeset() copies the page into a node-local published pool (a host memcpy, no network, no master) and pushes only a tiny location record to the directory.
  • One-sided RDMA READ on readget() looks up the directory, then issues a zero-copy IBV_WR_RDMA_READ straight into SGLang's registered host buffer.
  • Concurrent multi-threaded I/O — a per-peer channel pool (each an RC QP with its own completion queue) lets reader/writer threads run with no shared-CQ contention; the control plane parallelises directory lookups too.
  • Disk persistence tier (L4) — pages evicted from memory spill to disk (default /data/peercache/, 100GB) and are promoted back into the pool on a later read (locally or by a remote reader).
  • Built-in monitoring — Prometheus /metrics + an embedded HTML dashboard (default port 31997): hit rate, throughput, latency p50/p99, mem/disk usage.

Architecture

PeerCache splits into a C++ data plane (raw RDMA verbs) and a Python control plane (discovery, directory, pool). The meta node only tracks membership; the key -> location map is sharded across every node by consistent hashing, and the KV bytes never leave the node that produced them until a peer reads them directly.

flowchart LR
    subgraph A["Node 0 — prefill / producer"]
        S0["PeerCacheStore"]
        P0[("Published pool MR (LRU)")]
        D0["Directory shard"]
    end
    subgraph B["Node 1 — decode / consumer"]
        S1["PeerCacheStore"]
        R1[("Receive MR = SGLang kv_buffer")]
        D1["Directory shard"]
    end
    M(["Embedded discovery / membership"])

    S0 -- "set(): local memcpy" --> P0
    S0 -- "PUT key→loc (hash)" --> D1
    S1 -- "GET key (hash)" --> D0
    S1 == "one-sided RDMA READ (zero copy)" ==> P0
    P0 -. "lands directly in" .-> R1
    S0 -. "register / heartbeat" .-> M
    S1 -. "register / heartbeat" .-> M
write:  set() ── local memcpy ──> published pool MR
                └── PUT key->{node,addr,rkey,len} ──> directory shard (hash(key))
read:   get() ── GET key ──> directory shard ──> {node,addr,rkey,len}
                └── one-sided RDMA READ ──> local host buffer (zero copy)
  • C++ data plane (cpp/): raw libibverbs + librdmacm. RC QPs, one-sided READ/WRITE, per-peer channel pool with private CQs for concurrency. Exposed to Python via pybind11 as the _peercache module.
  • Python control plane (python/peercache/): TCP RPC, service discovery, consistent-hash ring, distributed directory, and the published-pool with LRU.
  • TCP fallback transport: a pure-Python transport that mirrors the RDMA API so the design can be validated end-to-end on machines without RDMA hardware.

Two-MR model (correctness)

SGLang's host KV buffer is the L2 tier and is evicted/overwritten by HiCache, so we cannot register its address into the directory directly (dangling reference). Each node therefore registers two memory regions:

  1. Receive MR = mem_pool_host.kv_buffer — destination of one-sided READ on get.
  2. Published pool MR = a backend-owned host pool with LRU — source of READ on remote nodes. set memcpys the page into this pool (node-local, no network) and publishes its addr+rkey+len to the directory. Eviction from the pool deletes the corresponding directory entry, so a published address stays valid until evicted.

See the Architecture docs for the full design.

Why simpler than Mooncake?

Mooncake PeerCache
metadata central master + metadata service sharded directory (consistent hash)
data placement dedicated managed pool stays on producing node
coordination master allocates / tracks objects only service discovery on meta node
transfer RDMA zero-copy RDMA zero-copy (one-sided READ)

Install

From PyPI (recommended)

pip install peercache

This builds the C++ data plane from source, so the target host needs a C++17 toolchain, CMake ≥ 3.18, and the RDMA dev headers (libibverbs / librdmacm, e.g. rdma-core or Mellanox OFED). If those headers are absent, the build automatically falls back to a stub module and the pure-Python TCP transport.

To force the no-RDMA build explicitly (control plane + TCP fallback only, e.g. on a laptop or in CI):

pip install peercache --config-settings=cmake.define.PEERCACHE_NO_RDMA=ON

From source

git clone https://github.com/flymysql/PeerCache.git
cd PeerCache
pip install .                 # or: pip install -e ".[test]"

Run with SGLang

The meta service is embedded — there is no separate meta process. Point discovery_addr at one node's IP on every node; the node whose IP matches auto-starts the discovery service in-process.

# On every SGLang node, set discovery_addr to the SAME node's IP (say node-0).
# node-0 detects the IP is itself and hosts the embedded meta automatically.
python -m sglang.launch_server --enable-hierarchical-cache \
  --hicache-storage-backend dynamic \
  --hicache-storage-backend-extra-config \
  '{"backend_name":"peercache","module_path":"peercache.store","class_name":"PeerCacheStore","discovery_addr":"NODE0_IP:31998","protocol":"rdma","device_name":"mlx5_0","global_segment_size":"4gb"}'

(Optionally, you can still run a standalone meta with peercache-meta --bind 0.0.0.0:31998 if you prefer a dedicated discovery host.)

See examples/sglang_launch.md for details.

Benchmarks

A systematic benchmark suite ships inside the package and is exposed as a single console command (no repo clone, no PYTHONPATH). It drives PeerCache's HiCacheStorage interface exactly as SGLang HiCache does (PD-disaggregated batch_set_v1 / batch_exists / batch_get_v1) and reports throughput (pages/s, tokens/s, GB/s) and latency tail (p50/p95/p99/p999/max) across a sweep of thread models, including the full-load saturation/peak throughput.

Measured baseline (cross-host RDMA, GET, MLA)

On 2× AMD EPYC 9K84 + 8× ConnectX-7 (RoCEv2, MTU 4096, MLNX_OFED 5.8):

scenario GET throughput
single NIC, PeerCache 46.0 GB/s (368 Gbps) — ~94% of bare ib_read_bw (49.0 GB/s)
single process, 8 rails (1 MiB pages) 147.6 GB/s (1.18 Tbps)
full machine, 8 NICs, multi-process 413.1 GB/s (≈ 3.3 Tbps)

PeerCache GET throughput: single NIC → whole machine

Methodology, charts, and reproduce commands: Performance baseline.

pip install peercache

# RDMA hardware (publishable numbers)
peercache-bench suite --device-name mlx5_0 --layout mla --page-size 131072 \
    --batch-size 32 --concurrencies 1,2,4,8,16,32,64 --duration 10 --tag rdma

peercache-bench subcommands: latency, throughput, saturation, suite (SGLang-HiCache), plus micro, mooncake, compare.

RDMA-first. PeerCache is built on RDMA one-sided READ; publishable figures must be measured on RDMA hardware. The TCP fallback is for functional smoke testing only and must not be quoted. See the bench README and the Benchmarks docs for the methodology, thread models, metric definitions, and reproduction recipe.

Test

pip install -e ".[test]"
pytest -q

Maintainer setup (one-time)

  • GitHub Pages: Settings → Pages → Build and deployment → Source = GitHub Actions. The Docs workflow then publishes to https://flymysql.github.io/PeerCache/ on every push to main.
  • PyPI Trusted Publishing: on the PyPI peercache project, add a GitHub publisher (owner flymysql, repo PeerCache, workflow release.yml, environment pypi). Tagging vX.Y.Z then builds the sdist, attaches it to a GitHub Release, and publishes to PyPI. Until configured, the PyPI step is non-blocking and the GitHub Release still ships the package.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

peercache-0.6.8.tar.gz (304.3 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

peercache-0.6.8-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (219.8 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

peercache-0.6.8-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (218.6 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

peercache-0.6.8-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (217.2 kB view details)

Uploaded CPython 3.10manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

peercache-0.6.8-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (217.6 kB view details)

Uploaded CPython 3.9manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

File details

Details for the file peercache-0.6.8.tar.gz.

File metadata

  • Download URL: peercache-0.6.8.tar.gz
  • Upload date:
  • Size: 304.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for peercache-0.6.8.tar.gz
Algorithm Hash digest
SHA256 491785a16f94cd0ac047570abe04028c9f5a9828a3074167cc8edc7eefce0139
MD5 5e532cc5768af0addad4cf9ad31b9c99
BLAKE2b-256 13afe0ee52d4e1e6f41bee6dbc1fa6a059a3ae8bcda6606c0d54b194d36827df

See more details on using hashes here.

Provenance

The following attestation bundles were made for peercache-0.6.8.tar.gz:

Publisher: release.yml on flymysql/PeerCache

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file peercache-0.6.8-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for peercache-0.6.8-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 563676462c1040f333285230c2dd263e53ea709e266c673e286d3c7f7cab1ba4
MD5 95ff7562752790f8af99e7142d0b0019
BLAKE2b-256 586508d50b5e9c8f985f1f8bace90d078c7a1a4fc940f7cf965bd3b06a8e7152

See more details on using hashes here.

Provenance

The following attestation bundles were made for peercache-0.6.8-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: release.yml on flymysql/PeerCache

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file peercache-0.6.8-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for peercache-0.6.8-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 bb4e7f00077d2cee620e4ece67d4194097b11171f80186215d937f26d3a6b8a8
MD5 1b602b437adc1b79e9e73bfeef2c1a1c
BLAKE2b-256 4ab8daee30a5f14e7882c5997dbfad4e9841481c76789644d8b422878e796903

See more details on using hashes here.

Provenance

The following attestation bundles were made for peercache-0.6.8-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: release.yml on flymysql/PeerCache

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file peercache-0.6.8-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for peercache-0.6.8-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 ea2f44e4ea2edb538750d7f0ef435cf41b089ad53f5e117a40f011ebdfbbb53f
MD5 3560cff2face2c445be37dac9385f5ef
BLAKE2b-256 eed9ad6592cd92209765b303d75db7915530dbb48c9d1b7bafae10c2ddd3118e

See more details on using hashes here.

Provenance

The following attestation bundles were made for peercache-0.6.8-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: release.yml on flymysql/PeerCache

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file peercache-0.6.8-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for peercache-0.6.8-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 d57688b4016952274c6a156a1fdd272eb1ec18bc0e5ddea4c2c7b63dfbccf64b
MD5 772cb759d2f777eac80f39ba32a0f5a8
BLAKE2b-256 3a74c7bc185a0d009ae1b5fca7749e453f81710c8fcebaa105c88209af887cda

See more details on using hashes here.

Provenance

The following attestation bundles were made for peercache-0.6.8-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: release.yml on flymysql/PeerCache

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page