<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://choupara.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://choupara.github.io/" rel="alternate" type="text/html" /><updated>2026-06-29T06:51:44+00:00</updated><id>https://choupara.github.io/feed.xml</id><title type="html">Paramita Choudhury</title><subtitle>HPC · performance analysis · machine learning · formal reasoning</subtitle><author><name>Paramita Choudhury</name><email>paramita.chdry@outlook.com</email></author><entry><title type="html">Future Blog Post</title><link href="https://choupara.github.io/posts/2012/08/blog-post-4/" rel="alternate" type="text/html" title="Future Blog Post" /><published>2199-01-01T00:00:00+00:00</published><updated>2199-01-01T00:00:00+00:00</updated><id>https://choupara.github.io/posts/2012/08/future-post</id><content type="html" xml:base="https://choupara.github.io/posts/2012/08/blog-post-4/"><![CDATA[<p>This post will show up by default. To disable scheduling of future posts, edit <code class="language-plaintext highlighter-rouge">config.yml</code> and set <code class="language-plaintext highlighter-rouge">future: false</code>.</p>]]></content><author><name>Paramita Choudhury</name><email>paramita.chdry@outlook.com</email></author><category term="cool posts" /><category term="category1" /><category term="category2" /><summary type="html"><![CDATA[This post will show up by default. To disable scheduling of future posts, edit config.yml and set future: false.]]></summary></entry><entry><title type="html">Where does the time really go in multi-GPU training?</title><link href="https://choupara.github.io/posts/2026/06/where-time-goes/" rel="alternate" type="text/html" title="Where does the time really go in multi-GPU training?" /><published>2026-06-28T00:00:00+00:00</published><updated>2026-06-28T00:00:00+00:00</updated><id>https://choupara.github.io/posts/2026/06/where-time-goes</id><content type="html" xml:base="https://choupara.github.io/posts/2026/06/where-time-goes/"><![CDATA[<p>When you scale a model across GPUs and throughput goes up, it is tempting to stop there. But “it scaled” and “I know <em>why</em> it scaled” are very different statements - and the gap between them is exactly what fine-grained performance tracing exists to close.</p>

<p>This is a short, honest writeup of one measured finding: scaling DNABERT-2 fine-tuning across <strong>1 → 4 → 8 GPUs</strong> with PyTorch DDP, and using <strong>Score-P</strong> and <strong>Vampir</strong> to ask where the wall-clock time actually goes - compute, or inter-GPU communication.</p>

<p><em>(The practical side - eleven errors I hit getting Score-P to trace a DDP run at all - is a separate field guide on <a href="https://dev.to/choupara/debugging-score-p-with-pytorch-ddp-a-field-guide-to-cuda-error-802-and-other-surprises-4ehe">dev.to</a>.)</em></p>

<hr />

<h2 id="the-question">The question</h2>

<p>When DNABERT-2 fine-tuning is scaled across GPUs with PyTorch DDP, where does the time go - compute, or inter-GPU communication? And does that balance shift as the GPU count grows?</p>

<h2 id="method">Method</h2>

<p>Each DDP rank was launched under its <strong>own</strong> Score-P measurement (<code class="language-plaintext highlighter-rouge">python -m scorep --cuda</code>), so every worker’s GPU activity - compute kernels <em>and</em> NCCL communication kernels - was captured in a per-rank OTF2 trace. Traces were collected for 1, 4 and 8 GPUs (50 training steps each, CUDA kernel/memcpy/sync tracing enabled) and analysed both as text (<code class="language-plaintext highlighter-rouge">scorep-score</code>) and visually in Vampir 10.8.</p>

<h2 id="what-the-traces-show">What the traces show</h2>

<p><strong>1. Communication appears only when you scale out.</strong>
On 1 GPU the trace contains <strong>zero NCCL kernels</strong> - a clean, communication-free baseline. From 2 GPUs onward, <code class="language-plaintext highlighter-rouge">ncclKernel_AllReduce_RING_LL_Sum_float</code> (the gradient synchronisation) appears and grows into the dominant GPU activity.</p>

<p><strong>2. At 8 GPUs, gradient-sync communication costs as much GPU time as the entire backward pass.</strong></p>

<p><img src="/images/vampir-function-summary.png" alt="Vampir Function Summary for an 8-GPU rank: ncclKernel_AllReduce_RING_LL_Sum_float at 2.375 s, sitting right beside torch.autograd:backward at 2.22 s" /></p>

<table>
  <thead>
    <tr>
      <th>GPU function</th>
      <th>Accumulated time</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">ncclKernel_AllReduce_RING_LL_Sum_float</code> (communication)</td>
      <td><strong>2.375 s</strong></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">torch.autograd:backward</code> (compute)</td>
      <td>2.22 s</td>
    </tr>
  </tbody>
