DeepEP on EFA

§2 · EFA vs InfiniBand — why MoE all-to-all behaves differently

Mixture-of-Experts dispatch is a storm of small, latency-sensitive messages, not a bandwidth-bound bulk transfer. That single fact is why the same DeepEP code that flies on an InfiniBand / IBGDA cluster runs into a wall on AWS EFA. This section derives the wall from the wire up: the transport semantics, the per-operation latency math, the measured 2.2% NIC utilization, and the load-invariant +14.7 ms/token decode tax that is the headline result.

Throughout, color is consistent so you can read the diagrams at a glance: InfiniBand RC (ordered, atomic, GPU-initiated) versus AWS EFA SRD (scalable reliable datagram — no ordering, no atomics, host-proxied).

1 · The two fabrics, side by side

InfiniBand serving GPUDirect uses Reliable Connected (RC) queue pairs: a connection-oriented, in-order, fully-featured RDMA transport. AWS EFA uses Scalable Reliable Datagram (SRD): reliable delivery and multipath spraying for scale, but a deliberately thinner verb set. The differences below are not tuning knobs — they are properties of the wire, and each one removes a primitive DeepEP's fast path was built on.

Property InfiniBand · RC AWS EFA · SRD Why it matters for MoE dispatch
Message ordering Ordered — RC delivers in send order msg_order = [ ] — no ordering guarantee DeepEP's receiver advances a per-peer signal sequence in order; SRD multipath can reorder, so the plugin must serialize completions instead of pipelining them.
Hardware atomics FI_ATOMIC — fetch-add, CAS in NIC NO FI_ATOMIC — atomics absent from caps The GPU-initiated signal path (DOCA put_signal) issues an RDMA atomic fetch-add to bump a peer's arrival counter. On EFA that WQE is silently dropped — the signal never lands.
Write-with-immediate WriteImm — data + 32-bit imm, one op No WriteImm on the SRD RMA write One-op "deliver payload + notify" is impossible; payload and notification become separate operations, roughly doubling the per-token op count.
Completion semantics CQE ⇒ remotely visible (RC) CQ is LOCAL — CQE = NIC accepted the WQE, not delivered A sender-side completion proves nothing about the peer. Wire-level verification must read peer memory or HW counters — "N posts fired" is Layer-0 evidence only.
GPU-initiated comm IBGDA — kernel posts WQE to NIC directly GDAKI — present but not shipped end-to-end (3-layer stack gap) Without a kernel-posted WQE path, every cross-node op detours through the host CPU proxy — the central cost this whole section quantifies.
Memory registration rkey usable across the HCA MR is per-NIC — an rkey from NIC 0 is invalid on NIC 1 16 NICs/node means 16 separate registrations and rkey tables; the plugin allgathers per-peer, per-NIC keys rather than one global key.
Scatter-gather on RDMA write Multi-SGE gather supported RDMA write is 1 local SGE (send-only inline differs) You cannot gather "payload + co-located count" in one WQE — the trick that would halve dispatch ops is rejected at the descriptor level (EFA_IO_TX_DESC_NUM_RDMA_BUFS = 1).
Multipath Single path per QP Spray across paths — scales to many flows EFA's strength: it scales to thousands of flows without HoL blocking. The cost is the reordering above — a trade made for scale-out, not bandwidth per flow.
The hardware ceiling is not the problem. A p5en.48xlarge node has 16 EFA NICs × 25 GB/s effective = 400 GB/s/node of bisection measured (25 GB/s/NIC effective per PR #612). InfiniBand and EFA are in the same bandwidth class here. The gap this section explains is entirely about how fast you can issue small operations, not how many bytes the wire can carry.

2 · The latency math — a host round-trip per all-to-all op

MoE dispatch sends each token's hidden vector to its top-k experts, which are scattered across the EP group. At decode the message is tiny — one token's worth, a few KB — and there are many of them. So the cost that matters is per-operation issue latency × operation count, not aggregate bandwidth. Here the two fabrics diverge sharply.

InfiniBand · IBGDA — kernel-issued

The GPU kernel builds the work-queue entry in device memory and rings the NIC doorbell itself. The host CPU is never on the data path.

# per-op cost, IBGDA (kernel-posted WQE) t_op = t_doorbell + t_RTT ~0.3 µs + ~1–2 µs (SRD-class RTT) ~1.5–2 µs, fully pipelined
AWS EFA · today — host-proxied

No kernel-posted WQE path is shipped. The kernel hands off to a CPU proxy, which calls libfabric, which posts to the NIC. Every op is a GPU→host→NIC round-trip.

# per-op cost, CPU-proxy GIN (measured-derived) t_op = t_D2Hring + t_proxypoll + t_libfabric + t_RTT # GPU writes a D2H ring slot, a CPU proxy thread # polls it, builds + posts the fi_writedata, then SRD a host round-trip, serialized per op

Two structural facts make the EFA path worse than the raw round-trip suggests, and both come straight from §1:

Decode is the worst case: a single token per user per step, dispatched to top-k experts, every forward pass. Bandwidth is irrelevant — you are paying t_op many times in series, and on EFA each t_op carries a host round-trip that InfiniBand's IBGDA does not.

3 · The key measured number — 2.2% NIC utilization (issue-bound, not wire-bound)

If the wire were the bottleneck, the EFA NICs would be saturated during dispatch. They are not. On the DeepEP dispatch path the measured NIC utilization is ~2.2% — the fabric is 97.8% idle. The limit is the rate at which the host proxy can issue operations, not the rate at which the wire can carry bytes.

EFA NIC utilization — DeepEP dispatch path 97.8% idle — wire is waiting on the host proxy 2.2% used ← the entire dispatch traffic fits here 400 GB/s/node available

Issue-bound, not wire-bound. DeepEP's whole value proposition is reducing all-to-all bytes (it sends each token to only its top-k experts, an O(top-k) volume instead of all-experts). But when the wire is 97.8% idle, sending fewer bytes does not make dispatch faster — you are gated on per-op post rate through the host, and that rate is unchanged whether the payload is large or small. measured This is the crux: byte-reduction is the wrong lever for an issue-bound path.

4 · The decode-step tax — a fixed +14.7 ms/token, load-invariant

This is the headline result, and the proof is in the shape of the data. Across a wide sweep of concurrency (4 → 32) and input length (ISL 120 → 2048), the DeepEP per-user decode throughput does not move: it sits at 19.6 tok/s (σ = 0.13). The same-framework dense / allgather arm is also flat, at 27.6 tok/s. Two flat lines, separated by a constant gap.

DeepEP per-user
19.6
tok/s · σ=0.13 measured
Dense per-user
27.6
tok/s · σ=0.51 measured
Fixed tax
+14.7
ms/token derived
Ratio
~0.67×
0.65 / 0.69 / 0.67 measured
Per-user decode throughput vs load — both lines flat ⇒ the gap is a fixed per-step cost 0 20 35 tok/s conc4 conc8 conc16 / ISL2048 conc32 increasing load (concurrency × input length) ──► dense 27.6 DeepEP 19.6 +14.7 ms/token tax

Constancy is the evidence. If the gap were queueing or contention, it would grow with load. It does not. A throughput that is flat in tok/s across a 8× concurrency range and a 17× ISL range can only be explained by a fixed per-decode-step cost — the CPU-proxy host round-trip, paid once per token step regardless of how much work surrounds it. The arithmetic: 1000/19.6 − 1000/27.6 = 51.0 − 36.3 = +14.7 ms/token derived. Measured DeepEP/dense ratios: 0.65× (conc4) · 0.69× (conc32) · 0.67× (conc16/ISL2048) measured — three regimes, same tax.

Why DeepEP's byte-reduction can't pay this back. DeepEP genuinely moves fewer bytes than dense all-to-all — that is real and measurable at the transport layer (prefill dispatch hits 33 GB/s SO, measured). But the decode tax is a latency per step, not a bandwidth cost. Saving bytes on an issue-bound, 97.8%-idle wire saves nothing on the clock that gates decode tok/s. The benefit is real in bytes and never reaches the user as tok/s.

5 · What would close the gap — GDAKI, and why it isn't shipped

There is exactly one thing that removes the tax: a kernel-posted WQE on EFA — the EFA analogue of InfiniBand's IBGDA, called GDAKI (GPU-Direct Async, kernel-initiated). If the GPU could ring the NIC doorbell itself, the host round-trip disappears and t_op collapses toward the IBGDA figure in §2. The honest status: part of it works, the part you need does not.

What works on EFA via GDAKI
  • Cross-node BARRIER — end-to-end, 16/16 ranks, ~19 µs measured measured. The kernel-posted-WQE wire is capable of carrying GDAKI traffic; the barrier proves the doorbell path exists.
  • Plugin MR registration on the efa-direct provider, per-peer/per-NIC rkey allgather.
What does NOT work — DISPATCH
  • Cross-node DISPATCH via GDAKI. DeepEP's dispatch needs the signal/count primitives, and those ride exactly the SRD limits from §1 — no atomics (the DOCA put_signal fetch-add is dropped) and no ordering (the in-order receiver stalls). Today, dispatch must use the CPU proxy (NCCL_GIN_TYPE=2).

And even the doorbell path that the barrier uses is not a free upgrade: the hardware-counter completion path GDAKI dispatch would need depends on a three-layer stack that is not shipped end-to-end:

GDAKI hardware-counter completion path — the 3 missing layers ─────────────────────────────────────────────────────────────── ① rdma-core needs ibv_create_comp_cntr / efadv_comp_cntr_init_attr ──► AWS-internal fork only, zero public PRs ② libfabric needs PR #12114 "prov/efa: support hardware counter" ──► open, mergeable, not released ③ aws-ofi-nccl needs gin-gdaki-signals (#if HAVE_FI_EFA_COMP_CNTR) ──► compiles to a no-op without ① + ② Result: each layer silently degrades without the one below it. The kernel-posted-WQE dispatch path is upstream-gated, not a config flag.

Combined with SRD's no-ordering / no-atomics, this is why the proxy is mandatory on EFA today for dispatch. GDAKI is the right answer architecturally — it is simply not a shipping option on this fabric yet. Anyone telling you to "just turn on GPU-initiated dispatch on EFA" is describing InfiniBand. Update 2026-07-17: our §10 campaign measured a counter-free native variant (CQ-poll completion) that runs dispatch+combine correctly and beats the proxy — see §10; the shipping-stack statement above still holds.

6 · The two data paths, layer by layer

Putting it together: the EFA path traverses the full host stack on every all-to-all op; the IBGDA path skips the host entirely. The latency lives in the layers the orange path must climb and the violet path does not.

AWS EFA · SRD — host-proxied (today) InfiniBand · IBGDA — kernel-posted GPU kernel — token routing D2H ring — kernel writes a slot +latency CPU proxy — single-threaded poll + post +latency libfabric — fi_writedata → efa-direct +latency EFA NIC — posts to wire SRD wire — 97.8% idle (issue-bound) host round-trip, per op GPU kernel — builds WQE + rings doorbell D2H ring · CPU proxy · libfabric — skipped entirely — no host on the data path IB HCA — kernel-posted WQE, WriteImm RC wire — ordered, atomic, ~1 doorbell + 1 RTT ~1.5–2 µs, pipelined

EFA (orange): every op climbs five layers — kernel → D2H ring → CPU proxy → libfabric → NIC — before reaching the wire, and the wire sits 97.8% idle waiting for the next issue. IBGDA (violet): the kernel posts the WQE and rings the doorbell directly; the entire host segment is skipped. The latency this whole section quantifies is exactly the height of the orange stack that the violet path does not climb. The D2H ring, CPU proxy, and libfabric layers are measured on the live path; the IBGDA per-op figure is derived from the §2 model.


Takeaway. EFA and InfiniBand have comparable bandwidth (400 GB/s/node here), but MoE dispatch is issue-bound, not bandwidth-bound. On EFA today the CPU proxy adds a fixed +14.7 ms/token decode tax that is load-invariant and cannot be paid back by DeepEP's byte savings, because the wire is already 97.8% idle. The counter-based GDAKI backend — kernel-posted WQE with hardware completion counters — works for barrier but not dispatch, and is gated behind an unshipped three-layer stack plus SRD's missing ordering and atomics. On this fabric, the host proxy is mandatory for dispatch.
All measured values from the efa-gda campaign: 2× p5en.48xlarge, AWS EFA (16 NICs/node, 25 GB/s/NIC effective per PR #612), Qwen3-30B-A3B-FP8 EP16 unless noted. EFA SRD ground truth: no atomics, no ordering, CQ-local, 1-SGE RDMA write. Sources: docs/invariants.md, deepep-calculator/MEASURED-DATA.md. Provenance tags — measured on hardware · derived by arithmetic from measured values · assumed pending verification.