Skip to content

Commit 08d2f68

Browse files
committed
Remove correction note, rename NCCL section, merge 3 bar charts into one combined chart
1 parent a524799 commit 08d2f68

1 file changed

Lines changed: 58 additions & 105 deletions

File tree

_posts/2026-02-15-IB vs Ethernet Fine-Tuning at Scale.md

Lines changed: 58 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ In this post, I benchmark InfiniBand (RDMA) against genuine Ethernet (TCP socket
2020

2121
**The results are dramatic.** InfiniBand delivers **26–28× higher multi-node throughput** than Ethernet for the 7B dense model, **38–45× higher** for the 72B dense model, and **56–57× higher** for the Mixtral 8x7B MoE model. The MoE architecture — with its expert routing and 46.7B total parameters sharded across nodes — is the most communication-intensive of the three, making InfiniBand even more critical.
2222

23-
> **Correction:** An earlier version of this post reported near-identical IB and Ethernet performance using `NCCL_IB_DISABLE=1`. That environment variable [does not work](#the-nccl_ib_disable-bug) with NCCL 2.23.4 on Azure ND-series VMs — the RDMA network plugin ignores it entirely. All previous "Ethernet" results were actually running on InfiniBand. This update uses `NCCL_NET=Socket` to force genuine TCP sockets, [verified via nccl-tests](#the-nccl_ib_disable-bug).
24-
2523
## Test Environment
2624

2725
| Component | Detail |
@@ -37,9 +35,9 @@ In this post, I benchmark InfiniBand (RDMA) against genuine Ethernet (TCP socket
3735
| **Models** | Qwen2.5-7B (~14 GB), Qwen2.5-72B (~136 GB), Mixtral-8x7B-v0.1 (~87 GB) in bf16 |
3836
| **Framework** | PyTorch FSDP (`size_based_auto_wrap_policy` for dense, `transformer_auto_wrap_policy` for MoE) |
3937

40-
### The `NCCL_IB_DISABLE` Bug
38+
### Why `NCCL_IB_DISABLE` Doesn't Work Here
4139

42-
The standard way to disable InfiniBand in NCCL is `NCCL_IB_DISABLE=1`. This is documented in NCCL's official docs, used in Azure tutorials, and appears in countless blog posts and Stack Overflow answers. But on Azure ND-series H100 VMs, **it doesn't work.**
40+
The standard way to disable InfiniBand in NCCL is `NCCL_IB_DISABLE=1`. This is documented in NCCL's official docs, used in Azure tutorials, and appears in countless blog posts and Stack Overflow answers. However, on Azure ND-series H100 VMs, **this flag has no effect.**
4341

4442
These VMs use an **external RDMA network plugin** (`libnccl-net.so`) that registers with NCCL as a transport provider. NCCL 2.23.4 routes all inter-node traffic through this plugin regardless of the `NCCL_IB_DISABLE` setting — the flag only controls NCCL's *built-in* IB transport, not external plugins.
4543

@@ -58,7 +56,7 @@ I discovered this when my initial "Ethernet" benchmarks produced suspiciously id
5856

5957
Both `NCCL_IB_DISABLE=0` and `NCCL_IB_DISABLE=1` produce identical ~78 GB/s per GPU — they're both running on RDMA. The environment variable is simply ignored when the external network plugin is loaded.
6058

61-
**The fix:** `NCCL_NET=Socket` overrides all network plugins and forces NCCL to use the kernel's TCP stack. The measured bandwidth gap: **392 GB/s (RDMA) vs. 3.19 GB/s (TCP) — a 122× difference.**
59+
**The solution:** `NCCL_NET=Socket` overrides all network plugins and forces NCCL to use the kernel's TCP stack. The measured bandwidth gap: **392 GB/s (RDMA) vs. 3.19 GB/s (TCP) — a 122× difference.**
6260

6361
### How the Two Modes Actually Differ
6462

@@ -90,55 +88,90 @@ The full scripts are in the [Reproducing These Results](#reproducing-these-resul
9088

9189
## Results
9290

93-
### Qwen2.5-7B (batch_size=2, seq_len=2048)
94-
95-
| Nodes | GPUs | IB (tok/s) | ETH (tok/s) | IB / ETH |
96-
|-------|------|-----------|------------|----------|
97-
| 1 | 8 | 65,274 | 65,230 | 1.0× |
98-
| 2 | 16 | 131,018 | 4,968 | **26.4×** |
99-
| 4 | 32 | 262,201 | 9,439 | **27.8×** |
100-
| 8 | 64 | 502,991 | 18,710 | **26.9×** |
101-
102-
<canvas id="chart7bThroughput" width="700" height="400"></canvas>
91+
<canvas id="chartAllThroughput" width="700" height="450"></canvas>
10392
<script>
104-
new Chart(document.getElementById('chart7bThroughput'), {
93+
new Chart(document.getElementById('chartAllThroughput'), {
10594
type: 'bar',
10695
data: {
10796
labels: ['1 node\n(8 GPU)', '2 nodes\n(16 GPU)', '4 nodes\n(32 GPU)', '8 nodes\n(64 GPU)'],
10897
datasets: [
10998
{
110-
label: 'InfiniBand (RDMA)',
99+
label: '7B — IB',
111100
data: [65274, 131018, 262201, 502991],
112-
backgroundColor: 'rgba(54, 162, 235, 0.8)',
101+
backgroundColor: 'rgba(54, 162, 235, 0.85)',
113102
borderColor: 'rgba(54, 162, 235, 1)',
114103
borderWidth: 1
115104
},
116105
{
117-
label: 'Ethernet (TCP)',
106+
label: '7B — ETH',
118107
data: [65230, 4968, 9439, 18710],
119-
backgroundColor: 'rgba(255, 99, 132, 0.8)',
120-
borderColor: 'rgba(255, 99, 132, 1)',
108+
backgroundColor: 'rgba(54, 162, 235, 0.3)',
109+
borderColor: 'rgba(54, 162, 235, 0.8)',
110+
borderWidth: 1
111+
},
112+
{
113+
label: '72B — IB',
114+
data: [null, 9127, 17604, 30175],
115+
backgroundColor: 'rgba(153, 102, 255, 0.85)',
116+
borderColor: 'rgba(153, 102, 255, 1)',
117+
borderWidth: 1
118+
},
119+
{
120+
label: '72B — ETH',
121+
data: [null, 203, 402, 784],
122+
backgroundColor: 'rgba(153, 102, 255, 0.3)',
123+
borderColor: 'rgba(153, 102, 255, 0.8)',
124+
borderWidth: 1
125+
},
126+
{
127+
label: 'MoE 8x7B — IB',
128+
data: [11577, 22520, 44239, 83583],
129+
backgroundColor: 'rgba(75, 192, 192, 0.85)',
130+
borderColor: 'rgba(75, 192, 192, 1)',
131+
borderWidth: 1
132+
},
133+
{
134+
label: 'MoE 8x7B — ETH',
135+
data: [11634, 398, 774, 1501],
136+
backgroundColor: 'rgba(75, 192, 192, 0.3)',
137+
borderColor: 'rgba(75, 192, 192, 0.8)',
121138
borderWidth: 1
122139
}
123140
]
124141
},
125142
options: {
126143
responsive: true,
127144
plugins: {
128-
title: { display: true, text: 'Qwen2.5-7B: Aggregate Throughput', font: { size: 16 } },
129-
legend: { position: 'top' }
145+
title: { display: true, text: 'Aggregate Throughput: All Models (Log Scale)', font: { size: 16 } },
146+
legend: { position: 'top' },
147+
tooltip: {
148+
callbacks: {
149+
label: function(ctx) { return ctx.dataset.label + ': ' + ctx.parsed.y.toLocaleString() + ' tok/s'; }
150+
}
151+
}
130152
},
131153
scales: {
132154
y: {
133-
beginAtZero: true,
134-
title: { display: true, text: 'Tokens/sec' },
155+
type: 'logarithmic',
156+
title: { display: true, text: 'Tokens/sec (log scale)' },
157+
min: 100,
158+
max: 600000,
135159
ticks: { callback: function(v) { return v.toLocaleString(); } }
136160
}
137161
}
138162
}
139163
});
140164
</script>
141165

166+
### Qwen2.5-7B (batch_size=2, seq_len=2048)
167+
168+
| Nodes | GPUs | IB (tok/s) | ETH (tok/s) | IB / ETH |
169+
|-------|------|-----------|------------|----------|
170+
| 1 | 8 | 65,274 | 65,230 | 1.0× |
171+
| 2 | 16 | 131,018 | 4,968 | **26.4×** |
172+
| 4 | 32 | 262,201 | 9,439 | **27.8×** |
173+
| 8 | 64 | 502,991 | 18,710 | **26.9×** |
174+
142175
**The single-node baseline is identical** — 65,274 (IB) vs. 65,230 (ETH) tok/s. No communication crosses the inter-node link, so the interconnect is irrelevant. This confirms the test is fair.
143176

144177
**At 2+ nodes, InfiniBand is 26–28× faster.** The gap appears immediately at 2 nodes and remains remarkably consistent as the cluster scales. InfiniBand shows near-perfect linear scaling — 65K → 131K → 262K → 503K tok/s — while Ethernet throughput is strangled by inter-node communication. The Ethernet per-GPU efficiency drops from 8,154 tok/s (1-node) to just 292–311 tok/s (multi-node): a **26× collapse** in per-GPU utilization the moment FSDP must communicate across the network.
@@ -153,46 +186,6 @@ The 72B model requires FSDP sharding across at least 2 nodes (16 GPUs) — a sin
153186
| 4 | 32 | 17,604 | 402 | **43.8×** |
154187
| 8 | 64 | 30,175 | 784 | **38.5×** |
155188

156-
<canvas id="chart72bThroughput" width="700" height="400"></canvas>
157-
<script>
158-
new Chart(document.getElementById('chart72bThroughput'), {
159-
type: 'bar',
160-
data: {
161-
labels: ['2 nodes\n(16 GPU)', '4 nodes\n(32 GPU)', '8 nodes\n(64 GPU)'],
162-
datasets: [
163-
{
164-
label: 'InfiniBand (RDMA)',
165-
data: [9127, 17604, 30175],
166-
backgroundColor: 'rgba(54, 162, 235, 0.8)',
167-
borderColor: 'rgba(54, 162, 235, 1)',
168-
borderWidth: 1
169-
},
170-
{
171-
label: 'Ethernet (TCP)',
172-
data: [203, 402, 784],
173-
backgroundColor: 'rgba(255, 99, 132, 0.8)',
174-
borderColor: 'rgba(255, 99, 132, 1)',
175-
borderWidth: 1
176-
}
177-
]
178-
},
179-
options: {
180-
responsive: true,
181-
plugins: {
182-
title: { display: true, text: 'Qwen2.5-72B: Aggregate Throughput', font: { size: 16 } },
183-
legend: { position: 'top' }
184-
},
185-
scales: {
186-
y: {
187-
beginAtZero: true,
188-
title: { display: true, text: 'Tokens/sec' },
189-
ticks: { callback: function(v) { return v.toLocaleString(); } }
190-
}
191-
}
192-
}
193-
});
194-
</script>
195-
196189
The 72B model shows a consistently larger gap than the 7B: **45× at 2 nodes, 44× at 4 nodes, and 39× at 8 nodes**. The 72B has 10× more parameters, generating proportionally more inter-node traffic per FSDP collective. InfiniBand scaling remains solid — 9,127 → 17,604 → 30,175 tok/s — though per-GPU efficiency drops from 571 to 472 tok/s (17%) as the massive collectives begin to saturate even the RDMA fabric. On Ethernet, each step takes over **2.5 minutes** regardless of scale, confirming completely network-dominated execution. The slight decrease in speedup ratio at 8 nodes reflects InfiniBand's growing (but still tolerable) communication overhead, while Ethernet is already so saturated that adding nodes barely changes per-GPU throughput.
197190

198191
### Mixtral 8x7B MoE (batch_size=1, seq_len=2048)
@@ -206,46 +199,6 @@ Mixtral 8x7B is a **Mixture-of-Experts** (MoE) model: 46.7B total parameters acr
206199
| 4 | 32 | 44,239 | 774 | **57.2×** |
207200
| 8 | 64 | 83,583 | 1,501 | **55.7×** |
208201

209-
<canvas id="chartMoeThroughput" width="700" height="400"></canvas>
210-
<script>
211-
new Chart(document.getElementById('chartMoeThroughput'), {
212-
type: 'bar',
213-
data: {
214-
labels: ['1 node\n(8 GPU)', '2 nodes\n(16 GPU)', '4 nodes\n(32 GPU)', '8 nodes\n(64 GPU)'],
215-
datasets: [
216-
{
217-
label: 'InfiniBand (RDMA)',
218-
data: [11577, 22520, 44239, 83583],
219-
backgroundColor: 'rgba(54, 162, 235, 0.8)',
220-
borderColor: 'rgba(54, 162, 235, 1)',
221-
borderWidth: 1
222-
},
223-
{
224-
label: 'Ethernet (TCP)',
225-
data: [11634, 398, 774, 1501],
226-
backgroundColor: 'rgba(255, 99, 132, 0.8)',
227-
borderColor: 'rgba(255, 99, 132, 1)',
228-
borderWidth: 1
229-
}
230-
]
231-
},
232-
options: {
233-
responsive: true,
234-
plugins: {
235-
title: { display: true, text: 'Mixtral 8x7B MoE: Aggregate Throughput', font: { size: 16 } },
236-
legend: { position: 'top' }
237-
},
238-
scales: {
239-
y: {
240-
beginAtZero: true,
241-
title: { display: true, text: 'Tokens/sec' },
242-
ticks: { callback: function(v) { return v.toLocaleString(); } }
243-
}
244-
}
245-
}
246-
});
247-
</script>
248-
249202
**The MoE model doubles the IB/ETH gap.** At 56–57× across all multi-node configurations, the MoE speedup is roughly **twice** the 7B dense model's 27× and exceeds even the 72B dense model's 38–45×. The single-node baseline is again identical (11,577 IB vs. 11,634 ETH), confirming the gap is purely an interconnect effect.
250203

251204
InfiniBand scaling remains excellent: **97.3% efficiency at 2 nodes and 90.2% at 8 nodes** (relative to the single-node baseline of 11,577 tok/s). On Ethernet, per-GPU throughput collapses from 1,454 tok/s (1-node) to 23–25 tok/s (multi-node) — each GPU spends over 98% of its time waiting for network transfers.

0 commit comments

Comments
 (0)