GPU P2P On vs Off: Tensor Parallel LLM Serving on Four R9700s
My four AMD Radeon AI PRO R9700s live in the box with six GPUs in one x16 slot, where they serve Qwen3.8-27B split across all four with tensor parallelism. Peer-to-peer (P2P) lets the cards read and write each other’s memory without a trip through the CPU. On the R9700s it needs a 32 GB BAR on each card, the window the CPU uses to reach the card’s memory. Getting those behind the switch took a kernel module, and the BARs post covers it.
A 1 GB copy between two R9700s ran at 14.0 GB/s with P2P on. To see what P2P does for serving, I ran the same benchmark three times: P2P on, P2P off, and P2P on again to check the first run.
With P2P off, prefill dropped 45 to 55%, decode about 30%, and throughput with 16 clients about 53%. Median time to first token under load more than tripled.
Tensor parallelism and all-reduce ๐
Tensor parallelism splits each layer’s weights across the cards, so every card computes part of every layer for every token. Before the next layer can start, every card needs the sum of those partial results. That’s an all-reduce: each card sends its piece and gets the total back. In Megatron-LM’s layout there are two per layer, one after attention and one after the MLP.
With P2P on, the R9700s swap those partial sums directly through the switch. With P2P off, the sums go up to pinned host memory and back.
Running radiance with P2P on and off ๐
The engine is radiance 1.4.0 serving Qwen3.8-27B in MXFP4 at tensor parallel 4 (TP4), with each card capped at 250 W. These flags stayed the same across the three runs, apart from the last line.
--max-num-seqs 16
--max-model-len 262144
--kv-cache-dtype fp8
--tp-wire wht6
--tp-wire-min-kb 128
--p2p off # the middle run only
With --p2p off, radiance’s startup log says all-reduces go through host memory. Its graph debug output
also shows host all-reduce kernels replacing the peer ones, so turning P2P off changes both the code
and the route.
Results: P2P off vs on ๐
| Measurement | P2P on (a) | P2P off | P2P on (b) |
|---|---|---|---|
| Prefill tok/s, 2k prompt | 4,562 | 2,069 | 4,579 |
| Prefill tok/s, 32k prompt | 4,674 | 2,142 | 4,690 |
| Prefill tok/s, 128k prompt | 3,973 | 2,001 | 3,969 |
| Prefill tok/s, 234k prompt (233,525 tokens) | 3,340 | 1,834 | 3,337 |
| Decode tok/s, 2k context | 293.3 | 207.5 | 304.2 |
| Decode tok/s, 234k context | 253.0 | 167.9 | 228.9 |
| Total output tok/s, 1 client | 151.1 | 100.5 | 154.3 |
| Total output tok/s, 16 clients | 601.1 | 285.7 | 608.1 |
| Median time to first token, 16 clients | 2.18 s | 7.93 s | 2.38 s |
| Longest time to first token, 16 clients | 3.49 s | 10.06 s | 3.48 s |
| GSM8K accuracy, 250 questions | 98.8% | 99.2% | 98.8% |
| Output tokens per joule, 4 cards, 16 clients | 0.61 | 0.39 | 0.62 |
| Average power, 4 cards, 16 clients | 985 W | 739 W | 988 W |
The chart shows what was left, so the 2k prompt at 45% is a 55% drop. Short prompts lost the most prefill: 55% at 2k against 45% at 234k. The 233,525-token prompt waited 127.31 seconds for its first token with P2P off and 69.92 with it on. GSM8K, 250 grade-school math problems, came out one question apart and took 80% longer.
The two P2P-on runs were within 4% of each other on throughput, except long-context decode at about 10%, and median time to first token was 9% apart.
With P2P off the four cards drew 739 W instead of about 985 W, but throughput fell further, so GPU energy per output token rose about 59%. That counts only the power the four cards report.
Copy test vs all-reduce ๐
The copy test moved one big buffer between one pair of cards. An all-reduce on four cards has every card sending and receiving at once.
With all four R9700s copying around a ring at once, the switch moved 54.7 GB/s. The host path carries about 28 GB/s each way over the x16 uplink to the CPU. All four cards share it, so they get about half that between them.
Checking P2P on your build ๐
- Benchmark your serving workload with P2P on and off, in addition to a copy test.
- Run P2P on twice, with short and long prompts, and one client and many.
- Check whether card-to-card traffic stays on a switch or goes through the CPU.
- Check that your driver supports P2P on your cards before you buy hardware for it.
I had help with this one. Claude ran the benchmarks on the box over SSH and helped me draft this post. I did the physical work, read all the words, checked the numbers, and rewrote anything that sounded like a chatbot, so the mistakes are mine.
Sources ๐
- Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism, two all-reduces per transformer layer, after attention and after the MLP
- radiance, the inference engine on the R9700s