Mooncake/docs/source
Suryxin 0a0e952b1e
[TransferEngine][docs] document FI_EFA_USE_DEVICE_RDMA=0 for same-host EFA loopback (#2222)
* [TransferEngine] feat(efa): add MC_EFA_LOOPBACK_PREFER_EMULATED to recover same-host loopback throughput

After #2041 ([TE] fix(efa): request libfabric API 1.18 so device RDMA is the default on all EFA generations) the EFA provider unconditionally enables device RDMA on every supported EFA hardware. This is the right default for cross-host transfers -- it is what unlocks the 300+ GB/s benchmarks documented in this file -- but it regresses any Mooncake Store deployment that runs producer and consumer as separate processes on the same host (single-machine development, single-host benchmarks, co-located workers).

EFA NICs have no hardware loopback short-circuit: a "loopback" fi_write still drives a real DMA round-trip through the device (PCIe out, NIC SRD packet processing, PCIe back), so a same-host transfer pays full per-packet NIC overhead instead of taking the memcpy fast path that libfabric's emulated RDMA provider applies for same-host endpoints.

Measured on p5.48xlarge (1 NIC, 80 MB transfer, two Mooncake Store clients on the same host, put_from):

  FI_EFA_USE_DEVICE_RDMA=1 (default after #2041):  ~830 ms / call
  FI_EFA_USE_DEVICE_RDMA=0 (emulated):              ~390 ms / call

The 2.1x ratio is reproducible across runs; the emulated number is on par with what we measure for the same transfer cross-host with device RDMA on (~340 ms), once single-host memory-bandwidth contention is accounted for, confirming the slow path is NIC loopback rather than anything in the Mooncake Store layers above.

Add MC_EFA_LOOPBACK_PREFER_EMULATED as an explicit opt-in. When set to 1/true/yes/on, EfaContext::construct sets FI_EFA_USE_DEVICE_RDMA=0 before fi_getinfo so the EFA provider takes the emulated path. The env is opt-in, not auto-detect, because a single EfaTransport instance may serve a mix of loopback and cross-host peers, and FI_EFA_USE_DEVICE_RDMA is a provider-level flag resolved at fi_getinfo time -- flipping it disables device RDMA for every transfer in the process, including cross-host ones, which is exactly the wrong behavior for production fan-out. We use setenv(..., 0) so an explicit FI_EFA_USE_DEVICE_RDMA set by the user still wins over the opt-in.

Cross-host benchmarks are unaffected unless the env is also set on the cross-host process; the default behavior of this code path is unchanged.

A real fix (per-transfer same-host memcpy or cross-process zero-copy via process_vm_writev) is tracked in a follow-up issue. This change is the minimal mitigation users need today to avoid silently giving up half their single-host throughput.

* [TransferEngine] refactor(efa): address review feedback on MC_EFA_LOOPBACK_PREFER_EMULATED

Per maintainer review on #2222:

1. Register MC_EFA_LOOPBACK_PREFER_EMULATED in the Environ singleton
   (mooncake-common/{include/environ.h,src/environ.cpp}) instead of
   parsing it ad-hoc at the call site, so it shows up in the same
   inventory as every other tunable and goes through the existing
   GetBool() helper (which already handles 1/true/TRUE/on/yes).

2. Guard the setenv("FI_EFA_USE_DEVICE_RDMA", "0", 0) block with
   !std::getenv("FI_EFA_USE_DEVICE_RDMA"). This fixes two bugs:
     - We no longer log "-> FI_EFA_USE_DEVICE_RDMA=0" when the user
       has already set the env explicitly (setenv is a no-op there,
       so the old log line was misleading).
     - EfaContext::construct runs once per NIC (up to 32 times on
       p5.48xlarge); the getenv check causes the first NIC to set
       the env and subsequent NICs to skip the block entirely, so
       we log exactly once.

3. Drop the hand-rolled std::transform + ::tolower entirely (which
   was UB on signed char anyway -- flagged by Copilot and gemini)
   by delegating to Environ::GetBool. Removes <algorithm>, adds
   <cstdlib> for std::getenv/setenv.

Behavior is unchanged for the same-host case in the verification
table; this is purely structural cleanup.

* [TransferEngine] docs(efa): correct transfer size in verification table

The verification table in PR #2222 cited "80 MB transfer" as the
per-call payload, but the actual measurement was per ~1.2 GiB
(1218.8 MiB) ref blob (see ref_extractor log: blob_bytes=1218.8MiB
put=489.88ms). Update both the docs section and the in-code
comment to reflect the real transfer size. Latency numbers
(~830 ms / ~390 ms / ~340 ms) are unchanged -- they were always
measured on the 1.2 GiB blob.

Also collapse two single-statement multi-line getters/initializers
in mooncake-common to single-line form to match the existing
convention in environ.{h,cpp} (all other GetX() accessors are
single-line). No behavior change.

* [TransferEngine] docs(efa): drop MC_EFA_LOOPBACK_PREFER_EMULATED wrapper, document FI_EFA_USE_DEVICE_RDMA=0 directly

Per review feedback on #2222: the EFA user base is already familiar with
FI_EFA_USE_DEVICE_RDMA (it is documented by the EFA installer and
appears in every libfabric/EFA tuning guide), so wrapping it in a
Mooncake-namespaced alias does not pay for itself. The wrapper was a
literal one-to-one alias with no defaulting or transform.

Revert the Environ registration and the efa_context.cpp setenv block
(net code change for this PR becomes zero). Keep the diagnosis and the
verification table in efa_transport.md, but rewrite the recommendation
to point at FI_EFA_USE_DEVICE_RDMA=0 directly with the same
per-process / mixed-traffic caveat.

The long-term fix for same-host loopback (routing same-host
different-process transfers through process_vm_writev as a new
TransferStrategy::CROSS_PROCESS_MEMCPY, bypassing the NIC entirely)
remains tracked as #2223.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 11:57:16 +08:00
..
_static [DOC] feat(docs): build documentation website for Mooncake using Sphinx (#354) 2025-05-12 23:33:18 +08:00
community [Doc] Add goverance doc (#1352) 2026-01-09 18:53:25 -08:00
deployment [Doc] Update snapshot object store docs (#2148) 2026-05-25 14:35:34 +08:00
design [TransferEngine][docs] document FI_EFA_USE_DEVICE_RDMA=0 for same-host EFA loopback (#2222) 2026-05-28 11:57:16 +08:00
getting_started [Docs]: fix docs config, remove autodoc2, archive zh docs (#2218) 2026-05-26 15:29:37 +08:00
http-api-reference [Docs]: fix docs config, remove autodoc2, archive zh docs (#2218) 2026-05-26 15:29:37 +08:00
image [Docs] Add SSD offload benchmark results (#1835) 2026-04-13 15:52:52 +08:00
performance [Docs]: fix docs config, remove autodoc2, archive zh docs (#2218) 2026-05-26 15:29:37 +08:00
python-api-reference [Store] Add opt-in grouped object routing semantics (#2180) 2026-05-27 13:15:57 +08:00
troubleshooting [Store] Add lock-free MmapArena allocator for buffer mmap path (#1820) 2026-05-03 14:18:18 +08:00
zh_archive [Docs]: fix docs config, remove autodoc2, archive zh docs (#2218) 2026-05-26 15:29:37 +08:00
autodoc2_docstring_parser.py [DOC] feat(docs): build documentation website for Mooncake using Sphinx (#354) 2025-05-12 23:33:18 +08:00
conf.py [Docs]: fix docs config, remove autodoc2, archive zh docs (#2218) 2026-05-26 15:29:37 +08:00
index.md [Docs]: fix docs config, remove autodoc2, archive zh docs (#2218) 2026-05-26 15:29:37 +08:00