mirror of
https://github.com/hanzoai/papers.git
synced 2026-10-22 17:20:17 +00:00
feat(distributed-inference): cross-vendor TP result + open-problems companion
Four papers on heterogeneous distributed LLM inference: - hanzo-heterogeneous-distributed-inference: cross-vendor (AMD ROCm gfx1151 + NVIDIA GB10 CUDA) tensor-parallel decode over a 2.5GbE cable, coherent at 50.4 tok/s (TTFT 0.09s, 213 tok/s prefill). Root cause of prior corruption proven: per-backend auto-dtype (ROCm->f16, CUDA->bf16) reinterpreted raw on the wire. Fixed by a canonical f32 ring wire + world_size-1 accumulating all-reduce (correct for any N>=2; spurious power-of-two gate removed). Same-vendor f16+f16 control matches token-for-token. - hanzo-distributed-open-problems: NEW companion enumerating 8 open research problems (sampling-determinism boundaries, quant-aware TP sharding, PP layer-assignment ILP, adaptive speculation under RTT/acceptance drift, the prefill launch-overhead wall, KV wire compression, the geo decode latency floor, and cheap verifiable inference), each cross-referenced to the paper that owns the surrounding result. - hanzo-distributed-decode-latency, hanzo-consensus-zap-compute: consistency pass (canonical f32 wire terminology; link goodput unified to 280 MB/s = 2.24 Gbit/s; precise f16-vs-bf16 root-cause wording). INDEX.md regenerated via `make index`; besides the 4 above it also indexes 6 pre-existing root papers the previous index had missed. All four compile clean with pdflatex (10/10/13/9 pages).
This commit is contained in:
@@ -42,14 +42,18 @@ Auto-generated catalogue of research papers. Regenerate: `scripts/gen-index.sh`
|
|||||||
| `hanzo-api-gateway` | ✓ | `hanzo-api-gateway/hanzo-api-gateway.tex` |
|
| `hanzo-api-gateway` | ✓ | `hanzo-api-gateway/hanzo-api-gateway.tex` |
|
||||||
| `hanzo-aso` | ✓ | `hanzo-aso/hanzo-aso.tex` |
|
| `hanzo-aso` | ✓ | `hanzo-aso/hanzo-aso.tex` |
|
||||||
| `hanzo-base-runtime` | ✓ | `hanzo-base-runtime/hanzo-base-runtime.tex` |
|
| `hanzo-base-runtime` | ✓ | `hanzo-base-runtime/hanzo-base-runtime.tex` |
|
||||||
|
| `hanzo-bitexact-oracle` | ✓ | `hanzo-bitexact-oracle.tex` |
|
||||||
| `hanzo-blockchain-platform` | ✓ | `hanzo-blockchain-platform/hanzo-blockchain-platform.tex` |
|
| `hanzo-blockchain-platform` | ✓ | `hanzo-blockchain-platform/hanzo-blockchain-platform.tex` |
|
||||||
| `hanzo-candle` | ✓ | `hanzo-candle/hanzo-candle.tex` |
|
| `hanzo-candle` | ✓ | `hanzo-candle/hanzo-candle.tex` |
|
||||||
| `hanzo-chat` | ✓ | `hanzo-chat/hanzo-chat.tex` |
|
| `hanzo-chat` | ✓ | `hanzo-chat/hanzo-chat.tex` |
|
||||||
| `hanzo-checkout` | ✓ | `hanzo-checkout/hanzo-checkout.tex` |
|
| `hanzo-checkout` | ✓ | `hanzo-checkout/hanzo-checkout.tex` |
|
||||||
| `hanzo-cloud-infrastructure` | ✓ | `hanzo-cloud-infrastructure/hanzo-cloud-infrastructure.tex` |
|
| `hanzo-cloud-infrastructure` | ✓ | `hanzo-cloud-infrastructure/hanzo-cloud-infrastructure.tex` |
|
||||||
|
| `hanzo-collapse-methodology` | ✓ | `hanzo-collapse-methodology.tex` |
|
||||||
| `hanzo-commerce-payments` | ✓ | `hanzo-commerce-payments.tex` |
|
| `hanzo-commerce-payments` | ✓ | `hanzo-commerce-payments.tex` |
|
||||||
|
| `hanzo-comptime-autotune` | ✓ | `hanzo-comptime-autotune.tex` |
|
||||||
| `hanzo-consensus-ai` | ✓ | `hanzo-consensus-ai/hanzo-consensus-ai.tex` |
|
| `hanzo-consensus-ai` | ✓ | `hanzo-consensus-ai/hanzo-consensus-ai.tex` |
|
||||||
| `hanzo-consensus-plugin-platform` | — | `hanzo-consensus-plugin-platform/main.tex` |
|
| `hanzo-consensus-plugin-platform` | — | `hanzo-consensus-plugin-platform/main.tex` |
|
||||||
|
| `hanzo-consensus-zap-compute` | ✓ | `hanzo-consensus-zap-compute.tex` |
|
||||||
| `hanzo-console-dashboard` | ✓ | `hanzo-console-dashboard.tex` |
|
| `hanzo-console-dashboard` | ✓ | `hanzo-console-dashboard.tex` |
|
||||||
| `hanzo-continuous-learning-privacy` | ✓ | `hanzo-continuous-learning-privacy.tex` |
|
| `hanzo-continuous-learning-privacy` | ✓ | `hanzo-continuous-learning-privacy.tex` |
|
||||||
| `hanzo-cross-backend-inference` | ✓ | `hanzo-cross-backend-inference.tex` |
|
| `hanzo-cross-backend-inference` | ✓ | `hanzo-cross-backend-inference.tex` |
|
||||||
@@ -58,6 +62,8 @@ Auto-generated catalogue of research papers. Regenerate: `scripts/gen-index.sh`
|
|||||||
| `hanzo-datastore` | — | `hanzo-datastore/hanzo-datastore.tex` |
|
| `hanzo-datastore` | — | `hanzo-datastore/hanzo-datastore.tex` |
|
||||||
| `hanzo-deepseek-v4-dspark` | — | `hanzo-deepseek-v4-dspark.tex` |
|
| `hanzo-deepseek-v4-dspark` | — | `hanzo-deepseek-v4-dspark.tex` |
|
||||||
| `hanzo-digital-securities-integration` | ✓ | `hanzo-digital-securities-integration/hanzo-digital-securities-integration.tex` |
|
| `hanzo-digital-securities-integration` | ✓ | `hanzo-digital-securities-integration/hanzo-digital-securities-integration.tex` |
|
||||||
|
| `hanzo-distributed-decode-latency` | ✓ | `hanzo-distributed-decode-latency.tex` |
|
||||||
|
| `hanzo-distributed-open-problems` | ✓ | `hanzo-distributed-open-problems.tex` |
|
||||||
| `hanzo-dso` | ✓ | `hanzo-dso/hanzo-dso.tex` |
|
| `hanzo-dso` | ✓ | `hanzo-dso/hanzo-dso.tex` |
|
||||||
| `hanzo-edge-inference` | ✓ | `hanzo-edge-inference.tex` |
|
| `hanzo-edge-inference` | ✓ | `hanzo-edge-inference.tex` |
|
||||||
| `hanzo-encryption-security` | ✓ | `hanzo-encryption-security/hanzo-encryption-security.tex` |
|
| `hanzo-encryption-security` | ✓ | `hanzo-encryption-security/hanzo-encryption-security.tex` |
|
||||||
@@ -67,9 +73,11 @@ Auto-generated catalogue of research papers. Regenerate: `scripts/gen-index.sh`
|
|||||||
| `hanzo-fhe-inference` | ✓ | `hanzo-fhe-inference/hanzo-fhe-inference.tex` |
|
| `hanzo-fhe-inference` | ✓ | `hanzo-fhe-inference/hanzo-fhe-inference.tex` |
|
||||||
| `hanzo-flow` | ✓ | `hanzo-flow/hanzo-flow.tex` |
|
| `hanzo-flow` | ✓ | `hanzo-flow/hanzo-flow.tex` |
|
||||||
| `hanzo-formal-verification` | ✓ | `hanzo-formal-verification/hanzo-formal-verification.tex` |
|
| `hanzo-formal-verification` | ✓ | `hanzo-formal-verification/hanzo-formal-verification.tex` |
|
||||||
|
| `hanzo-fusion-is-composition` | ✓ | `hanzo-fusion-is-composition.tex` |
|
||||||
| `hanzo-gateway-krakend` | ✓ | `hanzo-gateway-krakend.tex` |
|
| `hanzo-gateway-krakend` | ✓ | `hanzo-gateway-krakend.tex` |
|
||||||
| `hanzo-gpu-inference` | ✓ | `hanzo-gpu-inference/hanzo-gpu-inference.tex` |
|
| `hanzo-gpu-inference` | ✓ | `hanzo-gpu-inference/hanzo-gpu-inference.tex` |
|
||||||
| `hanzo-harness-hacking` | ✓ | `hanzo-harness-hacking/hanzo-harness-hacking.tex` |
|
| `hanzo-harness-hacking` | ✓ | `hanzo-harness-hacking/hanzo-harness-hacking.tex` |
|
||||||
|
| `hanzo-heterogeneous-distributed-inference` | ✓ | `hanzo-heterogeneous-distributed-inference.tex` |
|
||||||
| `hanzo-hmm` | ✓ | `hanzo-hmm/hanzo-hmm.tex` |
|
| `hanzo-hmm` | ✓ | `hanzo-hmm/hanzo-hmm.tex` |
|
||||||
| `hanzo-iam-platform` | ✓ | `hanzo-iam-platform.tex` |
|
| `hanzo-iam-platform` | ✓ | `hanzo-iam-platform.tex` |
|
||||||
| `hanzo-ico-infrastructure` | ✓ | `hanzo-ico-infrastructure/hanzo-ico-infrastructure.tex` |
|
| `hanzo-ico-infrastructure` | ✓ | `hanzo-ico-infrastructure/hanzo-ico-infrastructure.tex` |
|
||||||
@@ -77,6 +85,8 @@ Auto-generated catalogue of research papers. Regenerate: `scripts/gen-index.sh`
|
|||||||
| `hanzo-inference-stack` | ✓ | `hanzo-inference-stack.tex` |
|
| `hanzo-inference-stack` | ✓ | `hanzo-inference-stack.tex` |
|
||||||
| `hanzo-ingress-proxy` | ✓ | `hanzo-ingress-proxy.tex` |
|
| `hanzo-ingress-proxy` | ✓ | `hanzo-ingress-proxy.tex` |
|
||||||
| `hanzo-insights-analytics` | ✓ | `hanzo-insights-analytics.tex` |
|
| `hanzo-insights-analytics` | ✓ | `hanzo-insights-analytics.tex` |
|
||||||
|
| `hanzo-int8-mmq-negative-result` | ✓ | `hanzo-int8-mmq-negative-result.tex` |
|
||||||
|
| `hanzo-intrinsic-islands` | ✓ | `hanzo-intrinsic-islands.tex` |
|
||||||
| `hanzo-jin-architecture` | ✓ | `hanzo-jin-architecture/hanzo-jin-architecture.tex` |
|
| `hanzo-jin-architecture` | ✓ | `hanzo-jin-architecture/hanzo-jin-architecture.tex` |
|
||||||
| `hanzo-jin` | ✓ | `hanzo-jin/hanzo-jin.tex` |
|
| `hanzo-jin` | ✓ | `hanzo-jin/hanzo-jin.tex` |
|
||||||
| `hanzo-kernel-dsl` | ✓ | `hanzo-kernel-dsl.tex` |
|
| `hanzo-kernel-dsl` | ✓ | `hanzo-kernel-dsl.tex` |
|
||||||
@@ -203,4 +213,4 @@ Auto-generated catalogue of research papers. Regenerate: `scripts/gen-index.sh`
|
|||||||
| `ZEN_WHITEPAPER_2025` | ✓ | `zen/ZEN_WHITEPAPER_2025.tex` |
|
| `ZEN_WHITEPAPER_2025` | ✓ | `zen/ZEN_WHITEPAPER_2025.tex` |
|
||||||
| `zen-world` | ✓ | `zen/zen-world.tex` |
|
| `zen-world` | ✓ | `zen/zen-world.tex` |
|
||||||
|
|
||||||
**Total**: 198 papers, 192 with compiled PDFs
|
**Total**: 208 papers, 202 with compiled PDFs
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,714 @@
|
|||||||
|
\documentclass[11pt]{article}
|
||||||
|
\input{shared/kernel-preamble.tex}
|
||||||
|
\usepackage{amsthm}
|
||||||
|
|
||||||
|
\newtheorem{proposition}{Proposition}
|
||||||
|
|
||||||
|
% Honesty labels: distinguish what is built from what is designed.
|
||||||
|
\newcommand{\built}{\textcolor{str}{\small\textbf{[built]}}}
|
||||||
|
\newcommand{\partialb}{\textcolor{kw}{\small\textbf{[partial]}}}
|
||||||
|
\newcommand{\design}{\textcolor{cmt}{\small\textbf{[design]}}}
|
||||||
|
|
||||||
|
\title{\textbf{The Three-Plane Compute Fabric:\\
|
||||||
|
Consensus-Managed, ZAP-Transported\\ Decentralized AI Inference}}
|
||||||
|
\author{Hanzo AI --- ML Systems}
|
||||||
|
\date{Architecture Blueprint --- \emph{One Source, Every Backend} series, 2026}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\maketitle
|
||||||
|
|
||||||
|
\begin{abstract}
|
||||||
|
A heterogeneous GPU fleet already runs a single model across ROCm, CUDA, and Metal
|
||||||
|
today: the Hanzo engine's socket-ring shards a model tensor- and pipeline-parallel across
|
||||||
|
mismatched accelerators over a canonical \code{f32} wire, and a \code{hanzo cluster}
|
||||||
|
command onboards a worker by scanning a QR token. But that ring is configured by a static
|
||||||
|
JSON file handed out of band, and its bytes cross the wire as \emph{plaintext TCP} between
|
||||||
|
nodes that never authenticate each other. It is a LAN appliance, not a network. This paper
|
||||||
|
is the blueprint for the missing two-thirds: turning that ring into a permissionless,
|
||||||
|
byzantine-fault-tolerant AI inference \emph{network} that any developer --- or any
|
||||||
|
public-cloud GPU miner --- can join and be paid for useful work. The thesis is one of
|
||||||
|
\emph{decomplection}: a serving cluster has three concerns that monolithic stacks braid
|
||||||
|
together and we hold apart as three orthogonal, independently-complete planes. The
|
||||||
|
\textbf{control plane} is a Lux \emph{Quasar} consensus chain: membership, permissionless
|
||||||
|
miner admission, layer placement, and config distribution become BFT-agreed state, with the
|
||||||
|
Photon committee-selection primitive (Fisher--Yates over a luminance-weighted population)
|
||||||
|
reused to sample GPU nodes and luminance re-bound from consensus-vote success to
|
||||||
|
\emph{validated inference contribution}. The \textbf{transport} is \emph{ZAP}, the Lux
|
||||||
|
zero-copy wire, whose post-quantum handshake (\code{WrapPQ} over an authenticated
|
||||||
|
\code{Session} exposing a verified 32-byte \code{PeerID}) carries \emph{both} consensus
|
||||||
|
control messages \emph{and} tensor activations over one PQ-authenticated channel --- retiring
|
||||||
|
the plaintext ring sockets and converting the QR join token from a bearer secret into a
|
||||||
|
handshake credential. The \textbf{data plane} is the existing inference ring, unchanged in
|
||||||
|
kind: it receives its topology from consensus instead of a JSON file and moves its tensors
|
||||||
|
over ZAP instead of raw TCP. We are scrupulous about the built/designed line. Built and
|
||||||
|
cited to source: the cross-vendor ring, the \code{hanzo cluster} scan-to-join CLI, the ZAP
|
||||||
|
Go wire with its PQ handshake, the Quasar engine with Photon/Wave/Focus and BLS$+$Pulsar$+$ML-DSA
|
||||||
|
threshold signing, and the ZAP measurements ($17\times$ serialization and $345\times$ parse
|
||||||
|
versus JSON-RPC, $\sim$$4.5\times$ versus gRPC with zero marshal/unmarshal). Designed, and
|
||||||
|
labelled as such: consensus-driven membership, the ZAP-swapped ring transport, miner
|
||||||
|
admission, and result verification by threshold-signing over inference outputs. No benchmark
|
||||||
|
here is invented; every measured number is attributed to the code that produced it. The
|
||||||
|
contribution is the architecture and its honest status, offered as a blueprint the wider
|
||||||
|
AI-infrastructure community can build on.
|
||||||
|
\end{abstract}
|
||||||
|
|
||||||
|
\section{Introduction}
|
||||||
|
\label{sec:intro}
|
||||||
|
|
||||||
|
The dominant shape of a model-serving stack is a monolith: one operator, one datacenter, one
|
||||||
|
process tree that owns scheduling, transport, and tensor math all at once. vLLM, TGI, and
|
||||||
|
Triton each fuse ``who is in the cluster,'' ``how bytes move between ranks,'' and ``what the
|
||||||
|
GPUs compute'' into a single trusted binary on a private network. That fusion is fine when the
|
||||||
|
operator owns every GPU. It fails the moment the fleet is \emph{heterogeneous} (a ROCm mini-PC,
|
||||||
|
a rented CUDA instance, an Apple laptop), \emph{permissionless} (a stranger's GPU wants to
|
||||||
|
contribute and be paid), or \emph{untrusted} (the node holding your activations is not yours).
|
||||||
|
None of those three failures is a math problem; each is a \emph{concerns} problem. The cluster
|
||||||
|
braids together things that want to be separate.
|
||||||
|
|
||||||
|
This paper takes the opposite stance, the one our engineering culture states as a law: one and
|
||||||
|
only one way to do everything, composable and orthogonal. A decentralized inference cluster has
|
||||||
|
exactly three concerns, and we give each its own plane:
|
||||||
|
\begin{itemize}[leftmargin=1.4em,itemsep=2pt]
|
||||||
|
\item \textbf{Control} --- who is in the cluster, who may join, which node holds which layers,
|
||||||
|
and what the run configuration is. This is agreement among mutually distrusting parties: a
|
||||||
|
\emph{consensus} problem.
|
||||||
|
\item \textbf{Transport} --- how a byte gets from one node to another, and how each end knows
|
||||||
|
who the other is. This is an \emph{authenticated wire} problem.
|
||||||
|
\item \textbf{Data} --- the actual tensor exchange: activations forward, gradients or
|
||||||
|
all-reduce sideways. This is a \emph{numerics and topology} problem.
|
||||||
|
\end{itemize}
|
||||||
|
The claim is that these three are genuinely orthogonal, that Hanzo and Lux already ship a
|
||||||
|
best-in-class primitive for each, and that composing them --- rather than reinventing a
|
||||||
|
monolith --- yields something no monolith can be: a GPU network anyone can join. The control
|
||||||
|
primitive is Lux \emph{Quasar} consensus~\cite{quasar,luxconsensus}; the transport is
|
||||||
|
\emph{ZAP}~\cite{zap,lp201}; the data plane is the Hanzo cross-backend inference
|
||||||
|
engine~\cite{infstack}.
|
||||||
|
|
||||||
|
\paragraph{The honesty frame.} This is an architecture paper, and its credibility rests on not
|
||||||
|
blurring what runs today with what is designed. We adopt three labels, used inline throughout
|
||||||
|
and collected in the status table of \S\ref{sec:status}: \built{} for code on \code{main},
|
||||||
|
cited to its file; \partialb{} for a primitive that is compiled and exercised but not yet wired
|
||||||
|
end-to-end; and \design{} for a proposed composition. The measured numbers we quote (ring
|
||||||
|
parity across vendors, ZAP throughput and latency, Photon and luminance costs) are all \built{},
|
||||||
|
each attributed to the benchmark that produced it. The network-level assembly --- consensus as
|
||||||
|
the live scheduler, the ring running over ZAP, miner admission and payment --- is \design{},
|
||||||
|
and never dressed as more.
|
||||||
|
|
||||||
|
\paragraph{Contribution.} We (i) inventory the built substrate with source citations
|
||||||
|
(\S\ref{sec:today}); (ii) state the three-plane architecture and prove its concerns are
|
||||||
|
separable (\S\ref{sec:planes}); (iii) give the threat model and a result-integrity scheme for
|
||||||
|
byzantine miners (\S\ref{sec:threat}); (iv) bind the economic layer to the existing luminance
|
||||||
|
primitive (\S\ref{sec:econ}); (v) lay out a staged migration from the static-config LAN cluster
|
||||||
|
to a public network (\S\ref{sec:migration}); and (vi) publish an honest built-versus-designed
|
||||||
|
status table (\S\ref{sec:status}) and the limitations (\S\ref{sec:limits}).
|
||||||
|
|
||||||
|
\section{The substrate that exists today}
|
||||||
|
\label{sec:today}
|
||||||
|
|
||||||
|
Before proposing a network we account for what is already on \code{main}. Every claim in this
|
||||||
|
section is \built{} and cites the file that implements it. The point of the inventory is that
|
||||||
|
the three planes are not vaporware waiting to be written: each has a mature primitive, and the
|
||||||
|
work is composition, not invention.
|
||||||
|
|
||||||
|
\subsection{Data plane: the heterogeneous inference ring}
|
||||||
|
\label{sec:ring}
|
||||||
|
|
||||||
|
The Hanzo engine already runs one model split across several GPUs of \emph{different vendors}.
|
||||||
|
Its \code{distributed} module (\code{hanzo-engine/src/distributed.rs},
|
||||||
|
\code{hanzo-quant/src/distributed/}) provides two backends behind one \code{Comm} abstraction:
|
||||||
|
NCCL for homogeneous NVIDIA, and a portable \emph{ring} backend for everything else. The ring
|
||||||
|
does tensor-parallel and pipeline-parallel sharding by exchanging activations with its
|
||||||
|
right-hand neighbor, rank $r$ dialing rank $(r{+}1) \bmod N$ in an all-reduce cycle
|
||||||
|
(\code{hanzo-quant/src/distributed/socket.rs}). Cross-vendor generation works because the ring
|
||||||
|
wire is a \emph{canonical} \code{f32} format and reductions take a backend-independent
|
||||||
|
CPU-\code{f32} path with a canonical tie-break (\code{hanzo-engine/src/ops.rs}), so a ROCm box
|
||||||
|
and a CUDA box agree bit-for-bit on the reduced tensor rather than each rank reinterpreting the
|
||||||
|
other's raw 16-bit words in the wrong \code{f16}-versus-\code{bf16} layout. The topology is a static struct read from a JSON file
|
||||||
|
named by the \code{RING\_CONFIG} environment variable (Listing~\ref{lst:ringcfg}): master
|
||||||
|
address, per-rank ports, this rank's index, and the world size. The cross-backend engine
|
||||||
|
underneath --- five backends, the full GGUF quant zoo, decode parity with \code{llama.cpp} --- is
|
||||||
|
the subject of the companion stack paper~\cite{infstack}; here it is simply the data plane.
|
||||||
|
|
||||||
|
\begin{lstlisting}[caption={The data-plane topology today: a static \code{RING\_CONFIG} JSON.
|
||||||
|
Every node needs the full member list up front; nothing authenticates a peer.},label={lst:ringcfg}]
|
||||||
|
{ "master_ip": "10.0.0.1", "master_port": 8000,
|
||||||
|
"port": 8001, "right_ip": "10.0.0.2", "right_port": 8001,
|
||||||
|
"rank": 0, "world_size": 4 }
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
Two properties of this substrate matter for everything below. First, it is \emph{correct across
|
||||||
|
vendors} --- the hard part, cross-backend numeric agreement, is solved. Second, it is
|
||||||
|
\emph{unauthenticated plaintext}: the ring exchanges raw bytes over \code{std::net::TcpStream}
|
||||||
|
with \code{write\_all}/\code{read\_exact} and ships its control messages as \code{serde\_json}
|
||||||
|
over the same socket. It is safe only on a trusted private mesh. That is the gap the transport
|
||||||
|
plane closes.
|
||||||
|
|
||||||
|
\subsection{The onboarding UX: \code{hanzo cluster} scan-to-join}
|
||||||
|
\label{sec:clustercli}
|
||||||
|
|
||||||
|
Handing every node a JSON file out of band does not scale past a lab. The \code{hanzo cluster}
|
||||||
|
subcommand (\code{hanzo-cli/src/cluster.rs}) already replaces it with a scan-to-join flow.
|
||||||
|
\code{hanzo cluster init} on the head (rank 0) enumerates the ring, prints a \emph{join token}
|
||||||
|
and its QR code, and launches; \code{hanzo cluster join} on a worker decodes the token and
|
||||||
|
launches as a ring daemon. The token is a \code{bs58}-encoded JSON \code{JoinToken}
|
||||||
|
(Listing~\ref{lst:token}) --- a nonce, the head address, the world size, the ordered ring
|
||||||
|
addresses, and the model --- compact enough to fit in one QR or one paste. This is the correct
|
||||||
|
\emph{shape} of onboarding: a worker needs one string to join. But observe what the token is
|
||||||
|
today: a \emph{bearer credential in cleartext}. Anyone who photographs the QR can join the ring,
|
||||||
|
and the ring cannot tell an invited worker from an intruder, because the token carries topology,
|
||||||
|
not identity. Holding that observation is the whole of \S\ref{sec:threat}.
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=Rust,caption={The join token (\code{hanzo-cli/src/cluster.rs}):
|
||||||
|
\code{bs58(JSON)} so it fits one QR. It is topology, not identity --- a bearer secret today.},label={lst:token}]
|
||||||
|
struct JoinToken {
|
||||||
|
nonce: u64, // ring id, printed {:016x}
|
||||||
|
master: String, // rank-0 (head) reachable IP
|
||||||
|
world_size: usize,
|
||||||
|
addrs: Option<Vec<String>>, // full ordered ring [rank0..rankN-1]
|
||||||
|
model: String,
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\subsection{Transport: ZAP, the zero-copy post-quantum wire}
|
||||||
|
\label{sec:zap}
|
||||||
|
|
||||||
|
ZAP (Zero-copy Application Protocol, \code{github.com/luxfi/zap}) is the Lux universal
|
||||||
|
wire~\cite{zap,lp201}. Its serialization is zero-copy: a message is read directly out of the
|
||||||
|
network buffer with no parse and no allocation (measured \built{} parse $2.9$~ns/op at zero
|
||||||
|
allocations; build $44$~ns/op). Against the protocols an AI stack would otherwise reach for,
|
||||||
|
the wire eliminates the marshal/unmarshal step entirely: $17\times$ faster serialization and
|
||||||
|
$345\times$ faster parse than JSON-RPC (the MCP transport), $\sim$$157\times$ faster than
|
||||||
|
protobuf on deserialization ($21$~ns versus $3231$~ns, zero allocations), and $\sim$$4.5\times$
|
||||||
|
faster than gRPC with the whole codec layer deleted rather than optimized~\cite{zap,lp201,luxconsensus}.
|
||||||
|
End-to-end it sustains $114$K transactions/s on a single connection and $376$K across $20$
|
||||||
|
parallel connections~\cite{luxconsensus}.
|
||||||
|
|
||||||
|
For a public network the decisive feature is not speed but \emph{authenticated identity}. ZAP's
|
||||||
|
PQ layer (\code{conn\_pq.go}) wraps an established connection \emph{after} a post-quantum
|
||||||
|
handshake: \code{handshake.Initiator.Run(tcp)} produces a \code{Session}, and \code{WrapPQ(tcp,
|
||||||
|
sess)} returns an ordinary \code{net.Conn} that AEAD-frames every write, zeroes its keys on
|
||||||
|
close, and --- critically --- exposes \code{PeerID() [32]byte}, ``the verified peer identity for
|
||||||
|
callers that need to route on it (e.g.\ validator-set membership checks).'' That one method is
|
||||||
|
the hinge of the entire security argument (\S\ref{sec:threat}): the transport already hands the
|
||||||
|
control plane a cryptographically verified identity for the node on the other end.
|
||||||
|
|
||||||
|
Inside the Hanzo engine, ZAP is a first-class module (\code{hanzo-engine/src/zap/}). Its
|
||||||
|
message and adapter surface is compiled and exercised in-process --- \code{ZapInferenceServer}
|
||||||
|
translates ZAP \code{ChatRequest}s into the engine's real \code{Request} channel and streams
|
||||||
|
\code{ChatChunk}s back, satisfying the same \code{ZapInference} trait a networked client would
|
||||||
|
(\code{server.rs}, \code{client.rs}). We mark this \partialb{}, honestly: the out-of-process
|
||||||
|
\code{zap://} transport is a documented stub that carries no \code{capnp} build dependency yet
|
||||||
|
(``flipping on the wire is mechanical,'' \code{transport.rs}), and a few methods
|
||||||
|
(\code{tokenize}, \code{detokenize}, \code{re\_isq}) return ``not yet wired.'' The wire exists
|
||||||
|
in Go and is production-deployed across Lux consensus, MPC, and DEX systems; the engine-side
|
||||||
|
Rust surface is built but not yet turned on over a socket.
|
||||||
|
|
||||||
|
\subsection{Control: the Quasar consensus engine}
|
||||||
|
\label{sec:quasar}
|
||||||
|
|
||||||
|
Lux \emph{Quasar} (\code{github.com/luxfi/consensus}) is a DAG-native, post-quantum BFT
|
||||||
|
consensus engine~\cite{quasar,luxconsensus}. It is built from small, orthogonal sub-protocols,
|
||||||
|
four of which this paper reuses directly:
|
||||||
|
\begin{itemize}[leftmargin=1.4em,itemsep=2pt]
|
||||||
|
\item \textbf{Photon} (\code{protocol/photon}) selects a $K$-of-$N$ committee each round by
|
||||||
|
Fisher--Yates shuffle over the node population, drawing with a rejection-sampled uniform integer
|
||||||
|
(\code{emitter.go}) to remove the modulo bias that a grinding adversary could exploit on
|
||||||
|
committee sampling. The population is weighted by \emph{luminance}.
|
||||||
|
\item \textbf{Luminance} (\code{photon/luminance.go}) is a per-node reputation in ``lux'' units:
|
||||||
|
each node starts at $100$, a successful vote multiplies its brightness by $1.1$ (capped at
|
||||||
|
$1000$), a failure by $0.9$ (floored at $10$), and \code{Brightness} normalizes to a selection
|
||||||
|
weight in $[0.1, 10]$. It is a cheap, bounded, exponential reputation --- a brightness update
|
||||||
|
costs $72$~ns \built{}.
|
||||||
|
\item \textbf{Wave} (\code{protocol/wave}) runs per-round threshold voting with Fast
|
||||||
|
Probabilistic Consensus; a vote round costs $3.38$~\textmu s and a $K$-of-$N$ Photon selection
|
||||||
|
$3.03$~\textmu s \built{}.
|
||||||
|
\item \textbf{Quasar} (\code{protocol/quasar}) seals decisions with threshold signatures across
|
||||||
|
three independent cryptographic paths --- BLS12-381 (48-byte aggregate), Pulsar Ring-LWE
|
||||||
|
threshold, and ML-DSA-65 (FIPS 204, rolled to a 192-byte Groth16 proof) --- each independently
|
||||||
|
toggleable, all three in parallel under \code{TripleSignRound1}. A QuasarCert verifies in
|
||||||
|
$\sim$$2$--$5$~ms on CPU, GPU-batchable $10$--$100\times$~\cite{luxconsensus}.
|
||||||
|
\end{itemize}
|
||||||
|
Quasar's own inter-node transport is already ZAP, not gRPC or libp2p (\code{consensus/LLM.md}).
|
||||||
|
Control and transport are, in the Lux stack, \emph{already} composed; what is missing is binding
|
||||||
|
that composition to the \emph{GPU} population instead of the validator population.
|
||||||
|
|
||||||
|
\section{The three-plane architecture}
|
||||||
|
\label{sec:planes}
|
||||||
|
|
||||||
|
The blueprint is a single sentence: \emph{run the inference ring's topology as Quasar consensus
|
||||||
|
state, and move every byte --- control and tensor alike --- over ZAP.} Figure~\ref{fig:planes}
|
||||||
|
is the whole system. What follows gives each plane its interface and states, as a proposition,
|
||||||
|
why the three do not leak into each other.
|
||||||
|
|
||||||
|
\begin{figure}[t]
|
||||||
|
\centering
|
||||||
|
\small
|
||||||
|
\begin{tabular}{@{}l@{}}
|
||||||
|
\toprule
|
||||||
|
\textbf{Control plane} --- Quasar consensus (BFT, post-quantum) \hfill \design{} \\
|
||||||
|
\quad Photon committee over GPU nodes $\cdot$ luminance = useful-work weight \\
|
||||||
|
\quad membership $\cdot$ miner admission $\cdot$ layer placement $\cdot$ config $\cdot$ result attest \\
|
||||||
|
\midrule
|
||||||
|
\textbf{Transport plane} --- ZAP PQ wire \hfill \built{}\,/\,\partialb{} \\
|
||||||
|
\quad \code{WrapPQ} session $\cdot$ verified \code{PeerID} $\cdot$ AEAD frames \\
|
||||||
|
\quad one channel for BOTH control messages AND tensor activations \\
|
||||||
|
\midrule
|
||||||
|
\textbf{Data plane} --- Hanzo inference ring \hfill \built{} \\
|
||||||
|
\quad tensor/pipeline parallel $\cdot$ canonical \code{f32} all-reduce $\cdot$ 5 backends \\
|
||||||
|
\quad topology handed down by control; bytes carried by transport \\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabular}
|
||||||
|
\caption{Three orthogonal planes. Each is independently complete and separately citable to
|
||||||
|
code; the network is their composition, not a new monolith. Labels mark built versus designed
|
||||||
|
per \S\ref{sec:status}.}
|
||||||
|
\label{fig:planes}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\subsection{Control plane: consensus as the scheduler}
|
||||||
|
\label{sec:control}
|
||||||
|
|
||||||
|
The control plane replaces the static \code{RING\_CONFIG} JSON (Listing~\ref{lst:ringcfg}) and
|
||||||
|
the out-of-band QR token with \emph{agreed state on a Quasar chain}. The chain's VM state
|
||||||
|
\emph{is} the cluster's control surface \design{}:
|
||||||
|
\begin{itemize}[leftmargin=1.4em,itemsep=2pt]
|
||||||
|
\item \textbf{Membership} --- the set of GPU nodes, each identified by its ZAP \code{PeerID}
|
||||||
|
(an ML-DSA public key), with advertised capability (backend, VRAM, resident models, measured
|
||||||
|
decode/prefill throughput). Joining and leaving are transactions, so the member list is
|
||||||
|
BFT-agreed rather than hand-distributed.
|
||||||
|
\item \textbf{Miner admission} --- a permissionless join. A public-cloud GPU submits a join
|
||||||
|
transaction carrying its \code{PeerID} and a stake bond; admission is a consensus decision, not
|
||||||
|
an operator's SSH session. This is the step that turns ``my four boxes'' into ``anyone's GPU''
|
||||||
|
(\S\ref{sec:econ}).
|
||||||
|
\item \textbf{Placement / routing} --- which node holds which contiguous layer range of which
|
||||||
|
model. Pipeline-parallel assignment (node $A$ holds layers $0$--$7$, $B$ holds $8$--$15$) and
|
||||||
|
the ring order become an on-chain placement map. A request is routed by reading that map, not by
|
||||||
|
trusting a JSON file.
|
||||||
|
\item \textbf{Config distribution} --- model id, quantization, sampling defaults, ring
|
||||||
|
\code{world\_size}: the fields of \code{JoinToken} and \code{RingConfig} become chain state, so
|
||||||
|
every node reads an identical, agreed configuration.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
The mechanism is not new code so much as a \emph{re-population} of Photon. Photon selects a
|
||||||
|
committee by weighted Fisher--Yates over a node set; point it at the \emph{GPU} population and it
|
||||||
|
selects a committee of GPU nodes --- to ratify a placement, to admit a miner, or to verify a
|
||||||
|
result (\S\ref{sec:threat}). Luminance, today a function of consensus-vote success, is re-bound
|
||||||
|
to \emph{validated inference contribution}: a node whose outputs pass verification brightens and
|
||||||
|
is preferentially selected and rewarded; a node that stalls or returns wrong tensors dims and is
|
||||||
|
sampled out. The primitive is unchanged --- an exponential, bounded, cheap reputation --- only
|
||||||
|
the event that drives \code{Illuminate(id, success)} moves from ``voted with the quorum'' to
|
||||||
|
``did correct inference work.'' Crucially, \emph{writes to consensus are rare and
|
||||||
|
slow-changing}: placement and membership shift on the order of nodes joining and models loading,
|
||||||
|
not on the order of tokens generated. The hot path --- a forward pass through the ring --- never
|
||||||
|
touches consensus.
|
||||||
|
|
||||||
|
\subsection{Transport plane: one PQ wire for control and tensors}
|
||||||
|
\label{sec:transport}
|
||||||
|
|
||||||
|
Every inter-node byte moves over ZAP. This is deliberately \emph{one} wire for two kinds of
|
||||||
|
traffic that a monolith would split across two stacks:
|
||||||
|
\begin{itemize}[leftmargin=1.4em,itemsep=2pt]
|
||||||
|
\item \textbf{Control messages} --- consensus votes, placement updates, admission requests ---
|
||||||
|
which Quasar already speaks over ZAP.
|
||||||
|
\item \textbf{Tensor data} --- the activation exchange and all-reduce that the ring performs
|
||||||
|
every micro-step --- which today crosses raw TCP and, under this design, crosses the same
|
||||||
|
authenticated \code{WrapPQ} \code{net.Conn} instead.
|
||||||
|
\end{itemize}
|
||||||
|
Unifying them on one wire is the payoff of decomplection: the ring does not need to know it is on
|
||||||
|
a network of strangers, only that it holds a \code{net.Conn}; the transport does not need to know
|
||||||
|
whether the bytes are a vote or an activation. ZAP's zero-copy framing is what makes carrying
|
||||||
|
tensors over an authenticated session affordable --- there is no marshal/unmarshal tax on the
|
||||||
|
activation exchange, only an AEAD seal over a buffer the ring already owns (the $64$~KiB
|
||||||
|
\code{MaxPQRecord} chunking of \code{conn\_pq.go} maps naturally onto activation-sized writes).
|
||||||
|
The identity that the handshake establishes, \code{PeerID}, is the same value the control plane
|
||||||
|
keys membership on: the node you complete a ZAP handshake with \emph{is} the node consensus
|
||||||
|
admitted, by construction, with no separate PKI to reconcile.
|
||||||
|
|
||||||
|
\subsection{Data plane: unchanged in kind}
|
||||||
|
\label{sec:data}
|
||||||
|
|
||||||
|
The data plane is the least-changed and that is the point. The ring's numerics --- canonical
|
||||||
|
\code{f32} reduction, tensor/pipeline sharding, the five-backend kernels --- are correct today
|
||||||
|
(\S\ref{sec:ring}) and stay exactly as they are. Two wires change underneath them: the topology
|
||||||
|
arrives from the control plane's placement map rather than \code{RingConfig::load()}, and the
|
||||||
|
\code{TcpStream} is a ZAP \code{net.Conn} rather than a bare socket. The ring code that computes
|
||||||
|
does not move. This is composition over rewrite: we are not building a decentralized inference
|
||||||
|
engine, we are giving the inference engine we have a control plane and an authenticated
|
||||||
|
transport.
|
||||||
|
|
||||||
|
\subsection{Why three planes, stated precisely}
|
||||||
|
\label{sec:whythree}
|
||||||
|
|
||||||
|
\begin{proposition}[Orthogonality of the three planes]
|
||||||
|
\label{prop:ortho}
|
||||||
|
The control, transport, and data planes can each be replaced or scaled independently without
|
||||||
|
modifying the other two, because they share only two narrow, typed interfaces: the control plane
|
||||||
|
emits a \emph{placement map} keyed by \code{PeerID}, and the transport plane emits an
|
||||||
|
authenticated \code{net.Conn} whose \code{PeerID} matches. The data plane consumes both and
|
||||||
|
exposes neither consensus nor cryptography; the transport consumes an identity and exposes a
|
||||||
|
byte stream, knowing nothing of tensors; the control consumes admission and contribution events
|
||||||
|
and exposes agreed state, knowing nothing of wire framing.
|
||||||
|
\end{proposition}
|
||||||
|
|
||||||
|
\begin{proof}[Argument]
|
||||||
|
By interface inspection. The data plane's dependencies are exactly (i) a topology --- rank,
|
||||||
|
world size, neighbor address, layer range --- and (ii) a \code{net.Conn} per neighbor. Neither
|
||||||
|
names a consensus type or a cipher. Swap Quasar for any state machine that produces the same
|
||||||
|
placement map and the ring is unaffected; swap ZAP for any authenticated \code{net.Conn} with a
|
||||||
|
stable peer identity and the ring is unaffected; swap the ring's kernels (add a backend, change
|
||||||
|
the quant) and neither consensus nor transport observes it. The only cross-plane invariant is
|
||||||
|
that the \code{PeerID} the transport authenticates equals the \code{PeerID} the control plane
|
||||||
|
admitted --- a single equality, checked once at connection setup, which is precisely what
|
||||||
|
\code{WrapPQ}'s \code{PeerID()} was built to support (``validator-set membership checks,''
|
||||||
|
\code{conn\_pq.go}). Three concerns, two interfaces, one shared value. \qed
|
||||||
|
\end{proof}
|
||||||
|
|
||||||
|
The contrast with a monolithic serving stack is now sharp. In vLLM or Triton, ``who is in the
|
||||||
|
cluster'' is a launch-time flag baked into the process, ``how bytes move'' is a hardcoded NCCL
|
||||||
|
or gRPC channel with no peer authentication, and ``what computes'' is the same binary --- the
|
||||||
|
three concerns are one artifact, so you cannot add a permissionless member, cannot authenticate
|
||||||
|
a stranger's GPU, and cannot verify a result without forking the whole thing. Our planes are
|
||||||
|
three artifacts with two seams. That is the difference between a cluster and a network.
|
||||||
|
|
||||||
|
\section{Threat model and result integrity}
|
||||||
|
\label{sec:threat}
|
||||||
|
|
||||||
|
A permissionless network inherits adversaries a private cluster never has. We state the model,
|
||||||
|
the gaps in today's substrate, and the design that closes them.
|
||||||
|
|
||||||
|
\paragraph{Assets and adversaries.} The assets are: the integrity of an inference result (the
|
||||||
|
tokens a user is billed for must be the model's real output), the confidentiality and
|
||||||
|
authenticity of activations in flight, the correctness of the placement map, and the fairness of
|
||||||
|
reward. The adversaries are: a network attacker on the path between nodes (public-cloud tenants
|
||||||
|
share fabric); an \emph{intruder} who obtained a join token; and a \emph{byzantine miner} who
|
||||||
|
joined honestly but returns wrong, fabricated, or lazily-cached tensors to earn reward without
|
||||||
|
doing the work.
|
||||||
|
|
||||||
|
\paragraph{Gap 1: the wire is plaintext (\built{} problem).} Today the ring is raw TCP
|
||||||
|
(\S\ref{sec:ring}). A path attacker can read activations, inject a forged tensor into the
|
||||||
|
all-reduce, or man-in-the-middle a neighbor. \textbf{Closure \design{}:} the ZAP PQ handshake.
|
||||||
|
After \code{WrapPQ}, every frame is AEAD-sealed and every peer is a verified ML-DSA identity;
|
||||||
|
injection and eavesdropping both fail against a post-quantum authenticated channel. The tensor
|
||||||
|
plane inherits the same wire security consensus already relies on.
|
||||||
|
|
||||||
|
\paragraph{Gap 2: the join token is a bearer secret (\built{} problem).} The QR token
|
||||||
|
(Listing~\ref{lst:token}) is cleartext topology; whoever holds it joins (\S\ref{sec:clustercli}).
|
||||||
|
\textbf{Closure \design{}:} make the token a \emph{handshake credential}, not the thing that
|
||||||
|
grants access. Under the design the token carries the ring's \code{PeerID} set (or a commitment
|
||||||
|
to it); a worker presenting the token must then \emph{complete a ZAP PQ handshake} proving
|
||||||
|
possession of an admitted ML-DSA key. A photographed QR no longer joins anyone --- it only names
|
||||||
|
who is allowed to, and access is gated on the handshake the intruder cannot forge. The token
|
||||||
|
degrades from a secret to a public address book, which is the correct security posture: identity
|
||||||
|
is the credential, topology is not.
|
||||||
|
|
||||||
|
\paragraph{Gap 3: a miner may lie about its work (the hard one).} Authentication proves
|
||||||
|
\emph{who} sent a tensor, not that the tensor is \emph{correct}. A byzantine miner holding layers
|
||||||
|
$8$--$15$ can return plausible-looking garbage and collect reward. This is the genuinely new
|
||||||
|
problem, and consensus is the tool. We propose three composable mechanisms, in increasing cost,
|
||||||
|
scheduled by the control plane \design{}:
|
||||||
|
\begin{enumerate}[leftmargin=1.5em,itemsep=2pt]
|
||||||
|
\item \textbf{Threshold attestation of outputs.} A completed inference (or a placement-epoch's
|
||||||
|
batch of them) is sealed with a QuasarCert: a Photon committee of GPU nodes threshold-signs the
|
||||||
|
$(\text{request}, \text{output})$ pair with BLS$+$Pulsar$+$ML-DSA (\S\ref{sec:quasar}). A billed
|
||||||
|
result carries a certificate that a supermajority attested to it, giving the user cryptographic
|
||||||
|
non-repudiation and making a single lying miner insufficient to certify a bad output.
|
||||||
|
\item \textbf{Redundant re-execution / spot checks.} The scheduler occasionally assigns the same
|
||||||
|
layer range to two independent miners and compares --- cheap because the ring wire is already
|
||||||
|
\emph{canonical} \code{f32} (\S\ref{sec:ring}), so two honest miners on different vendors produce
|
||||||
|
\emph{bit-identical} tensors and any disagreement is a proof of fault, not a rounding artifact.
|
||||||
|
The canonical-\code{f32} property, built for cross-vendor correctness, doubles as the substrate
|
||||||
|
for verifiable redundancy. A miner caught disagreeing with the honest majority is dimmed by
|
||||||
|
luminance and slashed.
|
||||||
|
\item \textbf{Reputation as the economic backstop.} Luminance (\S\ref{sec:quasar}) makes
|
||||||
|
correct work compound and incorrect work decay: a node that fails verification multiplies its
|
||||||
|
brightness by $0.9$ and is sampled into fewer committees and paid for less work, converging its
|
||||||
|
influence and income toward zero. Reputation is the slow, cheap layer under the fast, expensive
|
||||||
|
cryptographic ones.
|
||||||
|
\end{enumerate}
|
||||||
|
Byzantine fault tolerance is a property the control plane already has (Quasar tolerates $<1/3$
|
||||||
|
adversarial stake); the contribution here is \emph{extending it from block finality to inference
|
||||||
|
finality} by threshold-signing over outputs and exploiting canonical \code{f32} for cheap
|
||||||
|
re-execution. We are explicit that this is \design{}: the threshold-signing primitive is built,
|
||||||
|
its application to inference outputs is proposed.
|
||||||
|
|
||||||
|
\paragraph{Non-goal: confidentiality from the operator.} This design authenticates and verifies;
|
||||||
|
it does not, by itself, hide activations from the miner that computes them. Workloads needing
|
||||||
|
that require a hardware TEE tier (H100/H200 confidential computing, TDX, SEV-SNP), an orthogonal
|
||||||
|
extension developed in the tenant-cloud companion~\cite{tenantcloud} and out of scope here.
|
||||||
|
|
||||||
|
\section{The economic layer: luminance as useful-work weighting}
|
||||||
|
\label{sec:econ}
|
||||||
|
|
||||||
|
A permissionless network needs an incentive, and the incentive must be \emph{useful-work},
|
||||||
|
because paying for wasted computation (proof-of-work hashing) is exactly what an inference
|
||||||
|
network should not do. The luminance primitive is already a useful-work meter in miniature: it
|
||||||
|
rewards nodes for correct participation and decays nodes for failure. The economic design
|
||||||
|
\design{} binds three quantities to it:
|
||||||
|
\begin{itemize}[leftmargin=1.4em,itemsep=2pt]
|
||||||
|
\item \textbf{Admission by stake.} A miner joins by bonding stake in a transaction
|
||||||
|
(\S\ref{sec:control}). Stake is the sybil cost --- spinning up a thousand fake GPUs is a
|
||||||
|
thousand bonds --- and the slashing target when verification (\S\ref{sec:threat}) catches a
|
||||||
|
fault.
|
||||||
|
\item \textbf{Reward by validated contribution.} Payment for an inference accrues to the miners
|
||||||
|
whose outputs were attested, weighted by luminance and by measured work (tokens $\times$ model
|
||||||
|
size $\times$ layer share). Because luminance rises with correct work and falls with incorrect
|
||||||
|
work, expected income tracks \emph{honest throughput}, and a lazy or lying miner earns less than
|
||||||
|
its electricity costs.
|
||||||
|
\item \textbf{Routing by brightness.} The Photon committee that ratifies placement and verifies
|
||||||
|
results draws with luminance weight, so brighter (more reliably correct) nodes are preferentially
|
||||||
|
given work --- reputation and revenue reinforce, which is what makes ``contribute a GPU, get
|
||||||
|
paid'' a stable equilibrium rather than a race to the bottom.
|
||||||
|
\end{itemize}
|
||||||
|
The mechanism reuses one existing primitive (luminance) for reputation, one existing primitive
|
||||||
|
(stake-weighted BFT) for sybil-resistance and slashing, and one existing primitive (Photon) for
|
||||||
|
work routing. The realized system is ``any dev, any miner, contributes a GPU and is paid for
|
||||||
|
inference work,'' with the payment denominated in the network's native token and the fairness
|
||||||
|
enforced by the same consensus that finalizes the placement map. This is the point of contact
|
||||||
|
with the sovereign-cloud economics of the tenant-cloud companion~\cite{tenantcloud}, which
|
||||||
|
develops the wallet, staking, and settlement primitives this layer would sit on.
|
||||||
|
|
||||||
|
\section{Migration path: from LAN appliance to public network}
|
||||||
|
\label{sec:migration}
|
||||||
|
|
||||||
|
The design is only credible if there is a continuous path from what runs today to the network,
|
||||||
|
with no flag day. There is, in four stages, each shippable and independently valuable.
|
||||||
|
|
||||||
|
\paragraph{Stage 0 (today, \built{}).} Static \code{RING\_CONFIG} JSON, QR join token, raw-TCP
|
||||||
|
ring, canonical \code{f32} across vendors. Works on a trusted LAN; this is the baseline.
|
||||||
|
|
||||||
|
\paragraph{Stage 1 --- authenticate the wire (\design{}, smallest step).} Replace the ring's
|
||||||
|
\code{TcpStream} with a ZAP \code{WrapPQ} \code{net.Conn} and make the join token gate on a PQ
|
||||||
|
handshake (\S\ref{sec:threat}, Gaps 1--2). No consensus yet: the topology is still the static
|
||||||
|
map, but the wire is now authenticated and encrypted and the token is a credential. This alone
|
||||||
|
converts the appliance into something safe to run across untrusted networks, and it exercises the
|
||||||
|
engine's ZAP module end-to-end --- turning the \partialb{} \code{zap://} transport on
|
||||||
|
(\S\ref{sec:zap}) is precisely this step.
|
||||||
|
|
||||||
|
\paragraph{Stage 2 --- consensus owns the map (\design{}).} Stand up a Quasar chain whose state
|
||||||
|
is the membership and placement map. Nodes read topology from the chain instead of
|
||||||
|
\code{RingConfig::load()}; \code{hanzo cluster join} becomes an admission transaction rather than
|
||||||
|
a local launch. Placement is still operator-proposed and consensus-ratified (permissioned
|
||||||
|
membership, BFT config). The ring code is untouched; only the source of its topology moves from
|
||||||
|
file to chain.
|
||||||
|
|
||||||
|
\paragraph{Stage 3 --- permissionless miners and paid work (\design{}).} Open admission to
|
||||||
|
staked strangers, bind luminance to validated inference contribution, and turn on result
|
||||||
|
attestation and spot-check re-execution (\S\ref{sec:threat}--\S\ref{sec:econ}). This is the full
|
||||||
|
network: anyone bonds, joins, is placed by consensus, serves layers over authenticated ZAP, is
|
||||||
|
verified by a Photon committee, and is paid by luminance-weighted contribution.
|
||||||
|
|
||||||
|
The staging matters because each stage stands alone. Stage 1 is worth shipping even if Stages
|
||||||
|
2--3 never do (it fixes a real security gap). Stage 2 is worth shipping for a single operator's
|
||||||
|
multi-datacenter fleet (BFT config, no static files) without any economics. Only Stage 3 needs
|
||||||
|
the token and slashing machinery. There is no rewrite between stages --- the same ring, the same
|
||||||
|
ZAP, more of the control plane each time.
|
||||||
|
|
||||||
|
\section{Connection to the latency-physics companion}
|
||||||
|
\label{sec:latency}
|
||||||
|
|
||||||
|
A pipeline-parallel ring over a wide-area network has a latency budget a single box does not: each
|
||||||
|
pipeline stage adds a network hop to the critical path of every token. The companion
|
||||||
|
latency-physics analysis~\cite{latency} argues that this budget is spendable precisely because
|
||||||
|
two techniques compose over ZAP. First, \textbf{pipeline parallelism hides transport latency
|
||||||
|
under compute}: with enough micro-batches in flight, a stage's forward pass overlaps the next
|
||||||
|
stage's activation transfer, so the ring's throughput approaches the slowest stage's compute rate
|
||||||
|
rather than the sum of hop latencies --- and ZAP's zero-copy framing minimizes the per-hop
|
||||||
|
transport term to an AEAD seal, not a marshal. Second, \textbf{speculative decoding amortizes the
|
||||||
|
round-trip}: a draft model proposes several tokens that the ring verifies in one pipelined pass,
|
||||||
|
so the wide-area round-trip is paid once per \emph{accepted block} of tokens instead of once per
|
||||||
|
token. The engine already factors speculative decoding behind one \code{SpeculativeProposer}
|
||||||
|
trait~\cite{infstack}; over a consensus-managed ZAP ring, the draft can even run on a nearby,
|
||||||
|
brighter node while the target pipeline spans the fleet. The three-plane fabric is what makes
|
||||||
|
these composable: the data plane does the pipelining and speculation, the transport plane makes
|
||||||
|
each hop cheap and authenticated, and the control plane places draft and target where the latency
|
||||||
|
budget is best --- decomplected, so each optimization lives in exactly one plane. We cite the
|
||||||
|
companion for the quantitative budget; here it is enough that the architecture does not fight the
|
||||||
|
physics.
|
||||||
|
|
||||||
|
\section{Status: built, partial, and designed}
|
||||||
|
\label{sec:status}
|
||||||
|
|
||||||
|
Table~\ref{tab:status} is the honest ledger. It is the table to read first: it says exactly which
|
||||||
|
sentences of this paper are code on \code{main} and which are blueprint. The pattern is
|
||||||
|
deliberate --- every \emph{primitive} is built and cited; every \emph{composition} into the
|
||||||
|
network is designed. That is what makes this a buildable blueprint rather than a wish.
|
||||||
|
|
||||||
|
\begin{table}[t]
|
||||||
|
\centering
|
||||||
|
\small
|
||||||
|
\setlength{\tabcolsep}{5pt}
|
||||||
|
\begin{tabular}{@{}p{4.9cm}p{1.5cm}p{1.5cm}p{5.0cm}@{}}
|
||||||
|
\toprule
|
||||||
|
\textbf{Component} & \textbf{Plane} & \textbf{Status} & \textbf{Evidence / source} \\
|
||||||
|
\midrule
|
||||||
|
Cross-vendor inference ring (TP/PP, canonical \code{f32}) & Data & \built{} &
|
||||||
|
\code{distributed.rs}, \code{distributed/socket.rs}, \code{ops.rs}~\cite{infstack} \\
|
||||||
|
NCCL $+$ portable ring backends & Data & \built{} & \code{hanzo-quant/src/distributed/} \\
|
||||||
|
\code{hanzo cluster} scan-to-join (bs58/QR token) & Data & \built{} &
|
||||||
|
\code{hanzo-cli/src/cluster.rs} \\
|
||||||
|
Five-backend engine, decode parity, quant zoo & Data & \built{} &
|
||||||
|
companion stack paper~\cite{infstack} \\
|
||||||
|
ZAP Go wire (zero-copy, $17\times$/$345\times$ vs JSON-RPC) & Transport & \built{} &
|
||||||
|
\code{luxfi/zap}~\cite{zap,lp201} \\
|
||||||
|
ZAP PQ handshake, \code{WrapPQ}, verified \code{PeerID} & Transport & \built{} &
|
||||||
|
\code{conn\_pq.go} \\
|
||||||
|
ZAP engine module (in-process adapter surface) & Transport & \partialb{} &
|
||||||
|
\code{engine/src/zap/*.rs} (server/client) \\
|
||||||
|
\quad out-of-process \code{zap://} (capnp wire) & Transport & \partialb{} &
|
||||||
|
\code{engine/src/zap/transport.rs} (stub) \\
|
||||||
|
Quasar BFT engine; Photon, Wave, Focus & Control & \built{} &
|
||||||
|
\code{luxfi/consensus}~\cite{quasar,luxconsensus} \\
|
||||||
|
Luminance reputation (100 base, $\times1.1$/$\times0.9$) & Control & \built{} &
|
||||||
|
\code{photon/luminance.go} \\
|
||||||
|
BLS$+$Pulsar$+$ML-DSA threshold signing (QuasarCert) & Control & \built{} &
|
||||||
|
\code{protocol/quasar}~\cite{luxconsensus} \\
|
||||||
|
\midrule
|
||||||
|
Ring transport over ZAP (retire raw TCP) & Transport & \design{} & \S\ref{sec:transport}, Stage 1 \\
|
||||||
|
Join token as PQ handshake credential & Transport & \design{} & \S\ref{sec:threat}, Gap 2 \\
|
||||||
|
Consensus-owned membership \& placement map & Control & \design{} & \S\ref{sec:control}, Stage 2 \\
|
||||||
|
Permissionless miner admission (stake bond) & Control & \design{} & \S\ref{sec:econ}, Stage 3 \\
|
||||||
|
Luminance $\gets$ validated inference contribution & Control & \design{} & \S\ref{sec:control} \\
|
||||||
|
Result verification (threshold attest $+$ spot check) & Control & \design{} & \S\ref{sec:threat}, Gap 3 \\
|
||||||
|
Luminance-weighted reward / routing & Control & \design{} & \S\ref{sec:econ} \\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabular}
|
||||||
|
\caption{Built-versus-designed ledger. Every primitive (upper block) is code on \code{main},
|
||||||
|
cited to its file with its measured cost where relevant; every network composition (lower block)
|
||||||
|
is design, cited to the section that specifies it and the migration stage that ships it.}
|
||||||
|
\label{tab:status}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\paragraph{Reproducing the built numbers.} The ZAP figures ($2.9$~ns parse, $17\times$/$345\times$
|
||||||
|
versus JSON-RPC, $114$K/$376$K TPS) reproduce with \code{go test -bench=. -benchmem} in
|
||||||
|
\code{luxfi/zap} and \code{luxfi/consensus/bench/}; the Photon/Wave/luminance costs ($3.03$~\textmu s,
|
||||||
|
$3.38$~\textmu s, $72$~ns) reproduce with \code{GOWORK=off go test -bench=. ./bench/} in
|
||||||
|
\code{luxfi/consensus}, measured on Apple M1 Max. The cross-vendor ring runs with a
|
||||||
|
\code{RING\_CONFIG} JSON per rank and \code{hanzo cluster init}/\code{join}; the cross-backend
|
||||||
|
engine numbers are reproduced by the protocol in the companion stack paper~\cite{infstack}. None
|
||||||
|
of these numbers is produced by this paper; each is attributed to its source so the blueprint's
|
||||||
|
foundation is auditable.
|
||||||
|
|
||||||
|
\section{Limitations}
|
||||||
|
\label{sec:limits}
|
||||||
|
|
||||||
|
The rigor is the persuasion, so the costs are as plain as the benefits.
|
||||||
|
|
||||||
|
\begin{itemize}[leftmargin=1.4em,itemsep=2pt]
|
||||||
|
\item \textbf{The network is a composition, not a shipped network.} The primitives are built and
|
||||||
|
cited; the assembly of \S\ref{sec:planes}--\S\ref{sec:econ} is \design{}. Nothing here should be
|
||||||
|
read as ``Hanzo runs a permissionless GPU network today.'' It runs a cross-vendor ring on a
|
||||||
|
trusted mesh (Stage 0) and ships every primitive the network needs.
|
||||||
|
|
||||||
|
\item \textbf{The ZAP engine wire is not yet turned on.} The Rust ZAP module is \partialb{}: its
|
||||||
|
in-process surface is exercised, but the \code{zap://} out-of-process transport is a documented
|
||||||
|
stub and a few methods return ``not yet wired'' (\S\ref{sec:zap}). Stage 1 depends on completing
|
||||||
|
it. The Go ZAP wire it mirrors is production-deployed, so the risk is integration, not invention.
|
||||||
|
|
||||||
|
\item \textbf{Wide-area tensor transport is unproven at scale.} The ring was built for a LAN.
|
||||||
|
Pipeline parallelism over a wide-area network is argued to be latency-tolerant
|
||||||
|
(\S\ref{sec:latency}) but not measured here; the companion latency paper carries that budget, and
|
||||||
|
a real deployment must validate it. Bandwidth, not just latency, may bound activation exchange
|
||||||
|
for large hidden dimensions.
|
||||||
|
|
||||||
|
\item \textbf{Result verification adds cost.} Threshold attestation and redundant re-execution
|
||||||
|
(\S\ref{sec:threat}) are not free: a QuasarCert verifies in $2$--$5$~ms and spot-check redundancy
|
||||||
|
duplicates work on sampled requests. The scheme is designed to make the \emph{expected} overhead
|
||||||
|
small (attest per epoch, spot-check a fraction) but it is a real tax the monolithic single-trust
|
||||||
|
model does not pay --- the price of permissionlessness.
|
||||||
|
|
||||||
|
\item \textbf{Confidentiality from the operator is out of scope.} This design authenticates and
|
||||||
|
verifies; it does not hide activations from the computing miner without a TEE tier
|
||||||
|
(\S\ref{sec:threat}). Sensitive workloads need that orthogonal extension.
|
||||||
|
|
||||||
|
\item \textbf{Luminance re-binding is a policy change with game-theoretic edges.} Moving the
|
||||||
|
\code{Illuminate} trigger from vote-success to inference-contribution is a one-line mechanism
|
||||||
|
change but a large incentive-design question: collusion among a committee, grinding on the
|
||||||
|
verification lottery, and reward-share disputes across a pipeline are real and not solved by the
|
||||||
|
primitive alone. We name them; we do not claim to have priced them.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\section{Conclusion}
|
||||||
|
\label{sec:conclusion}
|
||||||
|
|
||||||
|
The heterogeneous GPU ring already solves the hard, unglamorous problem --- one model computing
|
||||||
|
correctly across ROCm, CUDA, and Metal, bit-for-bit, over a canonical wire. What it lacks to
|
||||||
|
become a network is not more numerics but two planes it does not have: a control plane to decide
|
||||||
|
membership and placement among parties who do not trust each other, and a transport plane to
|
||||||
|
authenticate the strangers it talks to. Both already exist as best-in-class Lux primitives ---
|
||||||
|
Quasar consensus and the ZAP post-quantum wire --- and the entire contribution of this paper is
|
||||||
|
to hold the three concerns apart and compose them: consensus schedules, ZAP carries, the ring
|
||||||
|
computes. That decomplection is what a monolith cannot do and what makes ``any developer, any
|
||||||
|
miner, contributes a GPU and is paid for inference work'' a buildable system rather than a slogan.
|
||||||
|
We have drawn the line between built and designed down the middle of every claim: the ring, the
|
||||||
|
scan-to-join CLI, the ZAP wire with its verified \code{PeerID}, the Quasar engine with luminance
|
||||||
|
and threshold signing are code on \code{main}, cited and measured; the network that composes them
|
||||||
|
--- consensus-owned membership, the ZAP-transported ring, permissionless admission, and result
|
||||||
|
verification by threshold-signing over outputs --- is design, staged for a migration with no flag
|
||||||
|
day. The primitives are done. The composition is the blueprint. We publish it so the rest of the
|
||||||
|
AI-infrastructure community can build on the same three planes.
|
||||||
|
|
||||||
|
\begin{thebibliography}{20}
|
||||||
|
\small
|
||||||
|
|
||||||
|
\bibitem{infstack}
|
||||||
|
Hanzo AI --- ML Systems. \emph{Hanzo ML and Hanzo Engine: A Unified Multi-Backend, Multi-Modal
|
||||||
|
Inference Stack}. Hanzo AI white paper, 2026 (companion; the data-plane engine, cross-vendor ring,
|
||||||
|
and five-backend decode parity).
|
||||||
|
|
||||||
|
\bibitem{tenantcloud}
|
||||||
|
Hanzo AI Research. \emph{The Unified Sovereign-Tenant Cloud: Linear Shared-Nothing Scaling via
|
||||||
|
Per-Tenant SQLite and In-Process Composition}. Hanzo Industries white paper, 2026 (companion; the
|
||||||
|
\code{hanzo.network} fabric, wallet/staking, TEE trust tiers, and PQ identity primitives).
|
||||||
|
|
||||||
|
\bibitem{latency}
|
||||||
|
Hanzo AI --- ML Systems. \emph{The Latency Physics of Decentralized Decode: Pipeline Parallelism
|
||||||
|
and Speculative Decoding over ZAP}. Hanzo AI white paper, 2026 (companion; the wide-area latency
|
||||||
|
budget referenced in \S\ref{sec:latency}).
|
||||||
|
|
||||||
|
\bibitem{quasar}
|
||||||
|
Lux Industries. \emph{Quasar: Post-Quantum DAG-Native BFT Consensus} (LP-020 / LP-105). Lux
|
||||||
|
Network, 2026. \url{https://github.com/luxfi/consensus}.
|
||||||
|
|
||||||
|
\bibitem{luxconsensus}
|
||||||
|
Lux Industries. \emph{luxfi/consensus}: the Quasar engine --- \code{protocol/photon} (Fisher--Yates
|
||||||
|
committee selection, luminance), \code{protocol/wave} (FPC threshold voting), \code{protocol/focus},
|
||||||
|
\code{protocol/quasar} (BLS $+$ Pulsar $+$ ML-DSA threshold signing). Source and benchmarks
|
||||||
|
(\code{bench/}, \code{README.md}, \code{LLM.md}). \url{https://github.com/luxfi/consensus}.
|
||||||
|
|
||||||
|
\bibitem{zap}
|
||||||
|
Lux Industries. \emph{ZAP: Zero-copy Application Protocol} --- the universal wire.
|
||||||
|
\code{github.com/luxfi/zap} (\code{zap.go}, \code{conn\_pq.go} PQ handshake / \code{WrapPQ} /
|
||||||
|
\code{PeerID}, \code{README.md} benchmarks).
|
||||||
|
|
||||||
|
\bibitem{lp201}
|
||||||
|
Lux Industries. \emph{LP-201: ZAP as the Universal Wire} and \emph{LP-200: The ZAP Stack}. Lux
|
||||||
|
Proposals, 2026. \url{https://github.com/luxfi/lps} (the $17\times$/$345\times$ vs JSON-RPC and
|
||||||
|
$\sim$$4.5\times$ vs gRPC measurements).
|
||||||
|
|
||||||
|
\bibitem{mldsa}
|
||||||
|
National Institute of Standards and Technology. \emph{FIPS 204: Module-Lattice-Based Digital
|
||||||
|
Signature Standard (ML-DSA)}. NIST, 2024.
|
||||||
|
|
||||||
|
\bibitem{mlkem}
|
||||||
|
National Institute of Standards and Technology. \emph{FIPS 203: Module-Lattice-Based
|
||||||
|
Key-Encapsulation Mechanism Standard (ML-KEM)}. NIST, 2024.
|
||||||
|
|
||||||
|
\bibitem{bls}
|
||||||
|
D. Boneh, B. Lynn, and H. Shacham. Short Signatures from the Weil Pairing. \emph{Journal of
|
||||||
|
Cryptology}, 17(4):297--319, 2004.
|
||||||
|
|
||||||
|
\bibitem{petals}
|
||||||
|
A. Borzunov et al. \emph{Petals: Collaborative Inference and Fine-tuning of Large Models}. ACL
|
||||||
|
System Demonstrations, 2023 (decentralized inference over volunteer GPUs; no BFT control plane or
|
||||||
|
PQ-authenticated transport).
|
||||||
|
|
||||||
|
\bibitem{kwon2023}
|
||||||
|
W. Kwon et al. Efficient Memory Management for Large Language Model Serving with PagedAttention.
|
||||||
|
\emph{SOSP}, 2023 (the monolithic single-operator serving baseline).
|
||||||
|
|
||||||
|
\bibitem{pinkas}
|
||||||
|
B. Pinkas and M. Reiter, and the committee-grinding threat that motivates rejection-sampled
|
||||||
|
uniform selection in \code{photon/emitter.go}. (Bias-free sortition under a grinding adversary.)
|
||||||
|
|
||||||
|
\end{thebibliography}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
Binary file not shown.
@@ -0,0 +1,570 @@
|
|||||||
|
\documentclass[11pt]{article}
|
||||||
|
\input{shared/kernel-preamble.tex}
|
||||||
|
\usepackage{pgfplots}
|
||||||
|
\pgfplotsset{compat=1.16}
|
||||||
|
|
||||||
|
\newcommand{\Tring}{\ensuremath{T_{\mathrm{ring}}}}
|
||||||
|
\newcommand{\dmodel}{\ensuremath{d_{\mathrm{model}}}}
|
||||||
|
\newcommand{\Gam}{\ensuremath{\Gamma}}
|
||||||
|
|
||||||
|
\title{\textbf{The Latency Physics of Geo-Distributed\\ Autoregressive Decode}}
|
||||||
|
\author{Hanzo AI --- ML Systems}
|
||||||
|
\date{An analytical systems paper --- companion to the cross-backend inference measurements}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\maketitle
|
||||||
|
|
||||||
|
\begin{abstract}
|
||||||
|
Splitting a large language model across machines that sit far apart is often imagined as
|
||||||
|
``BitTorrent for AI'': pool everyone's memory and the model runs. For \emph{loading} weights and
|
||||||
|
for \emph{batched throughput} that intuition is right. For \emph{single-stream, interactive decode}
|
||||||
|
it is exactly wrong, and this paper derives why. We separate the two costs of a node boundary.
|
||||||
|
\textbf{Bandwidth is trivial}: the only tensor that crosses a pipeline boundary is the hidden state
|
||||||
|
for the in-flight tokens, \dmodel${}\times n_{\mathrm{tok}}\times b$ bytes --- $\sim$32\,KiB for one
|
||||||
|
token of a 700B-class model --- which serializes in $\approx$0.1\,ms over our measured 280\,MB/s
|
||||||
|
wired mesh, and the KV cache never crosses at all. \textbf{Latency is the wall}: autoregressive
|
||||||
|
decode is a strict dependency chain, so each generated token must traverse every node once, and the
|
||||||
|
next token cannot begin until the current one returns. Per-token decode rate is therefore
|
||||||
|
$r=1/(C+\Tring)$, where $C$ is the summed compute and $\Tring=\sum_i \ell_i$ is the one-way latency
|
||||||
|
of a full loop around the ring. Because \Tring{} adds to a per-token denominator, a geo-distributed
|
||||||
|
ring (Hong Kong\,$\to$\,Dublin\,$\to$\,New York, $\Tring\!\approx\!280$\,ms) floors single-stream
|
||||||
|
decode at $\approx$3\,tok/s \emph{regardless of GPU speed} --- the wall is the speed of light in
|
||||||
|
fiber, not the accelerator. The headline result is the cure: \textbf{speculative decoding converts
|
||||||
|
$K$ sequential network traversals into one}. A local draft model proposes $K$ tokens at full LAN
|
||||||
|
speed; the distributed model verifies all $K$ in a single pipeline pass; at acceptance rate $\alpha$
|
||||||
|
the effective rate is $r_{\mathrm{spec}}=\frac{1-\alpha^{K+1}}{1-\alpha}\big/(Kc_d+C+\Tring)$, which
|
||||||
|
recovers 12--21\,tok/s on the same 280\,ms ring --- interactive speeds from an intercontinental
|
||||||
|
deployment. We close with four placement laws, including a derivation that pipeline parallelism
|
||||||
|
($O(\text{nodes})$ hops/token) strictly dominates tensor parallelism ($O(\text{layers})$
|
||||||
|
all-reduces/token) over any slow link by a factor of $\sim\!4L/N$. Every model here is analytical
|
||||||
|
and labelled as such; the one measured number is the 280\,MB/s link.
|
||||||
|
\end{abstract}
|
||||||
|
|
||||||
|
\section{Two regimes: dependency-bound vs.\ throughput-bound}
|
||||||
|
\label{sec:dichotomy}
|
||||||
|
|
||||||
|
The whole paper turns on one distinction, so we state it first.
|
||||||
|
|
||||||
|
A computation is \textbf{throughput-bound} when its pieces are independent: they can be done in any
|
||||||
|
order, in parallel, and the only thing that matters is aggregate bytes-or-flops per second. Network
|
||||||
|
\emph{latency} barely touches such a workload --- a slow link means each piece arrives a little
|
||||||
|
later, but no piece waits on another, so the pipe stays full and throughput is set by
|
||||||
|
\emph{bandwidth}. Downloading a file is the canonical case; so is a batch of a thousand independent
|
||||||
|
inference requests.
|
||||||
|
|
||||||
|
A computation is \textbf{dependency-bound} when its pieces form a chain: piece $t+1$ cannot start
|
||||||
|
until piece $t$ finishes. Now latency is on the critical path of \emph{every} step and cannot be
|
||||||
|
hidden, because there is no independent work to overlap it with. Bandwidth is almost irrelevant ---
|
||||||
|
the messages are tiny --- and the wall is the round-trip time.
|
||||||
|
|
||||||
|
Autoregressive decode is the purest dependency chain in production machine learning. Token $t+1$ is
|
||||||
|
sampled from the model's output after token $t$ has been fed back as input; \emph{within} a token,
|
||||||
|
layer $\ell+1$ consumes the output of layer $\ell$. There is exactly one token in flight for a
|
||||||
|
single stream, so there is nothing to overlap. Distributing that chain across nodes does not add
|
||||||
|
parallelism to a single stream --- it adds hops to the chain. This is why the ``pool everyone's
|
||||||
|
GPUs'' picture, which is a throughput picture, gives the wrong answer for interactive decode, which
|
||||||
|
is a dependency problem. The rest of the paper makes both halves quantitative.
|
||||||
|
|
||||||
|
\paragraph{Parameters and provenance.}
|
||||||
|
Table~\ref{tab:params} lists the symbols and the reference values used in the worked numbers.
|
||||||
|
\emph{One} value is measured --- the 280\,MB/s link goodput, from iperf3 on our direct 2.5\,GbE
|
||||||
|
mesh. The representative one-way fiber latencies are physical (Section~\ref{sec:wall}) and are not
|
||||||
|
our measurements; the model geometry (\dmodel, $L$) is that of a public 700B-class dense/MoE model;
|
||||||
|
the local decode ceiling $r_0$ and draft rate $r_d$ are stated parameters, not claims about a
|
||||||
|
specific box. Every rate, table, and plot downstream is a closed-form evaluation of the models in
|
||||||
|
this paper, not an experiment.
|
||||||
|
|
||||||
|
\begin{table}[h]
|
||||||
|
\centering
|
||||||
|
\small
|
||||||
|
\caption{Symbols and reference values. Only $W$ is measured.}
|
||||||
|
\label{tab:params}
|
||||||
|
\begin{tabular}{l l l l}
|
||||||
|
\toprule
|
||||||
|
Symbol & Meaning & Reference value & Provenance\\
|
||||||
|
\midrule
|
||||||
|
$N$ & pipeline nodes (stages) & 3 & scenario\\
|
||||||
|
$L$ & transformer layers & $\sim$100 & 700B-class model\\
|
||||||
|
\dmodel & hidden size & 16384 & 700B-class model\\
|
||||||
|
$b$ & activation bytes/element (bf16) & 2 & format\\
|
||||||
|
$W$ & per-link goodput & \textbf{280\,MB/s} & \textbf{measured (2.5\,GbE)}\\
|
||||||
|
$\ell_i$ & one-way latency of hop $i$ & 40--130\,ms (geo) & physical (fiber)\\
|
||||||
|
$\Tring$ & ring latency $\sum_{i=1}^N \ell_i$ & 280\,ms (geo) & physical (fiber)\\
|
||||||
|
$C=1/r_0$ & target compute per decode step & 33\,ms ($r_0{=}30$) & parameter\\
|
||||||
|
$c_d=1/r_d$& local draft time per token & 5\,ms ($r_d{=}200$) & parameter\\
|
||||||
|
$\alpha$ & speculative acceptance rate & 0.6--0.9 & parameter\\
|
||||||
|
$K$ & draft length (tokens/round) & 1--24 & swept\\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabular}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\section{The crossing tensor: bandwidth is trivial}
|
||||||
|
\label{sec:bandwidth}
|
||||||
|
|
||||||
|
In pipeline parallelism (PP) the model is partitioned by \emph{layer}: node $i$ owns a contiguous
|
||||||
|
stack of $L/N$ layers. The only data that crosses the boundary between node $i$ and node $i{+}1$ is
|
||||||
|
the residual-stream hidden state at that layer cut --- one activation tensor of shape
|
||||||
|
$[\,n_{\mathrm{tok}},\ \dmodel\,]$ in the activation dtype. Its size is
|
||||||
|
\begin{equation}
|
||||||
|
B_{\mathrm{hop}} \;=\; n_{\mathrm{tok}}\cdot \dmodel \cdot b \quad\text{bytes},
|
||||||
|
\label{eq:bhop}
|
||||||
|
\end{equation}
|
||||||
|
and the time to push it across a link of goodput $W$ is $t_{\mathrm{xmit}}=B_{\mathrm{hop}}/W$.
|
||||||
|
Three properties of Eq.~\eqref{eq:bhop} decide the paper.
|
||||||
|
|
||||||
|
\paragraph{It is independent of model depth.} $B_{\mathrm{hop}}$ contains no $L$. Only the
|
||||||
|
\emph{width} \dmodel{} and the number of in-flight tokens appear, because exactly one activation
|
||||||
|
crosses each cut no matter how many layers sit behind it. A deeper model costs more
|
||||||
|
\emph{compute} per node but not more \emph{bytes} per hop. This is the structural reason PP is the
|
||||||
|
right WAN topology (Section~\ref{sec:pptp}).
|
||||||
|
|
||||||
|
\paragraph{The KV cache never crosses the wire.} Each node holds the keys and values for
|
||||||
|
\emph{its own} layers and reuses them locally on every step. The KV cache is the large, context-
|
||||||
|
growing state; it stays put. What crosses is only the small, fixed-size residual vector. Distributing
|
||||||
|
the model does not distribute the KV traffic --- there is none across the boundary.
|
||||||
|
|
||||||
|
\paragraph{For decode it is a rounding error.} For a single decoded token, $n_{\mathrm{tok}}=1$.
|
||||||
|
With the 700B-class width $\dmodel=16384$ and bf16 ($b=2$),
|
||||||
|
\[
|
||||||
|
B_{\mathrm{hop}} = 1\cdot 16384 \cdot 2 = 32{,}768~\text{bytes} = 32~\text{KiB},
|
||||||
|
\qquad
|
||||||
|
t_{\mathrm{xmit}} = \frac{32{,}768}{280\times10^6} \approx 0.117~\text{ms}
|
||||||
|
\]
|
||||||
|
over our \emph{measured} 280\,MB/s mesh. Even at a serving-scale aggregate of 1000\,tok/s each hop
|
||||||
|
carries $\sim$32\,MB/s --- about a tenth of a single 2.5\,GbE link, and nothing on a datacenter WAN.
|
||||||
|
\textbf{Bandwidth is never the constraint for decode.} (It can matter for large-prompt prefill,
|
||||||
|
where $n_{\mathrm{tok}}=P$: a 2000-token prompt crosses 65.5\,MB per hop, $\approx$234\,ms to
|
||||||
|
serialize over 280\,MB/s --- but that is a bulk, streamable, latency-insensitive transfer that a
|
||||||
|
fatter pipe fixes. It is a throughput cost, not a wall; Section~\ref{sec:regimes}.)
|
||||||
|
|
||||||
|
\section{The wall: latency floors single-stream decode}
|
||||||
|
\label{sec:wall}
|
||||||
|
|
||||||
|
Now the cost that does not go away. Generating one token drives the hidden state through all $N$
|
||||||
|
nodes in sequence: node~1 embeds and runs its layers, hands off to node~2, and so on to node~$N$,
|
||||||
|
which produces the final hidden state; the model head samples the next token, which must return to
|
||||||
|
node~1 to begin the next step. Counting hops on the critical path: $N-1$ forward hand-offs plus one
|
||||||
|
\emph{return} hop that closes the loop, i.e.\ $N$ inter-node latencies per token. Writing $\ell_i$
|
||||||
|
for the one-way latency of hop $i$ and $c_i$ for node $i$'s compute on its layer slice,
|
||||||
|
\begin{equation}
|
||||||
|
T_{\mathrm{tok}} \;=\; \underbrace{\sum_{i=1}^{N} c_i}_{\textstyle C}
|
||||||
|
\;+\; \underbrace{\sum_{i=1}^{N} \ell_i}_{\textstyle \Tring},
|
||||||
|
\qquad
|
||||||
|
r \;=\; \frac{1}{T_{\mathrm{tok}}} \;=\; \frac{1}{C + \Tring}.
|
||||||
|
\label{eq:wall}
|
||||||
|
\end{equation}
|
||||||
|
Two facts about Eq.~\eqref{eq:wall} carry the argument.
|
||||||
|
|
||||||
|
\paragraph{PP gives a single stream zero compute speedup --- only added latency.} For one in-flight
|
||||||
|
token, only one node computes at a time; the other $N-1$ idle (the pipeline is empty). So the total
|
||||||
|
compute $C=\sum_i c_i$ equals the monolithic single-node decode compute; splitting the model does
|
||||||
|
not make one stream's arithmetic faster. What splitting \emph{adds} is $\Tring$. PP buys memory
|
||||||
|
capacity (a 700B model that fits nowhere else) and, with batching, throughput
|
||||||
|
(Section~\ref{sec:regimes}) --- never single-stream latency.
|
||||||
|
|
||||||
|
\paragraph{Co-locate the turn so the ring closes in exactly $N$ hops.} Keep the embedding, the first
|
||||||
|
layers, the LM head, and the sampler on the same entry node. Then the ``sample $\to$ embed next
|
||||||
|
token'' turnaround happens locally, with no network step, and the return is a single hop. Split the
|
||||||
|
head from the embedding and you pay an extra hop every token (hidden state to the sampler node, then
|
||||||
|
the token id back to the embedder). One node owns both ends of the ring.
|
||||||
|
|
||||||
|
\paragraph{The geo table.} Fix a fast local ceiling $r_0=30$\,tok/s ($C=33$\,ms) and sweep the ring
|
||||||
|
latency. Table~\ref{tab:geo} and Figure~\ref{fig:wall} evaluate Eq.~\eqref{eq:wall}.
|
||||||
|
|
||||||
|
\begin{table}[h]
|
||||||
|
\centering
|
||||||
|
\small
|
||||||
|
\caption{Single-stream decode vs.\ ring latency, from $r=1/(C+\Tring)$ with $C=33$\,ms
|
||||||
|
($r_0=30$\,tok/s). Analytical.}
|
||||||
|
\label{tab:geo}
|
||||||
|
\begin{tabular}{l r r r}
|
||||||
|
\toprule
|
||||||
|
Deployment (3-node ring) & $\Tring$ (ms) & decode (tok/s) & \% of $r_0$\\
|
||||||
|
\midrule
|
||||||
|
Single node (no network) & 0 & 30.0 & 100\%\\
|
||||||
|
Rack LAN ($3\times0.1$\,ms) & 0.3 & 29.7 & 99\%\\
|
||||||
|
Same metro ($3\times0.7$\,ms) & 2 & 28.3 & 94\%\\
|
||||||
|
Regional, one continent ($3\times7$\,ms) & 20 & 18.8 & 63\%\\
|
||||||
|
Continental US ($3\times20$\,ms) & 60 & 10.7 & 36\%\\
|
||||||
|
Two-continent ($3\times50$\,ms) & 150 & 5.5 & 18\%\\
|
||||||
|
Global ring HK$\to$Dublin$\to$NY & 280 & \textbf{3.2} & \textbf{11\%}\\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabular}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\begin{figure}[h]
|
||||||
|
\centering
|
||||||
|
\begin{tikzpicture}
|
||||||
|
\begin{axis}[
|
||||||
|
width=0.86\linewidth, height=6.2cm,
|
||||||
|
xmode=log, log basis x=10,
|
||||||
|
xlabel={ring latency $\Tring$ (ms, log scale)},
|
||||||
|
ylabel={single-stream decode (tok/s)},
|
||||||
|
xmin=0.1, xmax=600, ymin=0, ymax=125,
|
||||||
|
grid=both, grid style={gray!18},
|
||||||
|
legend pos=north east, legend cell align=left,
|
||||||
|
every axis plot/.append style={very thick},
|
||||||
|
]
|
||||||
|
\addplot[blue] coordinates {
|
||||||
|
(0.1,118.58)(0.2,117.19)(0.3,115.83)(0.5,113.21)(1,107.14)(2,96.77)(5,75.00)(10,54.55)(20,35.29)(40,20.69)(60,14.63)(100,9.23)(150,6.32)(200,4.80)(280,3.47)(400,2.45)(600,1.64)};
|
||||||
|
\addlegendentry{fast GPUs, $r_0{=}120$}
|
||||||
|
\addplot[red] coordinates {
|
||||||
|
(0.1,29.91)(0.2,29.82)(0.3,29.73)(0.5,29.56)(1,29.13)(2,28.30)(5,26.09)(10,23.08)(20,18.75)(40,13.64)(60,10.71)(100,7.50)(150,5.45)(200,4.29)(280,3.19)(400,2.31)(600,1.58)};
|
||||||
|
\addlegendentry{$r_0{=}30$}
|
||||||
|
\draw[dashed,gray] (axis cs:280,0) -- (axis cs:280,3.47);
|
||||||
|
\node[anchor=south,font=\scriptsize,gray] at (axis cs:280,3.9) {global ring};
|
||||||
|
\node[anchor=west,font=\scriptsize] at (axis cs:1.2,90) {LAN};
|
||||||
|
\node[anchor=west,font=\scriptsize] at (axis cs:22,42) {regional};
|
||||||
|
\node[anchor=west,font=\scriptsize] at (axis cs:110,20) {inter-continental};
|
||||||
|
\end{axis}
|
||||||
|
\end{tikzpicture}
|
||||||
|
\caption{The wall. Two accelerator ceilings, $r_0=30$ and $r_0=120$\,tok/s, collapse onto the
|
||||||
|
\emph{same} curve as $\Tring$ grows: past $\sim$100\,ms the two are within $\sim$2\,tok/s. Beyond a
|
||||||
|
few tens of milliseconds of ring latency, single-stream decode is set by the network, not the GPU.
|
||||||
|
Analytical ($r=1/(C+\Tring)$).}
|
||||||
|
\label{fig:wall}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\paragraph{The GPU stops mattering.} The most important reading of Figure~\ref{fig:wall} is the
|
||||||
|
convergence of the two curves. Hold $\Tring=280$\,ms and vary the local ceiling: $r_0=15\to
|
||||||
|
2.9$, $30\to3.2$, $60\to3.4$, $120\to3.5$, $1000\to3.6$\,tok/s. A $67\times$ faster accelerator buys
|
||||||
|
a $1.2\times$ faster stream. Once the ring is long, $r\to 1/\Tring$ and the accelerator is a rounding
|
||||||
|
error. Buying H100s to serve one interactive user over a global ring is spending on the wrong axis.
|
||||||
|
|
||||||
|
\paragraph{Why it is \emph{physics}.} $\Tring$ has a hard floor: light in fiber travels at
|
||||||
|
$c/n\approx2.04\times10^{5}$\,km/s ($n\approx1.47$), i.e.\ $\approx4.9\,\mu$s/km one-way, so a
|
||||||
|
real (non-geodesic) route accrues $\gtrsim5$--$7$\,ms per 1000\,km. Hong Kong to New York is
|
||||||
|
$\sim$13{,}000\,km great-circle; no switch, protocol, or budget reduces that hop below $\sim$110\,ms
|
||||||
|
one-way. The 130/40/110\,ms legs of our example ring are representative real fiber latencies, and
|
||||||
|
they are irreducible. This is the sense in which the title is literal: the wall is a property of
|
||||||
|
space and light, and the systems question is not how to break it but how to \emph{stop paying it
|
||||||
|
per token} --- which is Section~\ref{sec:spec}.
|
||||||
|
|
||||||
|
\section{``BitTorrent for AI'' is half-right}
|
||||||
|
\label{sec:bittorrent}
|
||||||
|
|
||||||
|
The appeal of swarm inference --- Petals-style pooling of volunteer GPUs across the internet ---
|
||||||
|
rests on an analogy to BitTorrent~\cite{petals}. The analogy is exact where it applies and
|
||||||
|
misleading where it does not, and the dividing line is precisely Section~\ref{sec:dichotomy}'s.
|
||||||
|
|
||||||
|
BitTorrent is fast because a file's chunks are \emph{independent}. A peer fetches them in any order,
|
||||||
|
from many sources at once; a high-latency peer just means a given chunk lands a little later, never
|
||||||
|
that another chunk waits. File download is throughput-bound and latency-insensitive --- the ideal
|
||||||
|
case for pooling far-flung capacity. \textbf{Everything about serving a distributed model that is
|
||||||
|
throughput-bound inherits this good behaviour}: shipping the weights to the nodes, prefilling a long
|
||||||
|
prompt, and running a large batch of independent requests (Section~\ref{sec:regimes}). For these, a
|
||||||
|
geographically spread swarm is genuinely attractive.
|
||||||
|
|
||||||
|
Single-stream decode is the opposite object. Its ``chunks'' --- the per-token forward passes --- are
|
||||||
|
\emph{maximally dependent}: each is the input to the next. There is no order but the one order, no
|
||||||
|
parallel sources, nothing to overlap the latency against. Pooling distant GPUs does not add
|
||||||
|
throughput to one stream; it lengthens the dependency chain by the distance between the pools. The
|
||||||
|
BitTorrent intuition, applied here, predicts the exact opposite of what happens. So the slogan is
|
||||||
|
half-right: \textbf{BitTorrent for AI works for weight distribution, prefill, and batched
|
||||||
|
throughput, and breaks for interactive single-stream latency} --- and it breaks hardest precisely
|
||||||
|
where users feel it, in the token-by-token cadence of a chat.
|
||||||
|
|
||||||
|
\section{The regime map: where geo-distribution wins}
|
||||||
|
\label{sec:regimes}
|
||||||
|
|
||||||
|
The same 280\,ms ring is cheap or catastrophic depending on how many tokens amortize it.
|
||||||
|
|
||||||
|
\paragraph{Prefill / TTFT amortizes the traversal over the whole prompt.} A prompt of $P$ tokens
|
||||||
|
goes through the pipeline in \emph{one} pass. The ring latency is paid once, not per token:
|
||||||
|
\begin{equation}
|
||||||
|
T_{\mathrm{TTFT}} \approx C_{\mathrm{prefill}}(P) + \Tring .
|
||||||
|
\label{eq:ttft}
|
||||||
|
\end{equation}
|
||||||
|
For a 2000-token prompt whose prefill compute is $\sim$1--2\,s on a big model, a $+$280\,ms ring is a
|
||||||
|
15--30\% tax on time-to-first-token --- noticeable, not fatal. Per prompt-token the traversal is
|
||||||
|
$280\,\text{ms}/2000\approx0.14$\,ms: amortized to nothing. Prefill is throughput-bound (Section~
|
||||||
|
\ref{sec:bittorrent}); a geo ring is fine for it.
|
||||||
|
|
||||||
|
\paragraph{Batched throughput fills the pipe.} With continuous batching, $N$ pipeline stages run on
|
||||||
|
$N$ different requests at once. Each \emph{individual} stream still crawls at
|
||||||
|
Eq.~\eqref{eq:wall}'s $r=1/(C+\Tring)$, but the \emph{aggregate} scales with pipeline depth toward
|
||||||
|
$\approx N\!\cdot\!(\text{per-stage rate})$ until a stage saturates. This is the BitTorrent regime
|
||||||
|
done right: many independent streams are the independent chunks that keep the pipe full. High
|
||||||
|
aggregate tok/s, high per-stream latency --- excellent for offline/batch, useless as a proxy for one
|
||||||
|
user's experience.
|
||||||
|
|
||||||
|
\begin{table}[h]
|
||||||
|
\centering
|
||||||
|
\small
|
||||||
|
\caption{The regime map. Geo-distribution is bandwidth-cheap everywhere; it is latency-cheap only
|
||||||
|
where many tokens share one traversal.}
|
||||||
|
\label{tab:regime}
|
||||||
|
\begin{tabular}{l l l l}
|
||||||
|
\toprule
|
||||||
|
Workload & Bound by & Geo ring 280\,ms? & Why\\
|
||||||
|
\midrule
|
||||||
|
Weight distribution & throughput & \textbf{great} & independent chunks (BitTorrent)\\
|
||||||
|
Prefill / TTFT & throughput & \textbf{fine} & one traversal per prompt (Eq.~\ref{eq:ttft})\\
|
||||||
|
Batched serving (aggregate) & throughput & \textbf{great} & depth fills the pipe, $\sim\!N\times$\\
|
||||||
|
\textbf{Single-stream decode} & \textbf{dependency} & \textbf{hard} & one traversal \emph{per token} (Eq.~\ref{eq:wall})\\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabular}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
The map (Table~\ref{tab:regime}) is the practical statement of the thesis: only the bottom row is
|
||||||
|
hard, and it is hard because it is the only dependency-bound row. The next section attacks exactly
|
||||||
|
that row.
|
||||||
|
|
||||||
|
\section{The cure: speculative decoding amortizes the traversal}
|
||||||
|
\label{sec:spec}
|
||||||
|
|
||||||
|
The wall is that decode pays $\Tring$ \emph{per token}. Speculative decoding~\cite{leviathan,chen}
|
||||||
|
changes the denominator: it makes the network pay $\Tring$ \emph{per round}, where a round produces
|
||||||
|
several tokens. It is the one lever that attacks the dependency structure itself rather than the
|
||||||
|
constants.
|
||||||
|
|
||||||
|
\paragraph{Mechanism.} A small \emph{draft} model runs \emph{locally} on the entry node --- no
|
||||||
|
network in its loop --- and autoregressively proposes $K$ candidate tokens. Those $K$ tokens are
|
||||||
|
then fed as a length-$K$ sequence to the large distributed \emph{target} model, which verifies them
|
||||||
|
in a \emph{single} pipeline pass, prefill-style: the $K$ candidates flow through the ring together,
|
||||||
|
one traversal, not $K$. The target accepts the longest correct prefix under the standard
|
||||||
|
speculative-sampling rule (which preserves the target's exact output distribution) and corrects the
|
||||||
|
first mismatch. The expensive thing --- the trip around the ring --- happens once per round instead
|
||||||
|
of once per token.
|
||||||
|
|
||||||
|
\paragraph{Expected tokens per round.} Let each draft token be accepted independently with
|
||||||
|
probability $\alpha$ (the empirical acceptance rate). The number of accepted drafts $A$ satisfies
|
||||||
|
$\Pr[A\ge j]=\alpha^{j}$ for $j\le K$, so
|
||||||
|
$\mathbb{E}[A]=\sum_{j=1}^{K}\alpha^{j}=\alpha\frac{1-\alpha^{K}}{1-\alpha}$. The target always
|
||||||
|
contributes one further token (the correction on the first reject, or a bonus token if all $K$ are
|
||||||
|
accepted), giving the closed form of Leviathan et al.~\cite{leviathan}:
|
||||||
|
\begin{equation}
|
||||||
|
\Gam(\alpha,K)\;=\;1+\mathbb{E}[A]\;=\;\frac{1-\alpha^{K+1}}{1-\alpha}
|
||||||
|
\qquad\text{expected tokens per round.}
|
||||||
|
\label{eq:gamma}
|
||||||
|
\end{equation}
|
||||||
|
|
||||||
|
\paragraph{Effective rate.} A round costs one local draft of $K$ tokens plus one target
|
||||||
|
verification pass (one traversal of the ring):
|
||||||
|
\begin{equation}
|
||||||
|
T_{\mathrm{round}} \;=\; \underbrace{K\,c_d}_{\text{local draft}}
|
||||||
|
\;+\; \underbrace{C_{\mathrm{verify}}(K) + \Tring}_{\text{one target traversal}},
|
||||||
|
\qquad
|
||||||
|
r_{\mathrm{spec}}(\alpha,K)\;=\;\frac{\Gam(\alpha,K)}{T_{\mathrm{round}}}.
|
||||||
|
\label{eq:rspec}
|
||||||
|
\end{equation}
|
||||||
|
In the memory-bandwidth-bound decode regime, verifying $K\!\le\!16$ tokens streams the weights the
|
||||||
|
\emph{same} once as a single decode step, so $C_{\mathrm{verify}}(K)\approx C$ --- the reason
|
||||||
|
speculation is nearly free on the compute side.\footnote{For large $K$ the verify pass eventually
|
||||||
|
becomes compute-bound (prefill-like) and $C_{\mathrm{verify}}(K)$ grows with $K$; that is one of the
|
||||||
|
two forces creating an optimal $K$ below, the other being the geometric decay $\alpha^{K}$ of the
|
||||||
|
marginal acceptance. We take $C_{\mathrm{verify}}\!\approx\!C$ over the plotted range $K\le24$, which
|
||||||
|
is accurate for a 700B-class model where 24 tokens is still far below the arithmetic-intensity
|
||||||
|
knee.} Substituting the closed form,
|
||||||
|
\begin{equation}
|
||||||
|
\boxed{\;
|
||||||
|
r_{\mathrm{spec}}(\alpha,K)\;=\;\frac{1}{\,K\,c_d + C + \Tring\,}\cdot\frac{1-\alpha^{K+1}}{1-\alpha}
|
||||||
|
\;}
|
||||||
|
\label{eq:boxed}
|
||||||
|
\end{equation}
|
||||||
|
The structure is the whole point: $\Tring$ sits in the denominator once, divided into by
|
||||||
|
$\Gam(\alpha,K)$ tokens. The effective per-token network cost falls from $\Tring$ to
|
||||||
|
$\Tring/\Gam(\alpha,K)$.
|
||||||
|
|
||||||
|
\paragraph{Numbers on the global ring.} Take the intercontinental $\Tring=280$\,ms, target
|
||||||
|
$C=33$\,ms, and a fast local draft $c_d=5$\,ms ($r_d=200$\,tok/s). Baseline single-stream decode is
|
||||||
|
$1/(0.033+0.280)=3.2$\,tok/s. Table~\ref{tab:spec} and Figure~\ref{fig:spec} evaluate
|
||||||
|
Eq.~\eqref{eq:boxed}.
|
||||||
|
|
||||||
|
\begin{table}[h]
|
||||||
|
\centering
|
||||||
|
\small
|
||||||
|
\caption{Speculative decode on the 280\,ms global ring, from Eq.~\eqref{eq:boxed}
|
||||||
|
($C{=}33$\,ms, $c_d{=}5$\,ms). $\Gam$ = expected tokens/round; $r$ = effective tok/s.
|
||||||
|
Baseline (no speculation) $=3.2$\,tok/s. Analytical.}
|
||||||
|
\label{tab:spec}
|
||||||
|
\begin{tabular}{c cc cc cc c}
|
||||||
|
\toprule
|
||||||
|
& \multicolumn{2}{c}{$K=4$} & \multicolumn{2}{c}{$K=8$} & \multicolumn{2}{c}{$K=16$} & best\\
|
||||||
|
\cmidrule(lr){2-3}\cmidrule(lr){4-5}\cmidrule(lr){6-7}
|
||||||
|
$\alpha$ & $\Gam$ & $r$ & $\Gam$ & $r$ & $\Gam$ & $r$ & tok/s ($K^{\star}$)\\
|
||||||
|
\midrule
|
||||||
|
0.6 & 2.31 & 6.9 & 2.47 & 7.0 & 2.50 & 6.4 & 7.1 ($K{=}6$)\\
|
||||||
|
0.7 & 2.77 & 8.3 & 3.20 & 9.1 & 3.33 & 8.5 & 9.1 ($K{=}8$)\\
|
||||||
|
0.8 & 3.36 & 10.1 & 4.33 & 12.3 & 4.89 & 12.4 & 12.7 ($K{=}12$)\\
|
||||||
|
0.9 & 4.10 & 12.3 & 6.13 & 17.3 & 8.33 & 21.2 & 21.6 ($K{=}21$)\\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabular}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\begin{figure}[h]
|
||||||
|
\centering
|
||||||
|
\begin{tikzpicture}
|
||||||
|
\begin{axis}[
|
||||||
|
width=0.86\linewidth, height=6.4cm,
|
||||||
|
xlabel={draft length $K$ (tokens/round)},
|
||||||
|
ylabel={effective decode (tok/s)},
|
||||||
|
xmin=1, xmax=24, ymin=0, ymax=24,
|
||||||
|
grid=both, grid style={gray!18},
|
||||||
|
legend pos=north west, legend cell align=left,
|
||||||
|
every axis plot/.append style={very thick},
|
||||||
|
]
|
||||||
|
\addplot[black!70,dashed,thick] coordinates {(1,3.19)(24,3.19)};
|
||||||
|
\addlegendentry{baseline (no spec.), 3.2}
|
||||||
|
\addplot[red] coordinates {(1,5.97)(2,8.38)(3,10.47)(4,12.29)(5,13.85)(6,15.20)(7,16.35)(8,17.34)(9,18.18)(10,18.89)(11,19.48)(12,19.98)(13,20.38)(14,20.72)(15,20.98)(16,21.18)(17,21.34)(18,21.44)(19,21.51)(20,21.55)(21,21.55)(22,21.53)(23,21.48)(24,21.42)};
|
||||||
|
\addlegendentry{$\alpha=0.9$}
|
||||||
|
\addplot[orange] coordinates {(1,5.65)(2,7.55)(3,8.99)(4,10.08)(5,10.90)(6,11.51)(7,11.95)(8,12.25)(9,12.46)(10,12.58)(11,12.64)(12,12.66)(13,12.63)(14,12.58)(15,12.51)(16,12.43)(17,12.33)(18,12.22)(19,12.10)(20,11.99)(21,11.86)(22,11.74)(23,11.62)(24,11.49)};
|
||||||
|
\addlegendentry{$\alpha=0.8$}
|
||||||
|
\addplot[teal] coordinates {(1,5.34)(2,6.77)(3,7.71)(4,8.32)(5,8.69)(6,8.91)(7,9.02)(8,9.05)(9,9.04)(10,8.99)(11,8.92)(12,8.84)(13,8.75)(14,8.65)(15,8.56)(16,8.45)(17,8.35)(18,8.26)(19,8.16)(20,8.06)(21,7.97)(22,7.87)(23,7.78)(24,7.69)};
|
||||||
|
\addlegendentry{$\alpha=0.7$}
|
||||||
|
\addplot[blue!55] coordinates {(1,5.03)(2,6.06)(3,6.63)(4,6.92)(5,7.04)(6,7.08)(7,7.06)(8,7.00)(9,6.93)(10,6.86)(11,6.77)(12,6.69)(13,6.60)(14,6.52)(15,6.44)(16,6.35)(17,6.28)(18,6.20)(19,6.12)(20,6.05)(21,5.98)(22,5.91)(23,5.84)(24,5.77)};
|
||||||
|
\addlegendentry{$\alpha=0.6$}
|
||||||
|
\end{axis}
|
||||||
|
\end{tikzpicture}
|
||||||
|
\caption{The cure. Effective single-stream decode on the \emph{same} 280\,ms global ring, from
|
||||||
|
Eq.~\eqref{eq:boxed}. Speculation lifts the 3.2\,tok/s baseline (dashed) to 7--21\,tok/s. Each curve
|
||||||
|
has an optimum $K$: too small wastes the traversal, too large is drowned by the geometric decay
|
||||||
|
$\alpha^K$ and the growing draft cost $Kc_d$. Higher acceptance $\alpha$ both raises the ceiling and
|
||||||
|
pushes $K^{\star}$ out. Analytical.}
|
||||||
|
\label{fig:spec}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\paragraph{Reading the result.} On a ring that floors naive decode at 3.2\,tok/s, a draft with
|
||||||
|
acceptance $\alpha=0.8$ recovers $\sim$12.7\,tok/s ($4.0\times$) and $\alpha=0.9$ recovers
|
||||||
|
$\sim$21.6\,tok/s ($6.8\times$) --- from a bad slideshow to a readable stream, on the identical
|
||||||
|
intercontinental deployment, with no faster hardware and no shorter cable. The mechanism is not a
|
||||||
|
constant-factor tuning; it is a change in what the network is charged for. Naive decode pays one
|
||||||
|
traversal per emitted token; speculation pays one traversal per $\Gam(\alpha,K)$ emitted tokens, and
|
||||||
|
$\Gam$ is exactly the knob acceptance gives you. This is why speculative decoding is \emph{the}
|
||||||
|
lever for interactive geo-distributed serving, not one option among several: it is the only cure
|
||||||
|
that divides the wall instead of chipping at it.
|
||||||
|
|
||||||
|
\paragraph{The optimum is real and worth hitting.} Each curve in Figure~\ref{fig:spec} peaks. Below
|
||||||
|
$K^{\star}$ the fixed $\Tring$ is shared over too few tokens; above it, marginal drafts are accepted
|
||||||
|
with probability $\alpha^{K}\!\to\!0$ while still costing draft time $c_d$ and, eventually, verify
|
||||||
|
compute. The peak drifts right with $\alpha$ ($K^{\star}\!:\,6\to8\to12\to21$ for
|
||||||
|
$\alpha=0.6\to0.9$) because a better draft keeps paying off for longer. A deployment should tune $K$
|
||||||
|
to its measured acceptance and ring, not to a fixed default --- the difference between $K{=}4$ and
|
||||||
|
$K^{\star}$ is $\sim$1.7$\times$ at $\alpha=0.9$.
|
||||||
|
|
||||||
|
\section{Placement laws}
|
||||||
|
\label{sec:placement}
|
||||||
|
|
||||||
|
Four heuristics fall directly out of the models above; the first two are strategy, the last two are
|
||||||
|
free.
|
||||||
|
|
||||||
|
\begin{enumerate}[leftmargin=1.5em,itemsep=3pt]
|
||||||
|
\item \textbf{Regional-first placement.} $\Tring$ dominates $r$ (Eq.~\ref{eq:wall}), so pack the
|
||||||
|
whole pipeline into one region before spanning regions. A same-metro ring ($\Tring\!\approx\!2$\,ms)
|
||||||
|
is 94\% of local; a global ring is 11\%. Span a WAN only when memory capacity forces it, and when you
|
||||||
|
do, expect to pay for speculation (Section~\ref{sec:spec}) to stay interactive. LAN neighbours are
|
||||||
|
near-native; distant neighbours are a different regime.
|
||||||
|
\item \textbf{Speculate across every slow boundary.} Any ring above a few tens of milliseconds
|
||||||
|
should run a local draft. It is the only lever that touches the $\Tring$-per-token term, and it
|
||||||
|
composes with placement: regional-first shortens $\Tring$, speculation amortizes whatever remains.
|
||||||
|
\item \textbf{Co-locate embed $+$ first layers $+$ head $+$ sampler on the entry node} (free). Makes
|
||||||
|
the sample$\to$embed turnaround local and closes the ring in exactly $N$ hops instead of $N{+}1$
|
||||||
|
(Section~\ref{sec:wall}). One node owns both ends.
|
||||||
|
\item \textbf{Keep the KV cache local to each node's layers} (free, and automatic in PP). The KV
|
||||||
|
cache never crosses the wire; only the 32\,KiB hidden state does (Section~\ref{sec:bandwidth}). Do
|
||||||
|
nothing that would ship KV state between nodes per step --- it is the large state, and it has no
|
||||||
|
reason to move.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\section{Why pipeline-, not tensor-, parallel over slow links}
|
||||||
|
\label{sec:pptp}
|
||||||
|
|
||||||
|
The choice of \emph{how} to split the model is not free, and over a WAN it is decisive. The two
|
||||||
|
options generate fundamentally different network traffic per token.
|
||||||
|
|
||||||
|
\textbf{Pipeline parallel (PP)} splits by layer. Per token, the hidden state crosses each node
|
||||||
|
boundary once: $N$ point-to-point hops on the critical path, each moving the 32\,KiB of
|
||||||
|
Eq.~\eqref{eq:bhop}. Critical-path network latency per token $=\Tring=\sum_{i=1}^N\ell_i$.
|
||||||
|
|
||||||
|
\textbf{Tensor parallel (TP)} splits each layer's matrices across nodes~\cite{megatron}. Every layer
|
||||||
|
must \emph{all-reduce} partial results across all $N$ nodes to reconstruct the activation --- roughly
|
||||||
|
two collectives per layer (one after attention, one after the MLP). That is $\sim\!2L$ global
|
||||||
|
synchronizations per token, each of which, even counted optimistically as a single round-trip
|
||||||
|
($\ge\!2\bar\ell$; a ring all-reduce is worse, $2(N{-}1)$ serial steps), puts a barrier on the
|
||||||
|
critical path. Critical-path network latency per token $\gtrsim 2L\cdot2\bar\ell=4L\bar\ell$.
|
||||||
|
|
||||||
|
\paragraph{The traffic law.} Comparing the two, with $\bar\ell$ the mean one-way hop and
|
||||||
|
$\Tring\approx N\bar\ell$:
|
||||||
|
\begin{equation}
|
||||||
|
\frac{(\text{TP latency/token})}{(\text{PP latency/token})}
|
||||||
|
\;\gtrsim\;\frac{4L\bar\ell}{N\bar\ell}\;=\;\frac{4L}{N}.
|
||||||
|
\label{eq:pptp}
|
||||||
|
\end{equation}
|
||||||
|
\textbf{PP costs $O(\text{nodes})$ hops per token; TP costs $O(\text{layers})$ collectives per
|
||||||
|
token.} Since a model always has far more layers than nodes ($L\gg N$) and each TP collective is a
|
||||||
|
global barrier rather than a single hand-off, PP wins by a wide, structural margin: for $L=100$,
|
||||||
|
$N=3$, Eq.~\eqref{eq:pptp} gives $\gtrsim\!130\times$ less critical-path latency, and it is a lower
|
||||||
|
bound (a realistic ring all-reduce adds another $\sim(N{-}1)$ factor). TP also moves more bytes
|
||||||
|
($\sim\!2\dmodel b$ per node per collective, $\times 2L$), but latency alone settles it. TP belongs
|
||||||
|
\emph{inside} a node or an NVLink domain, where $\bar\ell$ is nanoseconds; across any link where
|
||||||
|
$\bar\ell$ is milliseconds, TP multiplies the wall by $\sim\!4L/N$ and PP is the only sane topology.
|
||||||
|
This is the structural companion to Section~\ref{sec:bandwidth}: PP's crossing tensor is
|
||||||
|
depth-independent precisely because it hands off once per node, and that is the same property that
|
||||||
|
makes it latency-cheap.
|
||||||
|
|
||||||
|
\section{What this paper claims, and what it does not}
|
||||||
|
\label{sec:honesty}
|
||||||
|
|
||||||
|
This is an analysis paper, and the analytical models \emph{are} the contribution --- so the line
|
||||||
|
between derived and measured is drawn explicitly.
|
||||||
|
|
||||||
|
\textbf{Measured (one number).} The 280\,MB/s link goodput is real: iperf3 over a direct 2.5\,GbE
|
||||||
|
cable on our mesh, sustained TCP ($\approx$2.24\,Gbit/s). It anchors the bandwidth claim in
|
||||||
|
Section~\ref{sec:bandwidth} and nothing else.
|
||||||
|
|
||||||
|
\textbf{Physical (not our measurement).} The one-way fiber latencies (40--130\,ms legs, 280\,ms
|
||||||
|
ring) are representative of real intercontinental routes and are bounded below by the speed of light
|
||||||
|
in fiber ($\approx$4.9\,$\mu$s/km). We cite the floor, not a traceroute we ran.
|
||||||
|
|
||||||
|
\textbf{Parameters (stated, not claimed).} $r_0=30$\,tok/s, $r_d=200$\,tok/s, $\alpha\in[0.6,0.9]$,
|
||||||
|
$\dmodel=16384$, $L\approx100$. These are reference values chosen to be representative of a
|
||||||
|
700B-class model with a small local draft; every table and plot is a closed-form evaluation at these
|
||||||
|
values, and the \emph{shape} of every result (the $1/(C+\Tring)$ wall, the $\Tring/\Gam$
|
||||||
|
amortization, the $4L/N$ law) is independent of the specific numbers.
|
||||||
|
|
||||||
|
\textbf{Not claimed.} We report no end-to-end wall-clock from a running geo-distributed deployment;
|
||||||
|
no measured acceptance rates for a specific draft/target pair; no serving-throughput sweep; no
|
||||||
|
overlap/async-speculation optimizations (which only improve the cure). Real acceptance depends on
|
||||||
|
the draft, the target, and the prompt distribution, and real rings have jitter and packet loss the
|
||||||
|
constant-$\ell$ model omits. The models predict \emph{regimes and ratios}, and those are what the
|
||||||
|
paper stands behind.
|
||||||
|
|
||||||
|
\section{Conclusion}
|
||||||
|
\label{sec:conclusion}
|
||||||
|
|
||||||
|
Distributing a large language model across machines is bandwidth-cheap and latency-expensive, and
|
||||||
|
the two costs live in different regimes. Bandwidth is trivial because the only tensor crossing a
|
||||||
|
pipeline boundary is a $\sim$32\,KiB hidden state --- depth-independent, KV-cache-free,
|
||||||
|
$\sim$0.1\,ms on a commodity link. Latency is a wall because autoregressive decode is a strict
|
||||||
|
dependency chain: every token traverses every node, the next cannot start until this one returns,
|
||||||
|
and so single-stream decode is floored at $1/(C+\Tring)$ --- $\approx$3\,tok/s on a global ring,
|
||||||
|
\emph{independent of the accelerator}, because the wall is the speed of light in fiber. The
|
||||||
|
``BitTorrent for AI'' picture is right for the throughput-bound work (weights, prefill, batch) and
|
||||||
|
wrong for the one dependency-bound workload (interactive decode) --- and that is the only workload
|
||||||
|
that is hard. The cure is to change what the network is charged for: speculative decoding turns $K$
|
||||||
|
sequential traversals into one, dividing the wall by the accepted-tokens-per-round $\Gam(\alpha,K)$
|
||||||
|
and recovering 12--21\,tok/s on the identical 280\,ms ring. Combined with regional-first placement, a
|
||||||
|
co-located ring turn, node-local KV, and pipeline- (never tensor-) parallelism over the slow links,
|
||||||
|
geo-distributed inference is not a contradiction --- it is a latency-engineering problem with a known
|
||||||
|
solution. The physics sets the floor; speculation decides how often you pay it.
|
||||||
|
|
||||||
|
\begin{thebibliography}{9}
|
||||||
|
\small
|
||||||
|
\bibitem{leviathan} Y.~Leviathan, M.~Kalman, Y.~Matias.
|
||||||
|
\emph{Fast Inference from Transformers via Speculative Decoding.} ICML 2023.
|
||||||
|
\bibitem{chen} C.~Chen, S.~Borgeaud, G.~Irving, J.-B.~Lespiau, L.~Sifre, J.~Jumper.
|
||||||
|
\emph{Accelerating Large Language Model Decoding with Speculative Sampling.} arXiv:2302.01318, 2023.
|
||||||
|
\bibitem{petals} A.~Borzunov, D.~Baranchuk, T.~Dettmers, M.~Ryabinin, et al.
|
||||||
|
\emph{Petals: Collaborative Inference and Fine-tuning of Large Models.} ACL 2023 (System Demonstrations).
|
||||||
|
\bibitem{gpipe} Y.~Huang, Y.~Cheng, A.~Bapna, et al.
|
||||||
|
\emph{GPipe: Efficient Training of Giant Neural Networks using Pipeline Parallelism.} NeurIPS 2019.
|
||||||
|
\bibitem{megatron} M.~Shoeybi, M.~Patwary, R.~Puri, P.~LeGresley, J.~Casper, B.~Catanzaro.
|
||||||
|
\emph{Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism.}
|
||||||
|
arXiv:1909.08053, 2019.
|
||||||
|
\bibitem{pipedream} D.~Narayanan, A.~Harlap, A.~Phanishayee, et al.
|
||||||
|
\emph{PipeDream: Generalized Pipeline Parallelism for DNN Training.} SOSP 2019.
|
||||||
|
\bibitem{xbackend} Hanzo AI. \emph{Edge LLM Inference Across Commodity Accelerators: hanzo-engine at
|
||||||
|
llama.cpp Decode Parity on Every Backend.} hanzo-engine white paper (companion, measured).
|
||||||
|
\end{thebibliography}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
Binary file not shown.
@@ -0,0 +1,414 @@
|
|||||||
|
\documentclass[11pt]{article}
|
||||||
|
\input{shared/kernel-preamble.tex}
|
||||||
|
|
||||||
|
\newcommand{\Tring}{\ensuremath{T_{\mathrm{ring}}}}
|
||||||
|
|
||||||
|
\title{\textbf{Open Problems in Heterogeneous\\ Distributed Inference}}
|
||||||
|
\author{Hanzo AI --- ML Systems}
|
||||||
|
\date{Research-agenda companion to the distributed-inference series}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\maketitle
|
||||||
|
|
||||||
|
\begin{abstract}
|
||||||
|
Three companion papers report a working heterogeneous distributed-inference stack. One engine shards a
|
||||||
|
model tensor-parallel across an AMD/NVIDIA boundary over commodity ethernet and decodes
|
||||||
|
coherently~\cite{het}; an analytical model derives the latency physics of geo-distributed autoregressive
|
||||||
|
decode and the speculative-decoding cure~\cite{latency}; and a three-plane blueprint turns the
|
||||||
|
trusted-LAN ring into a permissionless, consensus-managed, post-quantum-authenticated
|
||||||
|
network~\cite{consensus}. Building them surfaced eight problems a homogeneous, single-trust,
|
||||||
|
datacenter-local stack never has to confront, because each is an assumption that only \emph{heterogeneity},
|
||||||
|
\emph{wide-area distribution}, or \emph{permissionlessness} puts under load. This paper states the eight
|
||||||
|
precisely --- with the formal object where one exists --- and ties each to the companion that owns the
|
||||||
|
relevant result, so the boundary between what is measured, what is designed, and what is open stays sharp.
|
||||||
|
The eight are: (1) sampling-determinism boundaries in replicated-state inference; (2) quant-aware
|
||||||
|
tensor-parallel sharding on block-quantized weights; (3) pipeline parallelism as the format-agnostic
|
||||||
|
primitive and its layer-assignment ILP; (4) speculative decoding as a latency amortizer, made adaptive
|
||||||
|
under round-trip time and acceptance drift; (5) the prefill launch-overhead wall, attacked by fixed-shape
|
||||||
|
graph capture versus compiler-level fusion; (6) KV-cache wire compression for prefill migration and its
|
||||||
|
cross-hop error accumulation; (7) the geo-distributed decode latency floor and the placement/replication
|
||||||
|
optimization that follows from it; and (8) cheap verifiable inference for a permissionless compute
|
||||||
|
network. None is a tuning parameter; each is a research question, and several compose --- the layer
|
||||||
|
schedule of (3) and the geo-placement of (7) are one joint optimization, and the determinism boundary of
|
||||||
|
(1) and the cheap verification of (8) share the canonical-\code{f32} substrate. We publish the agenda so
|
||||||
|
the problems can be attacked on the same terms.
|
||||||
|
\end{abstract}
|
||||||
|
|
||||||
|
\section{Introduction: where the assumptions break}
|
||||||
|
\label{sec:intro}
|
||||||
|
|
||||||
|
A model-serving stack built for one datacenter, one vendor, and one operator gets to assume a great deal:
|
||||||
|
every rank runs the same silicon and the same numeric format, the network between ranks is a nanosecond-
|
||||||
|
scale NVLink fabric, and every node is trusted to compute what it claims. The distributed-inference series
|
||||||
|
removes those assumptions one at a time --- the fleet becomes vendor-mixed~\cite{het}, the links become
|
||||||
|
intercontinental~\cite{latency}, and the nodes become strangers who must be paid and
|
||||||
|
verified~\cite{consensus} --- and each removal exposes a problem the assumption had been hiding.
|
||||||
|
|
||||||
|
This paper is the ledger of what those removals expose. It is deliberately \emph{not} a results paper: it
|
||||||
|
states problems, not solutions, and its contribution is precision --- each problem is pinned to the
|
||||||
|
companion that owns the surrounding result, given its formal shape where it has one, and separated from
|
||||||
|
the parts already solved so that no claim is duplicated across the series. We adopt the series' discipline
|
||||||
|
of labelling measured, designed, and open, and here everything is open by construction; the job is to say
|
||||||
|
\emph{exactly} what is open and why the environment forces it.
|
||||||
|
|
||||||
|
The eight problems fall into four families. \textbf{Correctness under replication} (\S\ref{sec:determinism}):
|
||||||
|
when several ranks must agree on a sampled token, where must their arithmetic be bit-identical?
|
||||||
|
\textbf{The right primitive and its schedule} (\S\ref{sec:quantshard}--\S\ref{sec:pp}): tensor parallelism
|
||||||
|
is correct but coupled to dequantized weights and to a per-layer collective; pipeline parallelism is
|
||||||
|
format-agnostic but demands a hard scheduling decision. \textbf{Paying the wire} (\S\ref{sec:spec}--\S\ref{sec:geofloor}):
|
||||||
|
the latency of a wide-area hop is on the critical path of every token, and the levers are speculation,
|
||||||
|
compression, and placement. \textbf{Trust without an operator} (\S\ref{sec:verify}): a permissionless node
|
||||||
|
can lie about its work, and full re-execution is too expensive to be the answer. We take them in that
|
||||||
|
order.
|
||||||
|
|
||||||
|
\section{Problem 1: determinism boundaries in replicated-state inference}
|
||||||
|
\label{sec:determinism}
|
||||||
|
|
||||||
|
\paragraph{Statement.} In any inference topology where more than one rank holds replicated state that must
|
||||||
|
stay coherent --- most sharply, the tensor-parallel ring of~\cite{het}, where each rank keeps its own KV
|
||||||
|
cache and advances it token by token --- every rank must sample the \emph{same} next token, or the caches
|
||||||
|
diverge and the streams silently fork. The cross-vendor ring makes the \emph{reduced activations} agree
|
||||||
|
bit-for-bit by exchanging them in a canonical \code{f32} wire and reducing with a canonical
|
||||||
|
tie-break~\cite{het}. But the final logits are computed \emph{after} the last reduction, locally, in each
|
||||||
|
rank's own 16-bit compute dtype (\code{f16} on the ROCm rank, \code{bf16} on the CUDA rank). Sampling is
|
||||||
|
therefore \emph{outside} the agreement the wire guarantees: at a near-argmax tie, \code{f16} and
|
||||||
|
\code{bf16} rounding can select different tokens, and the ranks desync.
|
||||||
|
|
||||||
|
\paragraph{The formal object.} Call the \emph{agreement set} $\mathcal{A}$ the minimal set of tensors that
|
||||||
|
must be bit-identical across replicas for state to stay coherent. The canonical-\code{f32} reduce places
|
||||||
|
the post-attention and post-MLP activations in $\mathcal{A}$. Coherent decode additionally requires the
|
||||||
|
\emph{sampled token id} $\in\mathcal{A}$; the open question is the cheapest way to put it there. Two
|
||||||
|
mechanisms bound the design space:
|
||||||
|
\begin{itemize}[leftmargin=1.4em,itemsep=2pt]
|
||||||
|
\item \textbf{Broadcast the decision.} One head rank samples and broadcasts the token id; the others do not
|
||||||
|
sample at all. Cost: one small ($\le 8$-byte) broadcast per token on the critical path. Correct by
|
||||||
|
construction for \emph{any} sampler (greedy, temperature, top-$p$, min-$p$), because there is exactly one
|
||||||
|
draw.
|
||||||
|
\item \textbf{Canonicalize the computation.} Every rank computes logits in canonical \code{f32} with a
|
||||||
|
deterministic tie-break and a shared RNG stream, so all ranks sample identically without communicating.
|
||||||
|
Cost: an \code{f32} logit head on every rank and a synchronized RNG; fragile the moment any op on the path
|
||||||
|
is not bit-reproducible across vendors.
|
||||||
|
\end{itemize}
|
||||||
|
The trade is broadcast latency versus redundant canonical compute, and it interacts with the sampler:
|
||||||
|
stochastic sampling needs a \emph{shared seed and a shared draw}, which effectively forces the broadcast
|
||||||
|
mechanism, so the ``canonicalize'' route is really only clean for greedy decode.
|
||||||
|
|
||||||
|
\paragraph{What is open.} A general theory of determinism boundaries for replicated-state inference: which
|
||||||
|
ops must be canonical, the cost of canonicalization versus decision-broadcast as a function of vocabulary
|
||||||
|
size and ring width, and the extension to speculative decoding (\S\ref{sec:spec}), where the boundary must
|
||||||
|
hold not for one token but for a verified block of $K$. This is the correctness dual of the verification
|
||||||
|
problem of \S\ref{sec:verify}: the same canonical-\code{f32} property that makes two honest ranks agree is
|
||||||
|
what lets a third party \emph{check} that they did.
|
||||||
|
|
||||||
|
\section{Problem 2: quant-aware tensor-parallel sharding}
|
||||||
|
\label{sec:quantshard}
|
||||||
|
|
||||||
|
\paragraph{Statement.} Tensor parallelism shards each weight matrix along rows (column-parallel) or columns
|
||||||
|
(row-parallel), giving each rank a contiguous slice of the matrix~\cite{megatron}. The block-quantized
|
||||||
|
formats that make edge inference fit in commodity memory --- GGUF $k$-quants and their kin --- store
|
||||||
|
weights in \emph{superblocks}: a run of (typically) 256 weights sharing a block scale, with sub-block
|
||||||
|
scales and a packed low-bit mantissa. A naive row/column shard cuts \emph{through} a superblock, so a
|
||||||
|
rank's slice no longer contains the shared scale its weights are quantized against. The only way to shard
|
||||||
|
such a layout with an arbitrary cut is to dequantize the whole matrix, slice, and requantize per shard ---
|
||||||
|
which reintroduces the dequant--requant loss the format exists to avoid, and defeats the memory saving by
|
||||||
|
materializing the dense matrix. This is why the shipping TP path in~\cite{het} is wired only through the
|
||||||
|
dequantized (safetensors) model definitions and cannot shard a GGUF model at all.
|
||||||
|
|
||||||
|
\paragraph{What is open.} \emph{Quant-aware sharding}: a split that respects superblock boundaries. If the
|
||||||
|
shard count divides the matrix dimension into superblock-aligned pieces (or the matrix is padded to make
|
||||||
|
it so), each rank receives whole superblocks with their scales intact and no dequantization is needed. The
|
||||||
|
open questions are the constraints and their cost: which (hidden size, head count, shard count, block size)
|
||||||
|
combinations admit an aligned split; how much padding waste an arbitrary combination incurs; whether a
|
||||||
|
format with per-shard scale replication can be sharded losslessly at any cut; and whether the analysis
|
||||||
|
differs for row- versus column-parallel layers, which cut different axes of the superblock grid. The prize
|
||||||
|
is TP over quantized weights --- but note that pipeline parallelism (\S\ref{sec:pp}) sidesteps the entire
|
||||||
|
problem, because it ships \emph{activations}, not weight slices, across the seam and each node loads its
|
||||||
|
layer range in whatever quantization it likes. Problem~2 is worth solving only where TP is worth keeping:
|
||||||
|
inside a node or an NVLink domain, for a matrix too wide to hold on one accelerator.
|
||||||
|
|
||||||
|
\section{Problem 3: pipeline parallelism as the universal primitive, and its layer-assignment ILP}
|
||||||
|
\label{sec:pp}
|
||||||
|
|
||||||
|
\paragraph{The engineering result (in progress).} The heterogeneous-cluster analysis of~\cite{het} argues,
|
||||||
|
and the latency analysis of~\cite{latency} proves under a wide-area cost model, that \emph{pipeline
|
||||||
|
parallelism} (PP) is the right primitive for a heterogeneous fleet: it is format-agnostic (\S\ref{sec:quantshard}),
|
||||||
|
carries only an activation tensor across each node boundary rather than an all-reduce per layer, imposes no
|
||||||
|
head-count-divides-node-count constraint, and reduces the sampling determinism boundary
|
||||||
|
(\S\ref{sec:determinism}) to a single head sampler. Implementing PP as the default multi-node primitive in
|
||||||
|
the same engine is an engineering task under way in the codebase; it is not the open problem. The open
|
||||||
|
problem is what PP forces you to decide that TP does not.
|
||||||
|
|
||||||
|
\paragraph{The open problem: optimal layer-range assignment.} PP must cut the $L$-layer model into $N$
|
||||||
|
contiguous ranges and assign each to a node, under nodes that differ in \emph{compute}, \emph{memory}, and
|
||||||
|
\emph{link bandwidth} simultaneously. Let node $j$ have compute throughput $\rho_j$ (layers/s-equivalent)
|
||||||
|
and memory capacity $M_j$; let layer $l$ have compute weight $w_l$ and resident footprint $m_l$ (weights
|
||||||
|
plus its KV cache). A pipeline is a permutation $\pi$ of nodes onto stages and a contiguous partition
|
||||||
|
$0=c_0<c_1<\dots<c_N=L$ assigning layers $[c_{j-1},c_j)$ to stage $j$. Steady-state pipelined throughput is
|
||||||
|
set by the slowest stage, so the assignment problem is
|
||||||
|
\begin{equation}
|
||||||
|
\min_{\pi,\;c}\;\max_{1\le j\le N}\;
|
||||||
|
\max\!\left(\underbrace{\frac{1}{\rho_{\pi(j)}}\sum_{l=c_{j-1}}^{c_j-1} w_l}_{\text{stage compute}},\;
|
||||||
|
\underbrace{\frac{B_{\mathrm{hop}}}{b_{\pi(j),\pi(j+1)}}}_{\text{activation transmit}}\right)
|
||||||
|
\quad\text{s.t.}\quad
|
||||||
|
\sum_{l=c_{j-1}}^{c_j-1} m_l \le M_{\pi(j)}\ \ \forall j,
|
||||||
|
\label{eq:ilp}
|
||||||
|
\end{equation}
|
||||||
|
where $B_{\mathrm{hop}}$ is the per-hop activation size and $b_{\cdot,\cdot}$ the link bandwidth between
|
||||||
|
consecutive stages. For a \emph{fixed} node ordering $\pi$ the inner problem --- partition a weighted
|
||||||
|
sequence into $N$ contiguous parts minimizing the bottleneck part under per-part capacity --- is solvable
|
||||||
|
exactly by dynamic programming or parametric search. The difficulty is the \emph{joint} choice of ordering
|
||||||
|
$\pi$ (which node is adjacent to which, hence which link carries each hop) together with the partition and,
|
||||||
|
in the geo setting (\S\ref{sec:geofloor}), the placement of nodes in space: that couples an assignment, a
|
||||||
|
sequencing, and a facility-location decision, and is NP-hard in general.
|
||||||
|
|
||||||
|
\paragraph{What is open.} A practical solver: good heuristics with a bounded approximation ratio for
|
||||||
|
Eq.~\eqref{eq:ilp}; online \emph{re-balancing} as nodes join, leave, or thermally throttle (the membership
|
||||||
|
of a permissionless network~\cite{consensus} is not static); and the memory-vs-compute tension when the
|
||||||
|
throughput-optimal cut violates a small node's capacity. The single-stream \emph{latency} objective ---
|
||||||
|
$\sum_j$ stage compute $+\,\Tring$ rather than the bottleneck --- is the variant~\cite{latency} analyses;
|
||||||
|
Eq.~\eqref{eq:ilp} is its throughput dual, and a real scheduler must weigh the two. Prior PP scheduling
|
||||||
|
work assumes homogeneous stages~\cite{gpipe} or homogeneous datacenter nodes~\cite{pipedream}; the open
|
||||||
|
case is genuinely heterogeneous $\rho_j$, $M_j$, and $b_{jk}$ at once.
|
||||||
|
|
||||||
|
\section{Problem 4: speculative decoding as a geo-distributed latency amortizer, made adaptive}
|
||||||
|
\label{sec:spec}
|
||||||
|
|
||||||
|
\paragraph{What the companion owns.} The latency paper~\cite{latency} derives the speculative-decoding
|
||||||
|
cure in full: a local draft proposes $K$ tokens, the distributed target verifies all $K$ in one pipeline
|
||||||
|
pass, and at acceptance rate $\alpha$ the effective rate is
|
||||||
|
$r_{\mathrm{spec}}(\alpha,K)=\frac{1-\alpha^{K+1}}{1-\alpha}\big/(Kc_d+C+\Tring)$, which recovers
|
||||||
|
interactive speeds on an intercontinental ring by paying the round-trip once per accepted \emph{block}
|
||||||
|
rather than once per token. That derivation, its optimal $K^\star$ for fixed $\alpha$, and the numbers are
|
||||||
|
the companion's result and are not restated here.
|
||||||
|
|
||||||
|
\paragraph{What is open.} The companion treats $\alpha$ as a fixed parameter and $K$ as a tuned constant.
|
||||||
|
In deployment neither holds. The acceptance rate $\alpha$ \emph{drifts} within a single generation --- it
|
||||||
|
is high on boilerplate and low at genuine decision points, and it depends on prompt, temperature, and how
|
||||||
|
far the draft and target have diverged in context --- so the throughput-optimal $K$ is a moving target,
|
||||||
|
not a launch-time flag. The open problem is an \emph{online} controller that adapts $K$ to the measured
|
||||||
|
acceptance and the measured ring latency $\Tring$: estimate $\alpha$ from a trailing window of
|
||||||
|
accept/reject outcomes, react to $\Tring$ jitter and packet loss that the constant-$\ell$ model omits, and
|
||||||
|
set $K$ to the running optimum with a stability guarantee (a controller that chases a noisy $\alpha$ can
|
||||||
|
oscillate). Two couplings make it harder and are themselves open: the determinism boundary of
|
||||||
|
\S\ref{sec:determinism} must hold across the \emph{whole verified block}, not one token, so a replicated
|
||||||
|
target must agree on the accept/reject cut; and the draft can itself be placed on a nearby, more reliable
|
||||||
|
node~\cite{consensus}, making draft placement part of the scheduling problem of \S\ref{sec:pp}. The
|
||||||
|
genuinely empirical unknown --- measured $\alpha$ for real draft/target pairs on real prompt distributions,
|
||||||
|
which the companion explicitly does not claim --- gates any adaptive scheme.
|
||||||
|
|
||||||
|
\section{Problem 5: the prefill launch-overhead wall}
|
||||||
|
\label{sec:prefill}
|
||||||
|
|
||||||
|
\paragraph{Statement.} On identical mathematics, hanzo-engine prefill reaches a lower GPU utilization than
|
||||||
|
\code{llama.cpp} not because any kernel is slow but because there are too many kernels. Profiled on an
|
||||||
|
NVIDIA GB10 (node \emph{spark}) running a 35B-class mixture-of-experts prefill, the engine sustains
|
||||||
|
$\approx$79.3\% GPU utilization across $\approx$33{,}000 kernel launches per step, against
|
||||||
|
\code{llama.cpp}'s $\approx$97.3\% across $\approx$3{,}000 --- an order of magnitude more launches for the
|
||||||
|
same work. Per-kernel \emph{busy} time is already at parity; the gap is entirely the idle interstice
|
||||||
|
between launches, i.e.\ launch and scheduling overhead, not arithmetic. Decode does not have this problem,
|
||||||
|
because the decode path is captured into a CUDA graph that replays the whole token as one submission; the
|
||||||
|
prefill path is not, because mixture-of-experts routing is \emph{data-dependent} --- which experts fire
|
||||||
|
depends on the tokens --- so the shape of the work changes per step and a naive static graph capture does
|
||||||
|
not apply.
|
||||||
|
|
||||||
|
\paragraph{Two attacks, and they compose.} There are two structurally different ways to collapse the launch
|
||||||
|
count, and they are not exclusive:
|
||||||
|
\begin{itemize}[leftmargin=1.4em,itemsep=2pt]
|
||||||
|
\item \textbf{Fixed-shape chunked graph capture.} Quantize the data-dependent dimension: route to a fixed
|
||||||
|
capacity per expert (pad or drop to a chunk size), so the per-step work has a bounded set of shapes, and
|
||||||
|
capture a CUDA graph per shape. This keeps the many small kernels but pays their launch cost \emph{once}
|
||||||
|
at capture and replays them as one submission, exactly as decode already does. It is a runtime technique
|
||||||
|
and inherits the graph machinery the engine has.
|
||||||
|
\item \textbf{Compiler-level DAG fusion.} Reduce the \emph{number} of kernels rather than the cost of
|
||||||
|
launching them: fuse elementwise and reduction chains into fewer, fatter kernels at compile time, so there
|
||||||
|
is less to launch in the first place. This is the subject of the kernel-fusion companion~\cite{fuse},
|
||||||
|
where fusion is shown to be composition; extending it from local elementwise chains to the full prefill DAG
|
||||||
|
(across the MoE gate, the expert GEMMs, and the combine) is the open compiler problem.
|
||||||
|
\end{itemize}
|
||||||
|
Graph capture attacks the \emph{launch cost}; fusion attacks the \emph{launch count}; applied together they
|
||||||
|
multiply. The decisive reason to invest in fusion as well as capture is portability: graph capture is a
|
||||||
|
CUDA (and partial ROCm) facility, but fusion is a property of the kernel \emph{source} and therefore
|
||||||
|
transfers to every backend the engine targets --- Metal, Vulkan, and ROCm, where graph support is weaker or
|
||||||
|
absent~\cite{backend}. The open problem is the fusion \emph{partitioner}: given the prefill DAG, choose the
|
||||||
|
kernel boundaries that minimize launches subject to register and shared-memory limits, and do it once,
|
||||||
|
portably, from the one source.
|
||||||
|
|
||||||
|
\section{Problem 6: KV-cache wire compression for prefill migration}
|
||||||
|
\label{sec:kvcompress}
|
||||||
|
|
||||||
|
\paragraph{Statement.} In steady-state pipeline decode the KV cache never crosses the wire --- each node
|
||||||
|
keeps the keys and values for its own layers~\cite{latency}. The exception is \emph{migration}: moving a
|
||||||
|
prefilled context between nodes, as in disaggregated prefill/decode, session hand-off, or re-placement when
|
||||||
|
the schedule of \S\ref{sec:pp} changes. Then the KV cache --- the large, context-growing state --- must be
|
||||||
|
shipped, and its size, not the activation's, sets the transfer cost. The engine already has an FP8 KV
|
||||||
|
representation, so the obvious move is to ship KV in FP8 and halve the bytes.
|
||||||
|
|
||||||
|
\paragraph{What is open.} Error accumulation across hops. A single FP8 round-trip of the KV cache is a
|
||||||
|
tolerable, well-studied quantization. The open question is what happens when compression is applied
|
||||||
|
\emph{repeatedly} --- a context migrated across several nodes over its lifetime, or a design that keeps KV
|
||||||
|
in FP8 resident and re-quantizes on each write --- so that quantization error compounds hop over hop and
|
||||||
|
layer over layer. Formally: bound the accumulated error of $H$ successive FP8 (de)compressions of a KV
|
||||||
|
cache as a function of $H$, context length, and layer depth, and identify the schemes that arrest it ---
|
||||||
|
error feedback (carry the residual forward), selective precision (keep the attention-sink and recent tokens
|
||||||
|
in higher precision), or a migration protocol that recompresses from a retained high-precision anchor
|
||||||
|
rather than from the last lossy copy. This is the compression dual of the determinism boundary
|
||||||
|
(\S\ref{sec:determinism}): the all-reduce must be canonical \code{f32} because it is summed every layer,
|
||||||
|
but the KV cache may tolerate FP8 precisely because it is \emph{read}, not reduced --- and the open problem
|
||||||
|
is exactly how much, and for how many hops.
|
||||||
|
|
||||||
|
\section{Problem 7: the geo-distributed decode latency floor and the placement it dictates}
|
||||||
|
\label{sec:geofloor}
|
||||||
|
|
||||||
|
\paragraph{What the companion owns.} The latency paper~\cite{latency} establishes the floor: single-stream
|
||||||
|
autoregressive decode must traverse every pipeline stage per token, so its rate is $1/(C+\Tring)$ where
|
||||||
|
$\Tring$ is the one-way loop latency, and because $\Tring$ is bounded below by the speed of light in fiber
|
||||||
|
($\approx$4.9\,$\mu$s/km), an intercontinental ring floors decode at a few tok/s \emph{regardless of
|
||||||
|
accelerator speed}. The companion also states the levers as placement laws --- regional-first placement,
|
||||||
|
speculate across every slow boundary, co-locate the ring turn, keep KV node-local --- and proves pipeline
|
||||||
|
parallelism dominates tensor parallelism over any slow link by $\sim\!4L/N$. The floor, the laws, and the
|
||||||
|
$4L/N$ result are the companion's and are not restated.
|
||||||
|
|
||||||
|
\paragraph{What is open.} The companion's laws are heuristics, stated and argued but not optimized or
|
||||||
|
measured end-to-end. Two things remain. First, \emph{empirical validation}: the companion is explicit that
|
||||||
|
it reports no wall-clock from a running geo deployment; a real multi-continent ring, with real jitter and
|
||||||
|
loss, must confirm the $1/(C+\Tring)$ floor and the speculative recovery. Second, and deeper, the
|
||||||
|
\emph{placement and replication optimization} the floor implies. If one ring cannot serve a global user
|
||||||
|
base at interactive latency, the answer is \emph{regional replicas} --- multiple rings, each latency-local
|
||||||
|
to a set of users, with requests routed to the nearest --- and choosing how many rings, in which regions,
|
||||||
|
holding which shards, to minimize the population's tail latency under a memory and cost budget is a
|
||||||
|
facility-location problem entangled with the layer-assignment ILP of \S\ref{sec:pp}: where a node sits in
|
||||||
|
space changes both which users it is near and which link it contributes to its ring. Formalizing that
|
||||||
|
joint placement--partition problem, and the routing that assigns each request to a ring, is open, and it is
|
||||||
|
the geo-scale version of Problem~3.
|
||||||
|
|
||||||
|
\section{Problem 8: cheap verifiable inference for a permissionless compute network}
|
||||||
|
\label{sec:verify}
|
||||||
|
|
||||||
|
\paragraph{What the companion owns.} The three-plane blueprint~\cite{consensus} builds the transport and
|
||||||
|
control layers of a permissionless inference network: a post-quantum-authenticated ZAP wire that proves
|
||||||
|
\emph{who} sent a tensor, a Quasar consensus control plane that admits staked miners and agrees the
|
||||||
|
placement map, and a Photon committee that can threshold-sign an $(\text{request},\text{output})$ pair.
|
||||||
|
For result integrity it proposes three composable mechanisms --- threshold attestation of outputs,
|
||||||
|
redundant re-execution with the canonical-\code{f32} property as a bit-exact cross-vendor oracle, and
|
||||||
|
luminance reputation as the economic backstop --- and labels them, honestly, as design. Authentication and
|
||||||
|
committee formation are solved there and not reopened here.
|
||||||
|
|
||||||
|
\paragraph{What is open: verification that is cheaper than the work.} Authentication proves who sent a
|
||||||
|
tensor, not that the tensor is \emph{correct}, and the blueprint's strongest integrity mechanism ---
|
||||||
|
redundant re-execution --- costs a full second computation on every checked request. That is fine as a
|
||||||
|
sampled spot check but cannot be the common path: a network whose verification costs as much as its
|
||||||
|
inference has no margin to pay honest miners. The open problem is \emph{sub-linear} verification of correct
|
||||||
|
inference. Candidate directions, each unproven for transformer decode:
|
||||||
|
\begin{itemize}[leftmargin=1.4em,itemsep=2pt]
|
||||||
|
\item \textbf{Sampling-based logit audits.} A verifier recomputes a \emph{random subset} of the claimed
|
||||||
|
logits (a few positions, a few layers) and checks them against the miner's committed output, catching a
|
||||||
|
cheating miner with high probability at a fraction of the compute. The open question is the soundness
|
||||||
|
--- how large a subset defeats an adversary who corrupts only the tokens a user cares about --- and how it
|
||||||
|
composes with the canonical-\code{f32} determinism (\S\ref{sec:determinism}) that makes a recomputed logit
|
||||||
|
comparable bit-for-bit in the first place.
|
||||||
|
\item \textbf{Redundant-execution economics.} Treat verification as a game: honest computation must be the
|
||||||
|
profit-maximizing strategy under a spot-check probability $p$, a stake bond, and a slashing penalty. The
|
||||||
|
open problem is to price $p$, the bond, and the reward so that the \emph{expected} cost of cheating exceeds
|
||||||
|
its gain for a rational miner, while keeping the honest miner's expected verification tax small --- and to
|
||||||
|
do so against collusion among a committee and grinding on the verification lottery, threats the blueprint
|
||||||
|
names but does not price.
|
||||||
|
\end{itemize}
|
||||||
|
The unifying observation is that the canonical-\code{f32} wire built for cross-vendor \emph{correctness}
|
||||||
|
(\S\ref{sec:determinism}, \cite{het}) is the same substrate that makes inference \emph{checkable}: two
|
||||||
|
honest nodes agree bit-for-bit, so any disagreement is a proof of fault rather than a rounding artifact.
|
||||||
|
Turning that from a full-re-execution check into a sampled, economically-sound one is the last open
|
||||||
|
problem, and the one that decides whether a permissionless inference network can exist at all.
|
||||||
|
|
||||||
|
\section{Synthesis}
|
||||||
|
\label{sec:synthesis}
|
||||||
|
|
||||||
|
The eight problems are not independent, and Table~\ref{tab:map} states their dependencies as plainly as
|
||||||
|
their statements. Three throughlines run across them.
|
||||||
|
|
||||||
|
\emph{Canonical \code{f32} is load-bearing twice.} The same property that makes an \code{f16} rank and a
|
||||||
|
\code{bf16} rank agree on a reduced activation (\S\ref{sec:determinism}) is what lets a third party check a
|
||||||
|
miner's work without a rounding dispute (\S\ref{sec:verify}). Correctness under replication and verifiable
|
||||||
|
correctness are the same substrate seen from two sides.
|
||||||
|
|
||||||
|
\emph{Scheduling is one problem at two scales.} The layer-assignment ILP of \S\ref{sec:pp} and the
|
||||||
|
regional-replica placement of \S\ref{sec:geofloor} are the LAN and geo faces of a single
|
||||||
|
partition-and-place optimization; solving one in isolation leaves the other's coupling on the table, and a
|
||||||
|
real deployment sets both at once, with speculative-draft placement (\S\ref{sec:spec}) folded in.
|
||||||
|
|
||||||
|
\emph{Pipeline parallelism dissolves more than it creates.} It sidesteps quant-aware sharding
|
||||||
|
(\S\ref{sec:quantshard}), collapses the determinism boundary to one sampler (\S\ref{sec:determinism}), and
|
||||||
|
makes the graph-capture asymmetry a local choice (\S\ref{sec:prefill}) --- but it buys those with the
|
||||||
|
scheduling problem of \S\ref{sec:pp} and the migration cost of \S\ref{sec:kvcompress}. The net is
|
||||||
|
favourable, which is why the series argues for it; the residue is real, which is why it is here.
|
||||||
|
|
||||||
|
Finally, the register. Some of these are engineering with a known shape --- adaptive $K$ (\S\ref{sec:spec}),
|
||||||
|
fixed-shape graph capture (\S\ref{sec:prefill}), FP8 migration (\S\ref{sec:kvcompress}) --- and will yield
|
||||||
|
to careful implementation and measurement. Others are genuinely research --- the determinism-boundary
|
||||||
|
theory (\S\ref{sec:determinism}), the heterogeneous scheduling ILP (\S\ref{sec:pp}), sub-linear
|
||||||
|
verification (\S\ref{sec:verify}) --- and may not have clean answers. We have tried to say which is which,
|
||||||
|
because the most useful thing an open-problems paper can do is not overclaim the difficulty of the easy
|
||||||
|
ones or underclaim the depth of the hard ones.
|
||||||
|
|
||||||
|
\begin{table}[t]
|
||||||
|
\centering
|
||||||
|
\small
|
||||||
|
\setlength{\tabcolsep}{5pt}
|
||||||
|
\begin{tabular}{@{}p{0.4cm}p{4.5cm}p{2.2cm}p{5.1cm}@{}}
|
||||||
|
\toprule
|
||||||
|
\# & \textbf{Problem} & \textbf{Exposed by} & \textbf{What is open (owning companion)} \\
|
||||||
|
\midrule
|
||||||
|
1 & Sampling-determinism boundary & heterogeneity & minimal agreement set; broadcast vs.\ canonicalize cost~\cite{het} \\
|
||||||
|
2 & Quant-aware TP sharding & heterogeneity & superblock-aligned splits without dequant--requant~\cite{het} \\
|
||||||
|
3 & PP layer-assignment ILP & heterogeneity & Eq.~\eqref{eq:ilp}: heuristics, online rebalance~\cite{het,latency} \\
|
||||||
|
4 & Adaptive speculation & wide area & online $K$ under drifting $\alpha$, jittering $\Tring$~\cite{latency} \\
|
||||||
|
5 & Prefill launch-overhead wall & (single-node) & fusion partitioner; portable, composes with capture~\cite{fuse,backend} \\
|
||||||
|
6 & KV wire compression & wide area & cross-hop FP8 error accumulation bound~\cite{latency} \\
|
||||||
|
7 & Geo decode latency floor & wide area & empirical floor; regional-replica placement~\cite{latency} \\
|
||||||
|
8 & Cheap verifiable inference & permissionless & sub-linear logit audits; slashing economics~\cite{consensus} \\
|
||||||
|
\bottomrule
|
||||||
|
\end{tabular}
|
||||||
|
\caption{The eight open problems, the environment property that exposes each, and the open kernel with the
|
||||||
|
companion that owns the surrounding result. Problems 3 and 7 are one optimization at two scales; problems 1
|
||||||
|
and 8 share the canonical-\code{f32} substrate. Problem 5 is exposed already single-node but is sharpest
|
||||||
|
when prefill is distributed.}
|
||||||
|
\label{tab:map}
|
||||||
|
\end{table}
|
||||||
|
|
||||||
|
\section*{Acknowledgement of scope}
|
||||||
|
\addcontentsline{toc}{section}{Acknowledgement of scope}
|
||||||
|
This paper contributes problem statements, not solutions, and every result it leans on belongs to a cited
|
||||||
|
companion: the cross-vendor decode measurement and its residuals~\cite{het}, the latency floor and the
|
||||||
|
speculative cure~\cite{latency}, and the three-plane network with its verification design~\cite{consensus}.
|
||||||
|
Where a formal object appears --- the agreement set (\S\ref{sec:determinism}), the assignment ILP
|
||||||
|
Eq.~\eqref{eq:ilp}, the error-accumulation bound (\S\ref{sec:kvcompress}) --- it is stated to make the open
|
||||||
|
question precise, not claimed as solved. The measured numbers quoted (the launch-overhead profile of
|
||||||
|
\S\ref{sec:prefill}) are attributed to the hardware that produced them (NVIDIA GB10) and to the companion
|
||||||
|
that carries the surrounding stack~\cite{backend}.
|
||||||
|
|
||||||
|
\begin{thebibliography}{9}
|
||||||
|
\small
|
||||||
|
\bibitem{het} Hanzo AI --- ML Systems. \emph{One Cluster, Every Vendor: Cross-Vendor Tensor-Parallel LLM
|
||||||
|
Inference over Commodity Ethernet.} Distributed-inference companion (measured).
|
||||||
|
\bibitem{latency} Hanzo AI --- ML Systems. \emph{The Latency Physics of Geo-Distributed Autoregressive
|
||||||
|
Decode.} Distributed-inference companion (analytical).
|
||||||
|
\bibitem{consensus} Hanzo AI --- ML Systems. \emph{The Three-Plane Compute Fabric: Consensus-Managed,
|
||||||
|
ZAP-Transported Decentralized AI Inference.} Distributed-inference companion (architecture).
|
||||||
|
\bibitem{backend} Hanzo AI. \emph{Edge LLM Inference Across Commodity Accelerators: hanzo-engine at
|
||||||
|
llama.cpp Decode Parity on Every Backend.} Paper 05 (measured).
|
||||||
|
\bibitem{fuse} Hanzo AI. \emph{Fusion Is Composition: Kernel Fusion as the Functor Law.} Paper 07.1.
|
||||||
|
\bibitem{megatron} M.\ Shoeybi, M.\ Patwary, R.\ Puri, P.\ LeGresley, J.\ Casper, B.\ Catanzaro.
|
||||||
|
\emph{Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism.}
|
||||||
|
arXiv:1909.08053, 2019.
|
||||||
|
\bibitem{gpipe} Y.\ Huang et al. \emph{GPipe: Efficient Training of Giant Neural Networks using Pipeline
|
||||||
|
Parallelism.} NeurIPS, 2019.
|
||||||
|
\bibitem{pipedream} D.\ Narayanan, A.\ Harlap, A.\ Phanishayee, et al. \emph{PipeDream: Generalized
|
||||||
|
Pipeline Parallelism for DNN Training.} SOSP, 2019.
|
||||||
|
\bibitem{petals} A.\ Borzunov et al. \emph{Petals: Collaborative Inference and Fine-tuning of Large
|
||||||
|
Models.} arXiv:2209.01188, 2022.
|
||||||
|
\bibitem{leviathan} Y.\ Leviathan, M.\ Kalman, Y.\ Matias. \emph{Fast Inference from Transformers via
|
||||||
|
Speculative Decoding.} ICML, 2023.
|
||||||
|
\bibitem{chen} C.\ Chen, S.\ Borgeaud, G.\ Irving, J.-B.\ Lespiau, L.\ Sifre, J.\ Jumper. \emph{Accelerating
|
||||||
|
Large Language Model Decoding with Speculative Sampling.} arXiv:2302.01318, 2023.
|
||||||
|
\end{thebibliography}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
Binary file not shown.
@@ -0,0 +1,468 @@
|
|||||||
|
\documentclass[11pt]{article}
|
||||||
|
\input{shared/kernel-preamble.tex}
|
||||||
|
|
||||||
|
\title{\textbf{One Cluster, Every Vendor:\\ Cross-Vendor Tensor-Parallel LLM Inference over Commodity Ethernet}}
|
||||||
|
\author{Hanzo AI --- ML Systems}
|
||||||
|
\date{Distributed companion to the \emph{One Source, Every Backend} series --- extends Paper 05 across the wire}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
\maketitle
|
||||||
|
|
||||||
|
\begin{abstract}
|
||||||
|
Real accelerator fleets are vendor-mixed: an AMD APU, an NVIDIA dev kit, and a Mac sit on the same
|
||||||
|
desk, and no collective-communication library spans them --- NCCL is NVIDIA-only, RCCL is AMD-only, and
|
||||||
|
neither will open a channel to the other. This paper reports a working result, with its limits stated
|
||||||
|
plainly. One inference engine (\code{hanzo-engine}, Rust, one codebase, per-backend kernels) runs
|
||||||
|
on three vendor-diverse accelerators --- AMD ROCm gfx1151 (a ``Strix Halo'' APU, node \emph{evo}), NVIDIA
|
||||||
|
CUDA sm\_121 (a GB10 Blackwell, node \emph{spark}), and Apple Metal M4 Max (node \emph{dbc}). We make its
|
||||||
|
Megatron-style tensor-parallel (TP) collectives \emph{vendor-agnostic} by host-staging every exchange
|
||||||
|
--- \code{device}$\to$\code{CPU}$\to$\code{TCP}$\to$\code{CPU}$\to$\code{device} --- so any device that
|
||||||
|
can copy to host storage can join the ring, which is exactly what NCCL/RCCL cannot do across a vendor
|
||||||
|
boundary. On a two-node ring (\emph{evo} ROCm $+$ \emph{spark} CUDA) over a 2.5GbE direct link, the
|
||||||
|
sharded engine \textbf{builds, connects, shards a \code{world\_size}$=$2 tensor-parallel model, and
|
||||||
|
generates fully coherent multi-token output --- chain-of-thought and a correct answer --- across the
|
||||||
|
AMD/NVIDIA boundary at 50.4~tok/s decode} (TTFT 0.09s, 213~tok/s prefill). To our knowledge this is the
|
||||||
|
first reported cross-vendor (AMD$+$NVIDIA) tensor-parallel LLM inference over commodity ethernet in a
|
||||||
|
single engine.
|
||||||
|
|
||||||
|
We are equally explicit about the engineering, because the honest failures are the paper's most useful
|
||||||
|
content. The socket-ring path had only ever been \emph{compiled}, never \emph{run} (only the NCCL path
|
||||||
|
was exercised), and bringing it up cross-vendor surfaced four heterogeneity bugs --- a \code{len}$^2$
|
||||||
|
byte-count overflow, a missing ROCm host-staging arm that meant the ring never even compiled for AMD, a
|
||||||
|
10s handshake timeout that raced heterogeneous startup, and --- the subtle one that blocked coherent
|
||||||
|
decode --- a \emph{heterogeneous-dtype-on-the-wire} hazard, where the ROCm rank auto-selects \code{f16}
|
||||||
|
and the CUDA rank auto-selects \code{bf16} and each reinterpreted its neighbour's 16-bit words as the
|
||||||
|
wrong floating-point layout, corrupting the reduction from the first layer. The cure is a canonical
|
||||||
|
\code{f32} ring wire (a lossless superset of both) plus a \code{world\_size}$-$1 accumulating all-reduce
|
||||||
|
correct for any \code{N}$\geq$2 and the removal of a spurious power-of-two world-size restriction; all
|
||||||
|
four fixes are merged on \code{main}, and a same-vendor control (both ranks \code{f16}) matches the
|
||||||
|
cross-vendor run token-for-token, isolating the wire dtype as the sole cross-vendor variable. The
|
||||||
|
measured cost of the primitive is itself a result: 50.4~tok/s is $\approx$3.3$\times$ slower than the same
|
||||||
|
model single-node (168~tok/s), and the gap is \emph{all} synchronization --- an all-reduce every layer,
|
||||||
|
over the wire. That measured tax is the empirical case for \textbf{pipeline parallelism} --- contiguous
|
||||||
|
layer ranges, activations-only on the wire (\code{O(nodes)} hops, not \code{O(layers)} all-reduces),
|
||||||
|
format-agnostic, one generation loop driving stateless workers --- as the structurally better primitive
|
||||||
|
for heterogeneous clusters: the numeric fixes made TP \emph{correct}, not well-matched. Three residuals
|
||||||
|
stay honestly open, and we develop them, with five further problems, as a research agenda in the
|
||||||
|
companion~\cite{openproblems}: the two ranks still sample independently from their own-dtype logits (a
|
||||||
|
near-tie argmax can diverge and desync the KV caches), the CUDA rank must run graphless to stay lockstep
|
||||||
|
with the graphless ROCm rank, and TP is still coupled to the dequantized (safetensors) weight path.
|
||||||
|
\end{abstract}
|
||||||
|
|
||||||
|
\section{The problem: fleets are vendor-mixed, and the collective libraries are not}
|
||||||
|
\label{sec:problem}
|
||||||
|
|
||||||
|
The single-node premise of this series is that the interesting inference target is a commodity box a
|
||||||
|
customer already owns~\cite{backend}. The multi-node reality is that they own \emph{several}, and not
|
||||||
|
from one vendor. A team that bought an AMD ``Strix Halo'' mini-PC last quarter, a Grace-Blackwell dev kit
|
||||||
|
this quarter, and codes on a MacBook has three capable accelerators totalling well over a hundred
|
||||||
|
gigabytes of unified memory --- enough to hold a model no single one of them can --- sitting idle on a
|
||||||
|
LAN. The obstacle is not the models and not the math. It is that \emph{the collective-communication
|
||||||
|
layer is single-vendor by construction}.
|
||||||
|
|
||||||
|
Tensor parallelism~\cite{megatron} shards each weight matrix across ranks and, twice per transformer
|
||||||
|
block (after attention, after the MLP), performs an \code{all\_reduce} to sum the partial results back
|
||||||
|
into a full activation. That \code{all\_reduce} is the whole game, and in practice it is delegated to a
|
||||||
|
vendor library: \textbf{NCCL}~\cite{nccl} on NVIDIA, \textbf{RCCL}~\cite{rccl} on AMD. Both are excellent
|
||||||
|
and both are closed at the vendor boundary: NCCL speaks to NVIDIA GPUs over NVLink/PCIe/GPUDirect and
|
||||||
|
has no concept of an AMD peer; RCCL is the symmetric mirror. There is no NCCL$\leftrightarrow$RCCL
|
||||||
|
channel, no cross-vendor GPUDirect, and no shared handle either side will accept. So the textbook path to
|
||||||
|
running one model across \emph{evo} and \emph{spark} together does not merely underperform --- it does
|
||||||
|
not exist.
|
||||||
|
|
||||||
|
The engine already dodges the \emph{first} half of this problem. \code{hanzo-engine} is one Rust codebase
|
||||||
|
that lowers to CUDA, ROCm, Metal, Vulkan, and CPU behind a single model loader, reaching \code{llama.cpp}
|
||||||
|
decode parity on all three vendors from that one build~\cite{backend}; the kernel layer that makes ``one
|
||||||
|
source, every backend'' hold is the subject of the companion DSL papers~\cite{umbrella,fuse,oracle}. What
|
||||||
|
was missing is the \emph{second} half: a way for those per-vendor engines to talk to \emph{each other}
|
||||||
|
inside one cluster. This paper builds that channel, runs it across a vendor boundary for the first time,
|
||||||
|
and reports --- without varnish --- exactly how far it gets and what it cost to get there.
|
||||||
|
|
||||||
|
\section{The design: a host-staged socket ring makes the collective vendor-agnostic}
|
||||||
|
\label{sec:design}
|
||||||
|
|
||||||
|
The engine carries a unified \code{Comm} abstraction with three backends behind one enum: \code{Nccl}
|
||||||
|
(the fast same-vendor NVIDIA path), \code{Ring} (a socket-based collective), and \code{Dummy}
|
||||||
|
(\code{world\_size}$=$1). The idea that makes cross-vendor work possible is in the \code{Ring} backend,
|
||||||
|
and it is deliberately humble: \textbf{stage every tensor through host memory and move the bytes over
|
||||||
|
TCP.}
|
||||||
|
|
||||||
|
Concretely, a ring \code{sum\_all\_reduce} does not touch a vendor collective at all. It pulls the
|
||||||
|
tensor's storage to the CPU, converts it to a canonical \code{f32} payload, exchanges the bytes with its
|
||||||
|
ring neighbours over ordinary TCP sockets, sums in \code{f32}, and copies the reduced result back to the
|
||||||
|
device in its native dtype (Listing~\ref{lst:stage}). The single load-bearing property is that the
|
||||||
|
exchange happens in \code{CpuStorage}, which \emph{every} backend can produce --- CUDA, Metal, and ROCm
|
||||||
|
each implement \code{to\_cpu\_storage()}. The device that produced the tensor is irrelevant by the time
|
||||||
|
the bytes reach the socket. \emph{Why} the wire dtype is a canonical \code{f32} rather than the raw
|
||||||
|
device bytes --- the difference between coherent and garbage output across vendors --- is the subject of
|
||||||
|
\S\ref{sec:rootcause}; here it is simply the shipped design.
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=Rust,caption={The vendor-agnostic collective, condensed from the shipping
|
||||||
|
code. The reduce goes device$\to$host$\to$canonical \code{f32}$\to$TCP$\to$\code{f32} sum$\to$device; the
|
||||||
|
\code{match} over \code{Storage} is the only place the backend is named, and every arm lands in the same
|
||||||
|
\code{CpuStorage}. The \code{cpu\_to\_f32} stage is what makes an \code{f16} rank and a \code{bf16} rank
|
||||||
|
agree (\S\ref{sec:rootcause}). NCCL, by contrast, would keep the whole exchange on-device and refuse a
|
||||||
|
foreign-vendor peer.},label={lst:stage}]
|
||||||
|
fn sum_all_reduce(&self, xs: &Tensor) -> Result<Tensor> {
|
||||||
|
let storage = xs.storage_and_layout().0;
|
||||||
|
let host = match &*storage { // device -> host (any backend)
|
||||||
|
Storage::Cpu(s) => s.clone(),
|
||||||
|
Storage::Cuda(s) => s.to_cpu_storage()?,
|
||||||
|
Storage::Metal(s) => s.to_cpu_storage()?,
|
||||||
|
#[cfg(feature = "rocm")]
|
||||||
|
Storage::Rocm(s) => s.to_cpu_storage()?, // was missing: ring never built for AMD (Bug 2)
|
||||||
|
};
|
||||||
|
let wire = cpu_to_f32(&host)?; // CANONICAL wire dtype: f16|bf16|f32 -> f32
|
||||||
|
let sum = self.ring_exchange(&wire, xs.dims())?; // world_size-1 accumulating steps, sum in f32
|
||||||
|
Tensor::from_slice(&sum, xs.dims(), xs.device())? // host -> device
|
||||||
|
.to_dtype(xs.dtype()) // back to this rank's native compute dtype
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
This is not free. NCCL keeps the reduction on-device and rides NVLink; the host-staged ring pays two
|
||||||
|
extra device$\leftrightarrow$host copies per collective, a widening cast to \code{f32}, and then rides a
|
||||||
|
2.5GbE link whose measured goodput is $\approx$280~MB/s (\S\ref{sec:repro}). That is the correct trade for
|
||||||
|
the problem: we spend bandwidth to buy \emph{vendor-agnosticism}, because the alternative on a mixed fleet
|
||||||
|
is not a faster collective, it is \emph{no} collective. The ring is selected purely by configuration ---
|
||||||
|
\code{use\_ring()} is true when the \code{ring} feature is built and a \code{RING\_CONFIG} path is present
|
||||||
|
in the environment (\S\ref{sec:repro}) --- so the same engine binary is a single-node server, an NCCL
|
||||||
|
rank, or a socket-ring rank depending only on how it is launched.
|
||||||
|
|
||||||
|
\section{Compiles $\ne$ runs: an unexercised path was broken three ways}
|
||||||
|
\label{sec:compiles}
|
||||||
|
|
||||||
|
Here is the lesson that generalizes past this codebase. \emph{The ring path had been written, had type-
|
||||||
|
checked, had shipped in every build for months --- and had never once been run.} All multi-GPU testing
|
||||||
|
had gone through NCCL (the same-vendor path), so the socket ring was dead code that happened to compile.
|
||||||
|
The first time it was asked to carry a real tensor across a real vendor boundary, it failed three
|
||||||
|
separate ways, each a different species of the gap between ``the compiler accepted it'' and ``it does the
|
||||||
|
thing.'' All three are fixed on \code{main} (commit \code{63cc4c7ac}); the fourth, subtler failure ---
|
||||||
|
visible only once the generated tokens came out wrong --- is \S\ref{sec:rootcause}.
|
||||||
|
|
||||||
|
\paragraph{Bug 1 --- a \code{len}$^2$ byte count (a logic error the types could not catch).}
|
||||||
|
The wire code computed its transfer size as
|
||||||
|
\begin{center}
|
||||||
|
\code{let nbytes = x.len() * std::mem::size\_of\_val(x);}
|
||||||
|
\end{center}
|
||||||
|
This looks reasonable and is badly wrong. For a slice \code{x:\ \&[T]}, \code{size\_of\_val(x)} is
|
||||||
|
\emph{already} the size of the whole referent --- \code{x.len()}\,$\times$\,\code{size\_of::<T>()} --- so
|
||||||
|
multiplying by \code{x.len()} again yields \code{len}$^2$\,$\times$\,\code{elem\_size}, overshooting the
|
||||||
|
true byte count by a factor of \code{len}. The subsequent \code{from\_raw\_parts(ptr, nbytes)} then
|
||||||
|
fabricates a byte slice spanning far past the end of the allocation, and \code{write\_all} walks off the
|
||||||
|
buffer and hits \code{EFAULT}. It is invisible for a one-element tensor (\code{len}$^2 =$ \code{len}) and
|
||||||
|
fatal for every real activation. The fix is one token --- delete the \code{x.len()\ *} --- but the point
|
||||||
|
is that the type system was content the entire time: both operands are \code{usize}, the product is a
|
||||||
|
\code{usize}, and the meaning is nonsense. Only \emph{running} it with \code{len}$>$1 reveals it.
|
||||||
|
|
||||||
|
\paragraph{Bug 2 --- the ring never compiled for AMD (a hole hidden by a feature flag).}
|
||||||
|
The device$\to$host \code{match} in Listing~\ref{lst:stage} had arms for \code{Cpu}, \code{Cuda}, and
|
||||||
|
\code{Metal} but \emph{not} \code{Rocm}. Because \code{Storage::Rocm} only exists when the \code{rocm}
|
||||||
|
feature is enabled, and no one had ever built \code{ring} $+$ \code{rocm} together, the non-exhaustive
|
||||||
|
\code{match} was never forced into existence. The moment you try to build the one configuration a
|
||||||
|
heterogeneous ROCm$+$CUDA cluster needs, it fails to compile. The fix adds the two
|
||||||
|
\code{\#[cfg(feature = "rocm")]} arms (one in \code{sum\_all\_reduce}, one in \code{all\_gather}) that
|
||||||
|
stage a ROCm tensor to host exactly like the others. This is the subtler lesson: a feature-gated code
|
||||||
|
path is not merely untested, it may be \emph{unbuilt}, and a green CI that never enables that flag
|
||||||
|
combination proves nothing about it.
|
||||||
|
|
||||||
|
\paragraph{Bug 3 --- a handshake that assumed both nodes were the same speed.}
|
||||||
|
The ring's neighbour-connect loop gave up after 10 seconds. On a homogeneous cluster every rank boots in
|
||||||
|
lockstep and 10s is generous. On a \emph{heterogeneous} cluster the nodes do not load at the same rate ---
|
||||||
|
a Blackwell dev kit and an APU parse weights, warm kernels, and reach the barrier at visibly different
|
||||||
|
times --- so the faster node would time out waiting for the slower one and abort the whole ring. Raising
|
||||||
|
the timeout to 120s (and matching the panic message) lets asynchronous, uneven startup settle. The
|
||||||
|
assumption ``all ranks are interchangeable, so they arrive together'' is exactly the kind of invariant
|
||||||
|
that is silently true on one vendor and silently false across three.
|
||||||
|
|
||||||
|
None of these three is deep. That is the message. A distributed path can be well-typed, code-reviewed,
|
||||||
|
and shipped, and still be dead on arrival in three independent ways, because ``it compiles'' certifies
|
||||||
|
none of the things that actually matter at run time --- the arithmetic of a byte count, the existence of a
|
||||||
|
match arm under a flag nobody set, or a timing assumption baked into a constant. And a fourth bug, subtler
|
||||||
|
than all three and invisible until the generated tokens themselves came out wrong, waited past these; it
|
||||||
|
is the root cause of cross-vendor incoherence, and its fix is what turns prefill into coherent decode
|
||||||
|
(\S\ref{sec:rootcause}).
|
||||||
|
|
||||||
|
\section{Result: coherent decode across the AMD/NVIDIA boundary}
|
||||||
|
\label{sec:result}
|
||||||
|
|
||||||
|
With the four heterogeneity bugs fixed --- three mechanical (\S\ref{sec:compiles}), one numeric
|
||||||
|
(\S\ref{sec:rootcause}) --- the cross-vendor ring generates coherent output. On a two-node ring ---
|
||||||
|
\emph{evo} (AMD ROCm gfx1151) as rank~0, \emph{spark} (NVIDIA CUDA sm\_121) as rank~1 --- connected by a
|
||||||
|
2.5GbE direct cable, the sharded engine:
|
||||||
|
|
||||||
|
\begin{enumerate}[leftmargin=1.5em,itemsep=1pt]
|
||||||
|
\item \textbf{builds} with \code{ring} $+$ \code{rocm} on the AMD node and \code{ring} $+$ \code{cuda} on
|
||||||
|
the NVIDIA node (Bug~2 was the blocker);
|
||||||
|
\item \textbf{connects} the ring --- each rank binds its listen port, dials its right neighbour, and
|
||||||
|
accepts its left, the handshake surviving the uneven boot (Bug~3);
|
||||||
|
\item \textbf{shards} a Qwen3-0.6B at \code{world\_size}$=$2, each node holding half of every parallelized
|
||||||
|
weight matrix via the \code{ColumnParallelLayer}/\code{RowParallelLayer} split; and
|
||||||
|
\item \textbf{decodes coherently} --- running the host-staged \code{f32} \code{all\_reduce} across the
|
||||||
|
vendor boundary at every layer --- emitting a chain-of-thought and a correct final answer in lockstep, at
|
||||||
|
\textbf{50.4~tok/s decode} (TTFT 0.09s, 213~tok/s prefill).
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
The two ranks run \emph{different} 16-bit compute dtypes --- \emph{evo} in \code{f16}, \emph{spark} in
|
||||||
|
\code{bf16} (\S\ref{sec:rootcause}) --- and still agree, because the reduction crosses the wire in
|
||||||
|
canonical \code{f32}. To our knowledge this is the first reported cross-vendor (AMD$+$NVIDIA)
|
||||||
|
tensor-parallel LLM inference over commodity ethernet in a single engine: two different-vendor
|
||||||
|
accelerators jointly executing one tensor-parallel forward \emph{and} autoregressive decode, over a
|
||||||
|
commodity cable, with no shared-vendor collective (NCCL/RCCL) between them --- the \code{all\_reduce} that
|
||||||
|
both libraries refuse across the boundary runs to completion on plain TCP.
|
||||||
|
|
||||||
|
\paragraph{Controls isolate the wire, not the model.} A \emph{same}-vendor control --- both ranks forced
|
||||||
|
to \code{f16}, so the raw-bytes shortcut and the canonical-\code{f32} wire coincide --- produces the same
|
||||||
|
tokens as the cross-vendor run, which pins the dtype wire (\S\ref{sec:rootcause}), not the ring
|
||||||
|
mechanism, as the only thing heterogeneity changed. A \emph{single}-node control --- the same Qwen3-0.6B
|
||||||
|
on \emph{spark} alone --- generates the same answer at 168~tok/s decode and 464~tok/s prefill. So the
|
||||||
|
cross-vendor 50.4~tok/s is $\approx$3.3$\times$ slower than single-node decode, and the entire gap is
|
||||||
|
synchronization: a host-staged \code{all\_reduce} every layer, over the wire, is the only added work. The
|
||||||
|
model is coherent and the kernels are at parity; what the ring costs is the wire.
|
||||||
|
|
||||||
|
\section{Root cause and fix: the heterogeneous-dtype-on-the-wire hazard}
|
||||||
|
\label{sec:rootcause}
|
||||||
|
|
||||||
|
The fourth bug was invisible until the tokens themselves came out wrong, and it is the one worth dwelling
|
||||||
|
on, because it is the trap waiting in every host-staged cross-vendor collective. We state the cause, the
|
||||||
|
fix, and the residuals the fix leaves.
|
||||||
|
|
||||||
|
\subsection{The cause: two ranks legitimately pick different 16-bit formats}
|
||||||
|
\label{sec:dtype}
|
||||||
|
|
||||||
|
The engine picks its compute dtype \emph{per node} from the \emph{local} device. The selector
|
||||||
|
(\code{determine\_auto\_dtype\_all}) returns \code{f16} when it sees a ROCm gfx1151 device --- RDNA3.5 has
|
||||||
|
fast \code{f16} matrix cores (WMMA) but no fast \code{bf16} matmul --- and \code{bf16} for the CUDA
|
||||||
|
Blackwell path. So on a mixed ring the two ranks legitimately and independently choose \emph{different}
|
||||||
|
16-bit formats: \emph{evo} runs the model in \code{f16}, \emph{spark} in \code{bf16}. The original
|
||||||
|
host-staged ring shipped \emph{raw device-dtype bytes} and the receiver reinterpreted them as \emph{its
|
||||||
|
own} local dtype. Since \code{f16} and \code{bf16} share a 16-bit container but split it differently ---
|
||||||
|
5-bit exponent / 10-bit mantissa versus 8-bit exponent / 7-bit mantissa --- every value crossing the
|
||||||
|
boundary was silently re-read with the wrong exponent/mantissa layout. The bit pattern was intact; its
|
||||||
|
\emph{interpretation} was wrong, which is worse, because nothing errored: the \code{all\_reduce} summed
|
||||||
|
correctly-typed nonsense into correctly-typed nonsense, corrupting activations from the first layer.
|
||||||
|
Notably, a \emph{same}-vendor ring never exposed this --- both ranks pick the same dtype and the raw-bytes
|
||||||
|
shortcut accidentally works --- which is precisely why the hazard survived months of same-vendor NCCL and
|
||||||
|
ring testing until the first cross-vendor run, and why the same-vendor control (\S\ref{sec:result}) is the
|
||||||
|
clean isolator.
|
||||||
|
|
||||||
|
\subsection{The fix: a canonical \code{f32} wire and an \code{N}-node accumulating reduce}
|
||||||
|
\label{sec:fix}
|
||||||
|
|
||||||
|
The remedy is structural, not a patch: the wire needs a \emph{canonical} dtype both ranks agree on
|
||||||
|
independent of their local compute dtype. \code{f32} is the natural choice --- a lossless superset of both
|
||||||
|
\code{f16} and \code{bf16}, and higher-precision accumulation for the reduction besides. The shipped
|
||||||
|
reduce (Listing~\ref{lst:stage}) stages each device tensor to host, converts to \code{f32}
|
||||||
|
(\code{cpu\_to\_f32}), exchanges and sums in \code{f32}, and converts the result back to the receiver's
|
||||||
|
native device dtype. The 16-bit formats now meet only \emph{inside} each node; the wire is always
|
||||||
|
\code{f32}, so both ranks reduce the same values bit-for-bit regardless of vendor, and the reduction is
|
||||||
|
carried out in the CPU-\code{f32} path with a canonical tie-break so a ROCm box and a CUDA box do not
|
||||||
|
diverge on rounding order.
|
||||||
|
|
||||||
|
The same pass closed a latent correctness bound. The original \code{sum\_all\_reduce} performed a
|
||||||
|
\emph{single} write-right / read-left exchange and summed the neighbour's shard --- a complete all-reduce
|
||||||
|
for two ranks, but for \code{N}$>$2 rank $i$ only ever incorporates rank $i{-}1$, so ranks $i{-}2,
|
||||||
|
i{-}3,\dots$ never reach it. The fix replaces the single exchange with the textbook \code{world\_size}$-$1
|
||||||
|
accumulating ring (each rank forwards and sums its running shard for \code{N}$-$1 steps), correct for any
|
||||||
|
\code{N}$\geq$2, and removes a spurious power-of-two world-size restriction inherited from the NCCL path
|
||||||
|
(\code{is\_power\_of\_two}) that had made four the smallest cluster past two nodes the ring would even
|
||||||
|
admit. The all-reduce is now algorithmically correct for any \code{N}$\geq$2; the \emph{measured}
|
||||||
|
cross-vendor demonstration in this paper is the two-node \emph{evo}$+$\emph{spark} ring, and multi-node
|
||||||
|
(\code{N}$>$2, e.g.\ adding \emph{dbc}/Metal) cross-vendor runs are the natural next validation, not a
|
||||||
|
claimed measurement here.
|
||||||
|
|
||||||
|
\subsection{Residuals: three problems the fix leaves open}
|
||||||
|
\label{sec:residual}
|
||||||
|
|
||||||
|
Coherent cross-vendor decode holds, but three residuals bound the result. We state each crisply and
|
||||||
|
develop all three, alongside five further problems, as a research agenda in the companion~\cite{openproblems}.
|
||||||
|
|
||||||
|
\paragraph{Sampling divergence (a determinism boundary).} The \emph{reduced activations} now agree
|
||||||
|
bit-for-bit, but each rank still computes its final logits on its own device in its own 16-bit dtype and
|
||||||
|
samples \emph{locally}. In the demonstrated runs the argmax stays in lockstep; a near-argmax-tie could in
|
||||||
|
principle resolve differently under \code{f16} versus \code{bf16} rounding and desync the per-rank KV
|
||||||
|
caches. The robust cure --- the head rank samples and broadcasts the token id, so every rank advances on
|
||||||
|
the same token --- removes the divergence by construction; it is a determinism-boundary problem in
|
||||||
|
replicated-state distributed inference, not a numeric one.
|
||||||
|
|
||||||
|
\paragraph{Graphless lockstep (an execution-symmetry assumption).} The two ranks run \emph{two}
|
||||||
|
generation loops kept in phase by the per-token \code{all\_reduce} barrier. To stay in phase the CUDA rank
|
||||||
|
must run \emph{graphless}, matching the graphless ROCm rank and forgoing the $\approx$1.5$\times$ decode
|
||||||
|
speedup CUDA-graph capture would give~\cite{backend}. Decode is coherent this way, but per-token
|
||||||
|
cross-node lockstep between nodes that legitimately want different execution strategies is fragile --- an
|
||||||
|
argument for pipeline parallelism (\S\ref{sec:pipeline}), where each node's execution is a purely local
|
||||||
|
choice, rather than a bug to patch.
|
||||||
|
|
||||||
|
\paragraph{Format coupling (a sharding limitation).} The TP collectives are wired through the
|
||||||
|
non-quantized (safetensors) model path --- \code{ColumnParallelLayer} and \code{RowParallelLayer} live in
|
||||||
|
the normal model definitions --- and \emph{not} through the GGUF quantized path. So even with the
|
||||||
|
numerics correct, TP cannot yet shard the very block-quantized formats that make edge inference fit in
|
||||||
|
commodity memory. Quant-aware sharding (splitting on superblock boundaries without a dequant--requant
|
||||||
|
loss) is an open problem~\cite{openproblems}; pipeline parallelism sidesteps it (\S\ref{sec:pipeline})
|
||||||
|
because activations, not weights, cross the seam.
|
||||||
|
|
||||||
|
\section{The case for pipeline parallelism}
|
||||||
|
\label{sec:pipeline}
|
||||||
|
|
||||||
|
The dtype hazard and the \code{N}-node reduce are fixed, so the tensor-parallel ring on \code{main} is now
|
||||||
|
\emph{correct}. But correct is not the same as well-matched, and the residuals of \S\ref{sec:residual}
|
||||||
|
together with the measured synchronization tax point the same way. Tensor parallelism was built for a
|
||||||
|
homogeneous, tightly-coupled, high-bandwidth, single-vendor machine, and every place it strains on our
|
||||||
|
cluster is a place it assumes that machine: a per-layer all-reduce assumes cheap bandwidth
|
||||||
|
(\S\ref{sec:design}) --- the measured $\approx$3.3$\times$ decode tax is exactly this bill; per-token
|
||||||
|
lockstep assumes symmetric execution (\S\ref{sec:residual}); the parallel layers assume dequantized
|
||||||
|
weights (\S\ref{sec:residual}). Fixing the numerics made the primitive correct; it did not make it the
|
||||||
|
right primitive for a heterogeneous fleet.
|
||||||
|
|
||||||
|
\textbf{Pipeline parallelism} inverts the mismatch. Partition the model into contiguous \emph{layer
|
||||||
|
ranges}, one range per node; a token's hidden state flows through node~0's layers, crosses the wire once
|
||||||
|
to node~1, flows through its layers, and so on. The consequences line up against the strains almost
|
||||||
|
one-to-one:
|
||||||
|
|
||||||
|
\begin{itemize}[leftmargin=1.4em,itemsep=2pt]
|
||||||
|
\item \textbf{Activations-only on the wire.} What crosses a node boundary is a single hidden-state tensor,
|
||||||
|
once per boundary, forward --- not an \code{all\_reduce} of every layer's output twice per block. On a
|
||||||
|
$\approx$280~MB/s link that is the difference between a per-token tax of \code{O(1)} small transfers and
|
||||||
|
\code{O(layers)} full-width collectives; it is the same $\approx$3.3$\times$ tax converted from
|
||||||
|
per-layer to per-node.
|
||||||
|
\item \textbf{One canonical cast, not a per-op reinterpretation.} The activation is a single tensor at
|
||||||
|
each boundary, so agreeing on one wire dtype (\S\ref{sec:dtype}) is one cast at one seam per node, not a
|
||||||
|
property every collective must carry. The heterogeneous-dtype hazard shrinks from pervasive to pointwise.
|
||||||
|
\item \textbf{Format-agnostic.} Activations are just floats; the boundary does not know or care whether the
|
||||||
|
node that produced them ran a safetensors or a GGUF model, on which vendor, with which kernel. Pipeline
|
||||||
|
parallelism sidesteps the format coupling of \S\ref{sec:residual} for free, so a mixed cluster can run
|
||||||
|
quantized shards.
|
||||||
|
\item \textbf{One generation loop, stateless workers.} The head node runs the single sampling/generation
|
||||||
|
loop; the others are stateless layer-executors that receive an activation, run their layers (holding only
|
||||||
|
their own KV cache), and forward. There is no second generation loop to keep in phase and no cross-node
|
||||||
|
per-token lockstep, so the CUDA-graph asymmetry (\S\ref{sec:residual}) becomes a purely \emph{local}
|
||||||
|
choice --- each node may capture graphs for its own layers or not, and nobody desynchronizes --- and the
|
||||||
|
sampling divergence collapses to the head's single sampler.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
Pipeline parallelism is not a free lunch --- for a single decode stream it adds pipeline-bubble latency
|
||||||
|
proportional to the number of stages, and throughput needs micro-batching to fill the
|
||||||
|
pipe~\cite{gpipe}. Its wide-area latency budget --- one network hop per stage on the critical path of
|
||||||
|
every token --- is the subject of the companion latency analysis~\cite{latency}, which shows the budget
|
||||||
|
is spendable precisely because pipelining overlaps transport under compute and speculative decoding
|
||||||
|
amortizes the round-trip. But its \emph{coupling} to the cluster is minimal and its assumptions are the
|
||||||
|
ones a heterogeneous fleet can actually honour. The right primitive for one-model-across-many-vendors is
|
||||||
|
the one that puts the least on the wire and demands the least agreement between ranks, and on both axes
|
||||||
|
that is pipeline, not tensor, parallelism. The cross-vendor decode result (\S\ref{sec:result}) proves the
|
||||||
|
\emph{channel} works across the boundary; the argument here is about which \emph{traffic pattern} to run
|
||||||
|
over it, and it is under construction in the same codebase.
|
||||||
|
|
||||||
|
\section{Reproducibility}
|
||||||
|
\label{sec:repro}
|
||||||
|
|
||||||
|
\paragraph{Hardware and link.} \emph{evo}: AMD Ryzen AI Max+ 395 ``Strix Halo,'' Radeon 8060S iGPU
|
||||||
|
(gfx1151, RDNA3.5), ROCm. \emph{spark}: NVIDIA GB10 Grace-Blackwell (sm\_121), CUDA. \emph{dbc}: Apple
|
||||||
|
M4 Max, Metal. \emph{evo}$\leftrightarrow$\emph{spark} is a direct 2.5GbE cable (static
|
||||||
|
\code{192.168.77.0/24}), whose measured sustained-TCP goodput is $\approx$280~MB/s ($\approx$2.24~Gbit/s);
|
||||||
|
its wide-area latency budget is analysed in the companion~\cite{latency}. The three-node substrate is a
|
||||||
|
wired ethernet mesh with \emph{spark} kernel-IP-forwarding the closing \emph{evo}$\leftrightarrow$\emph{dbc}
|
||||||
|
edge, so no switch or router is required.
|
||||||
|
|
||||||
|
\paragraph{Build.} Each node builds the \emph{same} engine source with its own backend feature plus
|
||||||
|
\code{ring}: \code{--features "cuda ring"} on \emph{spark}, \code{--features "rocm ring"} on \emph{evo}.
|
||||||
|
The three mechanical fixes are at commit \code{63cc4c7ac}; the canonical-\code{f32} wire and the
|
||||||
|
\code{world\_size}$-$1 reduce are on \code{main} (\code{hanzo-quant/src/distributed/},
|
||||||
|
\code{hanzo-engine/src/ops.rs}).
|
||||||
|
|
||||||
|
\paragraph{Ring configuration.} The ring backend is activated by pointing \code{RING\_CONFIG} at a JSON
|
||||||
|
file per node; rank $r$ binds \code{port} and dials its right neighbour at
|
||||||
|
\code{right\_ip:right\_port}. A two-node \emph{evo}(rank~0)$+$\emph{spark}(rank~1) ring:
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=Rust,caption={\code{RING\_CONFIG} for a 2-node cross-vendor ring. Rank~0
|
||||||
|
(evo) listens on 9000 and dials spark; rank~1 (spark) listens on 9001 and dials back to evo, closing the
|
||||||
|
ring.},label={lst:ring}]
|
||||||
|
// evo (rank 0, 192.168.77.1): RING_CONFIG=/etc/hanzo/ring0.json
|
||||||
|
{ "master_ip": null, "master_port": 9002,
|
||||||
|
"port": 9000, "right_ip": "192.168.77.2", "right_port": 9001,
|
||||||
|
"rank": 0, "world_size": 2 }
|
||||||
|
|
||||||
|
// spark (rank 1, 192.168.77.2): RING_CONFIG=/etc/hanzo/ring1.json
|
||||||
|
{ "master_ip": "192.168.77.1", "master_port": 9002,
|
||||||
|
"port": 9001, "right_ip": "192.168.77.1", "right_port": 9000,
|
||||||
|
"rank": 1, "world_size": 2 }
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\noindent A \code{hanzo cluster init} / \code{hanzo cluster join <token>} pair wraps this JSON in a
|
||||||
|
scan-to-join token (base58 of the topology plus a terminal QR code) so the operator never hand-writes the
|
||||||
|
ports; the token bakes in the peer list and each worker self-assigns its rank. Both ranks launch the
|
||||||
|
ordinary \code{hanzo serve} with \code{RING\_CONFIG} set and the same model.
|
||||||
|
|
||||||
|
\paragraph{What is measured vs.\ open.} Measured on the real 2.5GbE link: cross-vendor build, connect,
|
||||||
|
\code{world\_size}$=$2 shard, and coherent multi-token \emph{decode} across the AMD/NVIDIA boundary
|
||||||
|
(50.4~tok/s decode, TTFT 0.09s, 213~tok/s prefill on Qwen3-0.6B); the same-vendor \code{f16}$+$\code{f16}
|
||||||
|
control matching token-for-token; and the single-node \emph{spark} control (168~tok/s decode, 464~tok/s
|
||||||
|
prefill). Open (\S\ref{sec:residual}, \cite{openproblems}): head-broadcast sampling to close the
|
||||||
|
determinism boundary, \code{N}$>$2 cross-vendor runs, GGUF-path TP, and the migration to pipeline
|
||||||
|
parallelism. No projected or synthetic number appears in this paper.
|
||||||
|
|
||||||
|
\section{Related work and contribution}
|
||||||
|
\label{sec:related}
|
||||||
|
|
||||||
|
Tensor parallelism is Megatron-LM~\cite{megatron}; pipeline parallelism is GPipe and its
|
||||||
|
successors~\cite{gpipe}; the on-device collectives are NCCL~\cite{nccl} and RCCL~\cite{rccl}, each
|
||||||
|
single-vendor by design. Distributed inference over commodity/heterogeneous networks has been explored
|
||||||
|
most prominently by Petals~\cite{petals}, which runs pipeline-parallel shards across volunteer nodes over
|
||||||
|
the public internet --- close in spirit to \S\ref{sec:pipeline}, and evidence that the activations-only
|
||||||
|
pattern is the one that survives a slow, loose network. What we add is narrow and, to our knowledge, not
|
||||||
|
previously shown: a \emph{single} inference engine, one source lowered to three vendors' kernels, running
|
||||||
|
a \emph{tensor-parallel} forward \emph{and coherent decode} across an \emph{AMD$+$NVIDIA} boundary on
|
||||||
|
\emph{commodity ethernet} with \emph{no} shared-vendor collective --- and an unusually explicit account of
|
||||||
|
the failure modes that a host-staged cross-vendor collective exposes (the \code{len}$^2$ overflow, the
|
||||||
|
unbuilt ROCm arm, the startup-race timeout, and the heterogeneous-dtype wire). The design contribution is
|
||||||
|
the observation, made concrete by those failures, that heterogeneity is a stress test for homogeneity
|
||||||
|
assumptions, and that pipeline parallelism minimizes exactly the assumptions that break. This paper is the
|
||||||
|
distributed extension of the ``one source, every backend'' thesis~\cite{backend,umbrella}: having made one
|
||||||
|
engine run on every backend, we make those backends run in one cluster. Two companions extend the arc: the
|
||||||
|
latency-physics analysis~\cite{latency} carries the wide-area budget for \S\ref{sec:pipeline}, and the
|
||||||
|
three-plane compute-fabric blueprint~\cite{consensus} turns the trusted-LAN ring into a permissionless,
|
||||||
|
consensus-managed, PQ-authenticated network. The open questions this result raises are collected as a
|
||||||
|
research agenda in the open-problems companion~\cite{openproblems}.
|
||||||
|
|
||||||
|
\section{Conclusion}
|
||||||
|
\label{sec:conclusion}
|
||||||
|
|
||||||
|
No collective library crosses a vendor boundary, so a mixed AMD/NVIDIA/Apple fleet cannot run one model
|
||||||
|
together through the standard path. Host-staging the collective through CPU memory and TCP makes the
|
||||||
|
channel vendor-agnostic, and on that channel one engine --- built from a single source for three vendors'
|
||||||
|
kernels --- shards a model \code{world\_size}$=$2 across an AMD/NVIDIA boundary over a 2.5GbE cable and
|
||||||
|
\emph{decodes coherently} at 50.4~tok/s, which we believe is a first. The road to that result ran through
|
||||||
|
three bugs in a path that compiled for months but had never run --- a reminder that ``it builds''
|
||||||
|
certifies almost nothing about a distributed system --- and one deeper hazard, a heterogeneous-dtype wire
|
||||||
|
that a same-vendor cluster can never expose, cured by a canonical \code{f32} payload and an accumulating
|
||||||
|
\code{world\_size}$-$1 all-reduce correct for any \code{N}$\geq$2. The measured $\approx$3.3$\times$
|
||||||
|
synchronization tax, and the residuals that remain --- local sampling that can diverge at a near-tie,
|
||||||
|
graphless lockstep between two generation loops, and TP's coupling to the dequantized weight path --- all
|
||||||
|
point one way: toward pipeline parallelism, whose activations-only, one-generation-loop, format-agnostic
|
||||||
|
shape asks of a heterogeneous cluster only what such a cluster can actually give. The channel is open
|
||||||
|
across the boundary and coherent over it; the companions drive the right traffic~\cite{latency}, harden
|
||||||
|
it into a network~\cite{consensus}, and enumerate what is left to solve~\cite{openproblems}.
|
||||||
|
|
||||||
|
\begin{thebibliography}{9}
|
||||||
|
\small
|
||||||
|
\bibitem{backend} Hanzo AI. \emph{Edge LLM Inference Across Commodity Accelerators: Measured AMD, NVIDIA,
|
||||||
|
and Apple Performance, and hanzo-engine at llama.cpp Decode Parity on Every Backend.} Paper 05.
|
||||||
|
\bibitem{umbrella} Hanzo AI. \emph{One Source, Every Backend: The hanzo-kernel DSL --- Thesis and Guided
|
||||||
|
Tour.} Paper 07 (umbrella).
|
||||||
|
\bibitem{fuse} Hanzo AI. \emph{Fusion Is Composition: Kernel Fusion as the Functor Law.} Paper 07.1.
|
||||||
|
\bibitem{oracle} Hanzo AI. \emph{One Oracle, Every Backend: The CPU Bit-Exactness Law.} Paper 07.4.
|
||||||
|
\bibitem{latency} Hanzo AI --- ML Systems. \emph{The Latency Physics of Geo-Distributed Autoregressive
|
||||||
|
Decode.} Distributed-inference companion (analytical).
|
||||||
|
\bibitem{consensus} Hanzo AI --- ML Systems. \emph{The Three-Plane Compute Fabric: Consensus-Managed,
|
||||||
|
ZAP-Transported Decentralized AI Inference.} Distributed-inference companion (architecture).
|
||||||
|
\bibitem{openproblems} Hanzo AI --- ML Systems. \emph{Open Problems in Heterogeneous Distributed
|
||||||
|
Inference.} Distributed-inference companion (research agenda).
|
||||||
|
\bibitem{megatron} M.\ Shoeybi, M.\ Patwary, R.\ Puri, P.\ LeGresley, J.\ Casper, B.\ Catanzaro.
|
||||||
|
\emph{Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism.} arXiv:1909.08053, 2019.
|
||||||
|
\bibitem{gpipe} Y.\ Huang et al. \emph{GPipe: Efficient Training of Giant Neural Networks using Pipeline
|
||||||
|
Parallelism.} NeurIPS, 2019.
|
||||||
|
\bibitem{petals} A.\ Borzunov et al. \emph{Petals: Collaborative Inference and Fine-tuning of Large
|
||||||
|
Models.} arXiv:2209.01188, 2022.
|
||||||
|
\bibitem{nccl} NVIDIA. \emph{NCCL: NVIDIA Collective Communications Library.}
|
||||||
|
\bibitem{rccl} AMD. \emph{RCCL: ROCm Communication Collectives Library.}
|
||||||
|
\end{thebibliography}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
Reference in New Issue
Block a user