</table>

<p>The single AllReduce kernel is the largest GPU activity in the run - on par with the whole backward pass.</p>

<p><strong>3. …yet that communication is largely <em>hidden</em>, which is why scaling stays efficient.</strong></p>

<p><img src="/images/vampir-overlap-timeline.png" alt="Vampir Master Timeline showing two concurrent CUDA streams: compute kernels on the default stream CUDA[0:7] running at the same time as ncclKernel_AllReduce on a separate stream CUDA[0:20]" /></p>

<p>The Vampir Master Timeline shows two CUDA streams running <strong>concurrently</strong>: the default stream (<code class="language-plaintext highlighter-rouge">CUDA[0:7]</code>, <code class="language-plaintext highlighter-rouge">CUDA_NULL_STREAM</code>) carrying the forward/backward <strong>compute</strong> kernels (<code class="language-plaintext highlighter-rouge">gemm</code>, attention, element-wise), and a separate stream (<code class="language-plaintext highlighter-rouge">CUDA[0:20]</code>) carrying the <strong><code class="language-plaintext highlighter-rouge">ncclKernel_AllReduce</code></strong> kernels. The AllReduce blocks sit at the same time positions as the compute kernels: PyTorch DDP overlaps gradient AllReduce with backward computation by placing them on separate CUDA streams.</p>

<p>So although communication is <em>large in kernel-time</em>, much of it is <strong>overlapped behind compute</strong> and therefore costs little additional wall-clock. This is consistent with the measured end-to-end throughput, which scales near-linearly:</p>

<table>
  <thead>
    <tr>
      <th>Config</th>
      <th>Samples/sec</th>
      <th>Speedup</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1 GPU</td>
      <td>75.0</td>
      <td>1×</td>
    </tr>
    <tr>
      <td>4 GPU</td>
      <td>345.9</td>
      <td>4.61×</td>
    </tr>
    <tr>
      <td>8 GPU</td>
      <td>680.7</td>
      <td>9.08×</td>
    </tr>
  </tbody>
</table>

<h2 id="the-point">The point</h2>

<p>A higher-level metric - throughput, or job-level monitoring at ~30 s sampling - can only tell you <em>that</em> scaling is efficient. It cannot show you <em>why</em>. Fine-grained tracing with Score-P localises the cost to a specific kernel (<code class="language-plaintext highlighter-rouge">ncclKernel_AllReduce</code>), quantifies it (≈ backward-pass magnitude), and - through the Vampir stream timeline - reveals that it is hidden behind compute. That decomposition is invisible to monitoring and is exactly what trace-based performance analysis exists to provide.</p>

<h2 id="honest-caveats-stated-for-rigour">Honest caveats (stated for rigour)</h2>

<ul>
  <li>NCCL’s low-latency (LL) kernels <strong>busy-wait</strong> on peer flags, so their measured GPU-resident time includes synchronisation wait, not only data transfer - the absolute seconds <em>overstate</em> pure communication.</li>
  <li>Because compute and communication overlap on separate streams, summed kernel-time <strong>double-counts</strong> the overlapped portion; the <em>exposed</em> (wall-clock) cost is smaller and is what the timeline reveals.</li>
  <li>These are 50-step diagnostic runs on a shared node; absolute kernel times carry contention noise. The robust signals are <strong>qualitative and relative</strong>: NCCL absent at 1 GPU → dominant by 8 GPUs, overlapped with backward compute.</li>
</ul>

<hr />

<p><em>Traces generated on a SLURM cluster (A100-SXM4-40GB). The companion field guide on making Score-P and PyTorch DDP coexist is on <a href="https://dev.to/choupara/debugging-score-p-with-pytorch-ddp-a-field-guide-to-cuda-error-802-and-other-surprises-4ehe">dev.to</a>.</em></p>]]></content><author><name>Paramita Choudhury</name><email>paramita.chdry@outlook.com</email></author><category term="hpc" /><category term="gpu" /><category term="pytorch" /><category term="performance" /><category term="score-p" /><summary type="html"><![CDATA[A Score-P / Vampir trace analysis of DDP synchronisation overhead across 1 → 4 → 8 GPUs.]]></summary></entry><entry><title type="html">Blog Post number 4</title><link href="https://choupara.github.io/posts/2012/08/blog-post-4/" rel="alternate" type="text/html" title="Blog Post number 4" /><published>2015-08-14T00:00:00+00:00</published><updated>2015-08-14T00:00:00+00:00</updated><id>https://choupara.github.io/posts/2012/08/blog-post-4</id><content type="html" xml:base="https://choupara.github.io/posts/2012/08/blog-post-4/"><![CDATA[<p>This is a sample blog post. Lorem ipsum I can’t remember the rest of lorem ipsum and don’t have an internet connection right now. Testing testing testing this blog post. Blog posts are cool.</p>

<h1 id="headings-are-cool">Headings are cool</h1>

<h1 id="you-can-have-many-headings">You can have many headings</h1>

<h2 id="arent-headings-cool">Aren’t headings cool?</h2>]]></content><author><name>Paramita Choudhury</name><email>paramita.chdry@outlook.com</email></author><category term="cool posts" /><category term="category1" /><category term="category2" /><summary type="html"><![CDATA[This is a sample blog post. Lorem ipsum I can’t remember the rest of lorem ipsum and don’t have an internet connection right now. Testing testing testing this blog post. Blog posts are cool.]]></summary></entry><entry><title type="html">Blog Post number 3</title><link href="https://choupara.github.io/posts/2014/08/blog-post-3/" rel="alternate" type="text/html" title="Blog Post number 3" /><published>2014-08-14T00:00:00+00:00</published><updated>2014-08-14T00:00:00+00:00</updated><id>https://choupara.github.io/posts/2014/08/blog-post-3</id><content type="html" xml:base="https://choupara.github.io/posts/2014/08/blog-post-3/"><![CDATA[<p>This is a sample blog post. Lorem ipsum I can’t remember the rest of lorem ipsum and don’t have an internet connection right now. Testing testing testing this blog post. Blog posts are cool.</p>

<h1 id="headings-are-cool">Headings are cool</h1>

<h1 id="you-can-have-many-headings">You can have many headings</h1>

<h2 id="arent-headings-cool">Aren’t headings cool?</h2>]]></content><author><name>Paramita Choudhury</name><email>paramita.chdry@outlook.com</email></author><category term="cool posts" /><category term="category1" /><category term="category2" /><summary type="html"><![CDATA[This is a sample blog post. Lorem ipsum I can’t remember the rest of lorem ipsum and don’t have an internet connection right now. Testing testing testing this blog post. Blog posts are cool.]]></summary></entry><entry><title type="html">Blog Post number 2</title><link href="https://choupara.github.io/posts/2013/08/blog-post-2/" rel="alternate" type="text/html" title="Blog Post number 2" /><published>2013-08-14T00:00:00+00:00</published><updated>2013-08-14T00:00:00+00:00</updated><id>https://choupara.github.io/posts/2013/08/blog-post-2</id><content type="html" xml:base="https://choupara.github.io/posts/2013/08/blog-post-2/"><![CDATA[<p>This is a sample blog post. Lorem ipsum I can’t remember the rest of lorem ipsum and don’t have an internet connection right now. Testing testing testing this blog post. Blog posts are cool.</p>

<h1 id="headings-are-cool">Headings are cool</h1>

<h1 id="you-can-have-many-headings">You can have many headings</h1>

<h2 id="arent-headings-cool">Aren’t headings cool?</h2>]]></content><author><name>Paramita Choudhury</name><email>paramita.chdry@outlook.com</email></author><category term="cool posts" /><category term="category1" /><category term="category2" /><summary type="html"><![CDATA[This is a sample blog post. Lorem ipsum I can’t remember the rest of lorem ipsum and don’t have an internet connection right now. Testing testing testing this blog post. Blog posts are cool.]]></summary></entry><entry><title type="html">Blog Post number 1</title><link href="https://choupara.github.io/posts/2012/08/blog-post-1/" rel="alternate" type="text/html" title="Blog Post number 1" /><published>2012-08-14T00:00:00+00:00</published><updated>2012-08-14T00:00:00+00:00</updated><id>https://choupara.github.io/posts/2012/08/blog-post-1</id><content type="html" xml:base="https://choupara.github.io/posts/2012/08/blog-post-1/"><![CDATA[<p>This is a sample blog post. Lorem ipsum I can’t remember the rest of lorem ipsum and don’t have an internet connection right now. Testing testing testing this blog post. Blog posts are cool.</p>

<h1 id="headings-are-cool">Headings are cool</h1>

<h1 id="you-can-have-many-headings">You can have many headings</h1>

<h2 id="arent-headings-cool">Aren’t headings cool?</h2>]]></content><author><name>Paramita Choudhury</name><email>paramita.chdry@outlook.com</email></author><category term="cool posts" /><category term="category1" /><category term="category2" /><summary type="html"><![CDATA[This is a sample blog post. Lorem ipsum I can’t remember the rest of lorem ipsum and don’t have an internet connection right now. Testing testing testing this blog post. Blog posts are cool.]]></summary></entry></feed>