<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>liujunming</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>http://liujunming.github.io/</id>
  <link href="http://liujunming.github.io/" rel="alternate"/>
  <link href="http://liujunming.github.io/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, liujunming</rights>
  <subtitle>make it simple, make it happen.</subtitle>
  <title>L</title>
  <updated>2026-06-20T15:19:32.451Z</updated>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="计算机网络" scheme="http://liujunming.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
    <category term="虚拟化" scheme="http://liujunming.github.io/tags/%E8%99%9A%E6%8B%9F%E5%8C%96/"/>
    <category term="计算机网络" scheme="http://liujunming.github.io/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
    <content>
      <![CDATA[<p>本文将mark下macvtap的相关notes。<span id="more"></span></p><h2 id="tap-bridge"><a href="#tap-bridge" class="headerlink" title="tap + bridge"></a>tap + bridge</h2><p><img src="/images/2026/06/001.png"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">+-------------------+</span><br><span class="line">| QEMU VM           |</span><br><span class="line">|                   |</span><br><span class="line">|  virtio-net/e1000 |</span><br><span class="line">+---------+---------+</span><br><span class="line">          |</span><br><span class="line">          | TAP fd</span><br><span class="line">          v</span><br><span class="line">+-------------------+</span><br><span class="line">| tap0              |</span><br><span class="line">+---------+---------+</span><br><span class="line">          |</span><br><span class="line">          v</span><br><span class="line">+-------------------+</span><br><span class="line">| br0               |  Linux bridge</span><br><span class="line">|                   |</span><br><span class="line">| ports: tap0, eth0 |</span><br><span class="line">+---------+---------+</span><br><span class="line">          |</span><br><span class="line">          v</span><br><span class="line">+-------------------+</span><br><span class="line">| eth0 / ens33      |</span><br><span class="line">| physical NIC      |</span><br><span class="line">+---------+---------+</span><br><span class="line">          |</span><br><span class="line">          v</span><br><span class="line">+-------------------+</span><br><span class="line">| 外部二层网络       |</span><br><span class="line">| switch / LAN      |</span><br><span class="line">+-------------------+</span><br></pre></td></tr></table></figure><p>QEMU -&gt; tap0 -&gt; br0 -&gt; eth0 -&gt; LAN</p><h2 id="MACVLAN"><a href="#MACVLAN" class="headerlink" title="MACVLAN"></a>MACVLAN</h2><p><img src="/images/2026/06/002.png"></p><p>一般情况下，网卡只有一个MAC地址。然而，有些场景下需要给一个网卡设置多个MAC地址。Linux通过MACVLAN技术在一个物理网卡上创建多个MACVLAN虚拟设备，每个设备有着不同的MAC地址。当物理网卡收到数据包时，MACVLAN driver根据数据包MAC地址将数据包交由匹配的虚拟网卡处理。使用MACVLAN可以替代使用bridge来连接物理网卡和虚拟网络设备。</p><p>macvlan没有<code>/dev/tapX</code>。</p><h2 id="MACVTAP"><a href="#MACVTAP" class="headerlink" title="MACVTAP"></a>MACVTAP</h2><p><img src="/images/2026/06/003.png"></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">+-------------------+</span><br><span class="line">| QEMU VM           |</span><br><span class="line">|                   |</span><br><span class="line">|  virtio-net/e1000 |</span><br><span class="line">+---------+---------+</span><br><span class="line">          |</span><br><span class="line">          | /dev/tapX fd</span><br><span class="line">          v</span><br><span class="line">+-------------------+</span><br><span class="line">| macvtap0          |</span><br><span class="line">|                   |</span><br><span class="line">| TAP 字符设备接口   |</span><br><span class="line">| + macvlan 二层逻辑 |</span><br><span class="line">+---------+---------+</span><br><span class="line">          |</span><br><span class="line">          | lowerdev</span><br><span class="line">          v</span><br><span class="line">+-------------------+</span><br><span class="line">| eth0 / ens33      |</span><br><span class="line">| physical NIC      |</span><br><span class="line">+---------+---------+</span><br><span class="line">          |</span><br><span class="line">          v</span><br><span class="line">+-------------------+</span><br><span class="line">| 外部二层网络       |</span><br><span class="line">| switch / LAN      |</span><br><span class="line">+-------------------+</span><br></pre></td></tr></table></figure><p>QEMU -&gt; macvtap0 -&gt; eth0 -&gt; LAN</p><p>虚拟化中一般使用TAP和bridge来组建虚拟网络，但这样组网结构会稍显复杂。Linux上的MACTAP设备可以简化这种结构。MACVTAP设备集成了MACVLAN和TAP设备二者的特性。它可以基于一个物理网卡创建多个MAC地址不同的虚拟网卡，同时虚拟网卡收到的包不再交给内核协议栈，而是通过TAP设备的文件描述符传递到用户态进程。</p><p>macvtap把两层能力合并了：<br>普通 TAP 提供给 QEMU 的 fd + macvlan 提供的二层接入能力 &#x3D; macvtap</p><p>所以它能替代 tap + bridge 的关键点是：</p><ul><li>QEMU 仍然拿到一个像 TAP 一样的 fd</li><li>VM 仍然拥有独立 MAC</li><li>数据包仍然能从物理网卡进入外部二层网络</li><li>但中间不再需要显式 br0</li></ul><hr><p>参考资料:</p><ol><li><a href="https://developers.redhat.com/articles/2026/04/03/introduction-to-linux-interfaces-for-virtual-networking#">Introduction to Linux interfaces for virtual networking</a></li><li><a href="https://just4coding.com/2016/12/04/virtualnetworkdevice/">Linux虚拟网络设备</a></li></ol>]]>
    </content>
    <id>http://liujunming.github.io/2026/06/20/Notes-about-macvtap/</id>
    <link href="http://liujunming.github.io/2026/06/20/Notes-about-macvtap/"/>
    <published>2026-06-20T14:49:08.000Z</published>
    <summary>
      <![CDATA[<p>本文将mark下macvtap的相关notes。]]>
    </summary>
    <title>Notes about macvtap</title>
    <updated>2026-06-20T15:19:32.451Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="文件系统" scheme="http://liujunming.github.io/categories/%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F/"/>
    <category term="文件系统" scheme="http://liujunming.github.io/tags/%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F/"/>
    <content>
      <![CDATA[<p>本文将mark下Fuse over io-uring的相关notes。<span id="more"></span></p><h2 id="Prerequisite"><a href="#Prerequisite" class="headerlink" title="Prerequisite"></a>Prerequisite</h2><ul><li><a href="/2022/11/12/Notes-about-io-uring/">io-uring</a></li><li><a href="/2024/12/21/Notes-about-FUSE-filesystem/">fuse</a></li></ul><h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p>fast’24 paper:<a href="https://app.yinxiang.com/fx/40d78b24-4552-4582-b2c8-9a1354af89c8">RFUSE: Modernizing Userspace Filesystem Framework through Scalable Kernel-Userspace Communication</a></p><p>FUSE性能瓶颈：</p><ul><li>FUSE在内核和用户空间以及内存复制开销之间进行了多次上下文切换</li><li>FUSE驱动程序在向用户空间FUSE守护进程调度文件系统请求时，使用单个队列阻碍了FUSE实现可伸缩性能</li></ul><p><img src="/images/2026/05/043.png"></p><h2 id="Motivation"><a href="#Motivation" class="headerlink" title="Motivation"></a>Motivation</h2><p><a href="https://lwn.net/Articles/976031/">fuse: fuse-over-io-uring</a>:</p><blockquote><p>Motivation for these patches is all to increase fuse performance. In fuse-over-io-uring requests avoid core switching (application on core X, processing of fuse server on random core Y) and use shared memory between kernel and userspace to transfer data.</p></blockquote><h2 id="Fuse-over-io-uring-vs-RFUSE"><a href="#Fuse-over-io-uring-vs-RFUSE" class="headerlink" title="Fuse over io-uring vs RFUSE"></a>Fuse over io-uring vs RFUSE</h2><p>Fuse over io-uring与论文RFUSE的<strong>大方向高度一致，但实现路线不同、细节有差异</strong>；可以把 FUSE-over-io_uring 看作 RFUSE 思想在 Linux 内核里的<strong>工程化落地（io_uring 版）</strong>。</p><h3 id="核心思想：高度一致"><a href="#核心思想：高度一致" class="headerlink" title="核心思想：高度一致"></a>核心思想：高度一致</h3><p>两者都针对传统 FUSE 的同一痛点：</p><ol><li><strong>单队列 + 锁竞争</strong>，多核下扩展性差</li><li><strong>频繁上下文切换 + 内存拷贝</strong>，延迟高、CPU 开销大</li></ol><p>共同核心思路：</p><ol><li>per-core 独立队列：每个核一个通道，无锁、并行处理</li><li>共享内存 &#x2F; 环形缓冲区：内核↔用户态通过 ring 传递请求 &#x2F; 响应，减少系统调用与拷贝</li><li>兼容现有 FUSE：用户态文件系统不用改代码即可提速</li></ol><h3 id="关键差异：实现方案不同"><a href="#关键差异：实现方案不同" class="headerlink" title="关键差异：实现方案不同"></a>关键差异：实现方案不同</h3><ol><li>RFUSE（FAST’24 论文）</li></ol><ul><li>自研 类 io_uring 的 ring，不依赖内核 io_uring 子系统</li><li>hybrid polling：短时间忙轮询 + 休眠，平衡延迟与 CPU</li><li>原型，未并入主线内核</li></ul><ol start="2"><li>FUSE-over-io_uring（Linux 6.14 主线）</li></ol><ul><li>直接复用内核 io_uring 子系统，通过 IORING_OP_URING_CMD 扩展 FUSE</li><li>per-core io_uring 队列，亲和性调度，减少核间迁移</li><li>用 io_uring 原生机制处理提交 &#x2F; 完成 &#x2F; 内存管理，不做自研 ring</li><li>已合入主线，生产可用</li></ul><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>思想同源、实现分叉：</p><ul><li>RFUSE：学术验证，提出 “per-core ring + 共享内存 + 兼容 FUSE” 的高性能通信范式</li><li>FUSE-over-io_uring：工程实现，用标准io_uring把同一范式落地到 Linux 内核</li></ul><h2 id="Fuse-over-io-uring-per-core-io-uring"><a href="#Fuse-over-io-uring-per-core-io-uring" class="headerlink" title="Fuse over io-uring per-core io_uring"></a>Fuse over io-uring per-core io_uring</h2><p>内核文档:<br>在<a href="https://docs.kernel.org/next/filesystems/fuse-io-uring.html">FUSE-over-io-uring design documentation</a>可以看到:</p><blockquote><p>Note, every CPU core has its own fuse-io-uring queue.</p></blockquote><p><img src="/images/2026/05/044.png"></p><p>内核代码:<br><a href="https://elixir.bootlin.com/linux/v6.14/source/fs/fuse/dev_uring.c#L182-L194">https://elixir.bootlin.com/linux/v6.14/source/fs/fuse/dev_uring.c#L182-L194</a></p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">static</span> <span class="keyword">struct</span> fuse_ring *<span class="title function_">fuse_uring_create</span><span class="params">(<span class="keyword">struct</span> fuse_conn *fc)</span></span><br><span class="line">&#123;</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">fuse_ring</span> *<span class="title">ring</span>;</span></span><br><span class="line"><span class="type">size_t</span> nr_queues = num_possible_cpus();</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">fuse_ring</span> *<span class="title">res</span> =</span> <span class="literal">NULL</span>;</span><br><span class="line"><span class="type">size_t</span> max_payload_size;</span><br><span class="line"></span><br><span class="line">ring = kzalloc(<span class="keyword">sizeof</span>(*fc-&gt;ring), GFP_KERNEL_ACCOUNT);</span><br><span class="line"><span class="keyword">if</span> (!ring)</span><br><span class="line"><span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line"></span><br><span class="line">ring-&gt;queues = kcalloc(nr_queues, <span class="keyword">sizeof</span>(<span class="keyword">struct</span> fuse_ring_queue *),</span><br><span class="line">       GFP_KERNEL_ACCOUNT);</span><br></pre></td></tr></table></figure><p>每个CPU对应一个独立的 <code>struct fuse_ring_queue</code>。</p><p>请求分发:<br><a href="https://lwn.net/Articles/976031/">fuse: fuse-over-io-uring</a></p><blockquote><p>With fuse-over-io-uring requests are handled on the same core (sync requests)</p></blockquote><p>同步请求始终在发起的同一个 CPU 上处理，cache 友好：数据留在本地 core，减少 bouncing。</p><hr><p>参考资料:</p><ol><li><a href="https://docs.kernel.org/next/filesystems/fuse-io-uring.html">FUSE-over-io-uring design documentation</a></li><li><a href="https://lwn.net/Articles/976031/">fuse: fuse-over-io-uring</a></li><li><a href="https://www.phoronix.com/news/Linux-6.14-FUSE">FUSE Hooks Up With IO_uring For Greater Performance Potential In Linux 6.14</a></li></ol>]]>
    </content>
    <id>http://liujunming.github.io/2026/05/31/Notes-about-Fuse-over-io-uring/</id>
    <link href="http://liujunming.github.io/2026/05/31/Notes-about-Fuse-over-io-uring/"/>
    <published>2026-05-31T09:06:09.000Z</published>
    <summary>
      <![CDATA[<p>本文将mark下Fuse over io-uring的相关notes。]]>
    </summary>
    <title>Notes about Fuse over io-uring</title>
    <updated>2026-05-31T14:21:58.386Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="PCI&amp;PCIe" scheme="http://liujunming.github.io/categories/PCI-PCIe/"/>
    <category term="PCI&amp;PCIe" scheme="http://liujunming.github.io/tags/PCI-PCIe/"/>
    <content>
      <![CDATA[<p>本文将mark下PCIe Error Message机制的相关notes，内容主要转载自<a href="https://mp.weixin.qq.com/s/NZT6CtKFyCRkTZ36r51fvA">Error message控制</a>。<span id="more"></span></p><h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><p>error signaling: One agent notifying another agent of an error either by (1) sending an error Message, (2) sending a Completion with UR&#x2F;CA Status, or (3) poisoning a TLP.</p><p>PCIe Error Signaling Messages是一种带内(In-band)错误上报机制，通过专用的Message TLP(TLP Type is Msg)将错误信息从检测点传递给Root Complex，进而通知系统软件。</p><p>Error Signaling Messages are used to signal errors that occur on specific transactions and errors that are not necessarily associated with a particular transaction. These Messages are initiated by the agent that detected the error.</p><p>Message类型的TLP，使用隐式路由(Routed to Root Complex)，即无论从拓扑中哪个设备发出，都会沿上行方向逐跳传递，最终到达Root Complex。</p><h2 id="分类"><a href="#分类" class="headerlink" title="分类"></a>分类</h2><p><img src="/images/2026/05/029.png"></p><h2 id="流程"><a href="#流程" class="headerlink" title="流程"></a>流程</h2><p>The initiator of the Message is identified with the Requester ID of the Message header. The Root Complex translates these error Messages into platform level events.</p><p><img src="/images/2026/05/030.png"></p><p>如果Root Port支持AER(Advanced Error Report)，RC从message中提取Requester ID字段，并记录的Root port的Error Source Identification寄存器中，这个BDF号就是出错的设备。不过很多时候这个BDF是捕获不到的，或者出现多个错误，RC只能提取第一个错误的Requester ID。因此，需要driver遍历Root Port下的所有设备，检查哪些设备出现了错误。</p><p><img src="/images/2026/05/031.png"></p><p>如果Root Port支持AER，则收到error message(根据error code可以分为ERR_COR&#x2F;ERR_NONFATAL&#x2F;ERR_FATAL)或者Root port自己检查到对应错误时，会记录到Root Port的Root Error Status寄存器。</p><p><img src="/images/2026/05/032.png"></p><p>Error message从PCIe设备产生到路由到对应Root Por涉及一系列的控制寄存器和状态寄存器。</p><p><img src="/images/2026/05/033.png"></p><h2 id="PCIe-Error-Message产生MSI中断"><a href="#PCIe-Error-Message产生MSI中断" class="headerlink" title="PCIe Error Message产生MSI中断"></a>PCIe Error Message产生MSI中断</h2><p>Root Port下面的设备或Root Port本身发生PCIe错误想产生MSI中断话，需要关注哪些寄存器呢？</p><h3 id="整条链路上所有bridge的Bridge-ctrl-reg的SERR-bit"><a href="#整条链路上所有bridge的Bridge-ctrl-reg的SERR-bit" class="headerlink" title="整条链路上所有bridge的Bridge ctrl reg的SERR# bit"></a>整条链路上所有bridge的Bridge ctrl reg的SERR# bit</h3><p>如果该bit没有使能，则bridge不会往上游转发下游的设备上报的error message。如果是Root port本身产生的错误的则不需要关注该bit。</p><p><img src="/images/2026/05/034.png"></p><h3 id="整条链路上支持DPC的设备（DP和RP）是否使能了DPC"><a href="#整条链路上支持DPC的设备（DP和RP）是否使能了DPC" class="headerlink" title="整条链路上支持DPC的设备（DP和RP）是否使能了DPC"></a>整条链路上支持DPC的设备（DP和RP）是否使能了DPC</h3><p>如果使能了DPC trigger enable，下游设备发送的ERR_FATAL或ERR_NONFATAL的error message会被DPC拦截。</p><p><img src="/images/2026/05/035.png"></p><h3 id="整条链路上的所有PCIe设备的Device-ctrl-reg的对应bit是否使能"><a href="#整条链路上的所有PCIe设备的Device-ctrl-reg的对应bit是否使能" class="headerlink" title="整条链路上的所有PCIe设备的Device ctrl reg的对应bit是否使能"></a>整条链路上的所有PCIe设备的Device ctrl reg的对应bit是否使能</h3><p>Device ctrl reg的低4bit是一个比较大的控制开关，分布控制ERR_COR message、ERR_NONFATAL message、ERR_FATAL message和Unsupported Request的发送。</p><p><img src="/images/2026/05/036.png"></p><h3 id="Root-port的Root-error-command-reg对应bit是否为1"><a href="#Root-port的Root-error-command-reg对应bit是否为1" class="headerlink" title="Root port的Root error command reg对应bit是否为1"></a>Root port的Root error command reg对应bit是否为1</h3><p>如果Root error command reg对应bit为1，则Root port本身或者Root port收到下游设备上报错误时才会产生中断。</p><p><img src="/images/2026/05/037.png"></p><h3 id="Root-Port的Root-ctrl-reg对应bit是否清零"><a href="#Root-Port的Root-ctrl-reg对应bit是否清零" class="headerlink" title="Root Port的Root ctrl reg对应bit是否清零"></a>Root Port的Root ctrl reg对应bit是否清零</h3><p>如果Root ctrl reg对应bit没有清零，则会走左边的分支产生system error，对X86而言这是一条带外的pin。</p><p><img src="/images/2026/05/038.png"></p><h3 id="整个链路上的PCIe设备error-mask-reg是否置1"><a href="#整个链路上的PCIe设备error-mask-reg是否置1" class="headerlink" title="整个链路上的PCIe设备error mask reg是否置1"></a>整个链路上的PCIe设备error mask reg是否置1</h3><p>如果error mask reg为1，则产生对应错误时，只会更新PCIe CAP的error status寄存器，不会产生error message。</p><p><img src="/images/2026/05/039.png"></p><p><img src="/images/2026/05/040.png"></p><h3 id="Command寄存器的memory-space-enable-bit和bus-master-enable-bit是否为1，interrupt-disable是否为1"><a href="#Command寄存器的memory-space-enable-bit和bus-master-enable-bit是否为1，interrupt-disable是否为1" class="headerlink" title="Command寄存器的memory space enable bit和bus master enable bit是否为1，interrupt disable是否为1"></a>Command寄存器的memory space enable bit和bus master enable bit是否为1，interrupt disable是否为1</h3><p>MSI中断本质上是一个memory write请求，Command寄存器的memory space enable bit和bus master enable bit负责MSI中断的产生和转发。</p><p>Interrupt disable是禁止INTx中断。由于，INTx优先级比MSI中断要高，该bit需要禁止。</p><p><img src="/images/2026/05/041.png"></p><h3 id="Root-Port的MSI-enable是否为1"><a href="#Root-Port的MSI-enable是否为1" class="headerlink" title="Root Port的MSI enable是否为1"></a>Root Port的MSI enable是否为1</h3><p>Error message会路由到Root Port，如果想产生MSI中断，root port的MSI enable bit需要为1。</p><p><img src="/images/2026/05/042.png"></p><hr><p>参考资料:</p><ol><li><a href="https://mp.weixin.qq.com/s/NZT6CtKFyCRkTZ36r51fvA">Error message控制</a></li><li>PCI Express® Base Specification Revision 5.0 Version 1.0</li></ol>]]>
    </content>
    <id>http://liujunming.github.io/2026/05/31/Notes-about-PCIe-Error-Message%E6%9C%BA%E5%88%B6/</id>
    <link href="http://liujunming.github.io/2026/05/31/Notes-about-PCIe-Error-Message%E6%9C%BA%E5%88%B6/"/>
    <published>2026-05-30T22:56:26.000Z</published>
    <summary>
      <![CDATA[<p>本文将mark下PCIe Error Message机制的相关notes，内容主要转载自<a href="https://mp.weixin.qq.com/s/NZT6CtKFyCRkTZ36r51fvA">Error message控制</a>。]]>
    </summary>
    <title>Notes about PCIe Error Message机制</title>
    <updated>2026-05-31T08:38:22.618Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="PCI&amp;PCIe" scheme="http://liujunming.github.io/categories/PCI-PCIe/"/>
    <category term="PCI&amp;PCIe" scheme="http://liujunming.github.io/tags/PCI-PCIe/"/>
    <content>
      <![CDATA[<p>本文将mark下PCIe中上行端口(upstream port)和多个下行端口(downstream port)的相关notes<span id="more"></span>，内容主要转载自<a href="https://mp.weixin.qq.com/s/gEPPooSG9ATud50Ch2Ldcw">Upstream 和 Dowstream</a>。</p><p><strong>上行(Upstream)和下行(Downstream)是一个方向的概念</strong>。</p><p>在一个PCIe系统中，方向的定义是以RC为准的:RC高高在上，面往RC方向称之为上行（红色粗箭头），反之称之为下行（蓝色粗箭头）。</p><p><img src="/images/2026/05/027.png"></p><p>由于PCIe是点对点连接的，每个连接的地方，我们称之为Port。对于Root Complex而言，它仅有一个下行端口。对于PCIe switch，它有一个上行端口（upstream port）和多个下行端口（downstream ports）。而PCIe设备（EP）仅有一个上行端口。</p><p>一般而言，上行端口是和一个下行端口连接在一起。<strong>注意，是一般而言，某些特殊的情况下，两个下行端口也是可以连接在一起的，我们称之为crosslink</strong>。</p><p>复习一下switch内部的结构图，可以加深我们对上下行的认识。</p><p><img src="/images/2026/05/028.png"></p><p>上行和下行是PCIe中非常重要的方向，初期很容易记混淆。有必要再默记三遍：<strong>以RC为中心，朝着它去的方向就是上行，远离它的方向就是下行</strong>。</p>]]>
    </content>
    <id>http://liujunming.github.io/2026/05/30/Notes-about-PCIe-upstream-port-and-downstream-port/</id>
    <link href="http://liujunming.github.io/2026/05/30/Notes-about-PCIe-upstream-port-and-downstream-port/"/>
    <published>2026-05-30T07:49:37.000Z</published>
    <summary>
      <![CDATA[<p>本文将mark下PCIe中上行端口(upstream port)和多个下行端口(downstream port)的相关notes]]>
    </summary>
    <title>Notes about PCIe upstream port and downstream port</title>
    <updated>2026-05-30T08:20:16.929Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="AI Infra" scheme="http://liujunming.github.io/categories/AI-Infra/"/>
    <category term="AI Infra" scheme="http://liujunming.github.io/tags/AI-Infra/"/>
    <content>
      <![CDATA[<p>本文将mark下TP(Tensor Parallelism)、PP(Pipeline Parallelism)和DP(Data Parallelism)的相关notes，本文内容转载自<a href="https://mp.weixin.qq.com/s/XPHJTQVWSLLVBqiHbwdMkw">不是多卡，而是多种“切法”：一文讲透 TP、PP、DP</a>。<span id="more"></span></p><h2 id="标定前提"><a href="#标定前提" class="headerlink" title="标定前提"></a>标定前提</h2><p>本文统一用这个场景来讲：</p><ul><li>模型：70B</li><li>精度：FP16</li><li>模型参数体量：约 140GB</li><li>服务器：单机 8 张 GPU</li><li>每张 GPU 显存：80GB</li></ul><p>140GB模型参数体量意味着：如果你把模型参数按 FP16 存，总共大约要占<strong>140GB 显存</strong>。而单张卡只有 80GB。</p><p>所以第一件马上成立的事就是：<strong>单卡装不下</strong>。</p><p>这一步非常关键，因为它决定了一个基本事实：</p><blockquote><p>这不是“要不要多卡”的问题。这是“必须多卡”的问题。</p></blockquote><p>但“必须多卡”，并不自动等于你知道该怎么切。</p><p>因为你接下来马上会碰到第二个问题：既然 8 张卡总显存有 640GB，那是不是直接随便分一分就行？也不行。因为大模型不是一块静态硬盘文件。</p><p>它在跑的时候，除了参数本身，还有：</p><ul><li>激活值</li><li>中间结果</li><li>KV Cache（推理时）</li><li>梯度（训练时）</li><li>优化器状态（训练时更大）</li><li>通信缓存</li><li>框架额外开销</li></ul><p>所以真正的问题，不是“640GB 总显存是不是大于 140GB”。而是：<strong>你打算怎么把模型和计算过程，拆到这 8 张卡上</strong>。</p><p>这就引出了今天的主角：</p><ul><li>TP：Tensor Parallelism</li><li>PP：Pipeline Parallelism</li><li>DP：Data Parallelism</li></ul><p>它们都和“多卡”有关。但不是一回事。</p><p><img src="/images/2026/05/018.png"></p><h2 id="三种“切法”"><a href="#三种“切法”" class="headerlink" title="三种“切法”"></a>三种“切法”</h2><blockquote><p>TP 是横着切。<br>PP 是竖着切。<br>DP 不是切模型，而是复制模型、切数据。</p></blockquote><h3 id="TP-在切什么？"><a href="#TP-在切什么？" class="headerlink" title="TP 在切什么？"></a>TP 在切什么？</h3><p>它在切的是：<strong>同一层内部的张量计算</strong>。</p><p>也就是说，一层本来是一个大矩阵乘法。TP 会把这个大矩阵，拆到多张卡一起算。</p><h3 id="PP-在切什么？"><a href="#PP-在切什么？" class="headerlink" title="PP 在切什么？"></a>PP 在切什么？</h3><p>它在切的是：<strong>模型的层</strong>。</p><p>也就是说，模型原本是一层接一层串起来的。PP 会把前面几层放到一组卡，后面几层放到另一组卡。</p><h3 id="DP-在切什么？"><a href="#DP-在切什么？" class="headerlink" title="DP 在切什么？"></a>DP 在切什么？</h3><p>它不切模型结构。它切的是：<strong>输入数据 &#x2F; batch。</strong></p><p>也就是说，每一份模型副本都长得一样。但不同副本同时处理不同的数据，然后再同步梯度。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>TP 解决“单层太大”。<br>PP 解决“整模型太长、装不下”。<br>DP 解决“吞吐不够，想并行处理更多数据”。</p><p><img src="/images/2026/05/019.png"></p><h2 id="PP"><a href="#PP" class="headerlink" title="PP"></a>PP</h2><p>PP最先回答一个现实问题：<strong>140GB 模型，单卡 80GB 装不下，怎么办？</strong></p><p>最直观的办法是什么？把模型按层切开。</p><p>比如，一个大模型本质上可以粗略理解成很多层堆起来：</p><ul><li>Embedding</li><li>Transformer Block 1</li><li>Transformer Block 2</li><li>…</li><li>Transformer Block N</li><li>最后输出层</li></ul><p>这时候，PP 的思路就是：</p><blockquote><p>既然一整本书放不进一个抽屉，<br>那就把这本书按章节拆开，前半本放一个抽屉，后半本放另一个抽屉。</p></blockquote><p>在工程上，这就叫<strong>Pipeline Parallelism</strong>。</p><h3 id="PP-2是最自然的第一步"><a href="#PP-2是最自然的第一步" class="headerlink" title="PP&#x3D;2是最自然的第一步"></a>PP&#x3D;2是最自然的第一步</h3><p>70B 模型 140GB。<br>如果按 PP&#x3D;2 去切，意味着：</p><ul><li>模型前半段放在一组 GPU</li><li>模型后半段放在另一组 GPU</li></ul><p>从参数体量角度粗略看，就是：140GB&#x2F;2&#x3D;70GB</p><p>这就意味着：<strong>每一段模型参数大约 70GB</strong>，而 70GB 小于单卡 80GB。</p><p>于是，一个非常重要的事实出现了：<strong>从“参数能否装下”这个角度看，PP&#x3D;2 已经足够</strong>。</p><p><strong>PP首先是在解决“模型按层分段存放”</strong>，这是它最核心的意义之一。</p><h3 id="PP-2不代表只用2张卡"><a href="#PP-2不代表只用2张卡" class="headerlink" title="PP&#x3D;2不代表只用2张卡"></a>PP&#x3D;2不代表只用2张卡</h3><p>很多人一看到 PP&#x3D;2，就会下意识理解成：<strong>那是不是只用 2 张卡？</strong></p><p>不一定。PP&#x3D;2 的意思，是<strong>模型被分成2段</strong>。至于每一段用几张卡，那要看你这一段内部是否还继续做TP，或者做别的切法。</p><p>也就是说：</p><ul><li>PP 决定的是“分几段”</li><li>不是“总共几张卡”</li></ul><p><strong>这一点必须记住</strong>。</p><p>比如后面我们很可能会用：</p><ul><li>PP&#x3D;2</li><li>每段再用 TP&#x3D;2</li><li>那总共就是 2 × 2 &#x3D; 4 张卡</li></ul><p>甚至还可能在更多机器上叠加 DP。</p><p>所以 PP 和卡数之间，不是简单一一对应关系，它更像“先把模型分成几大段”</p><h3 id="PP的代价"><a href="#PP的代价" class="headerlink" title="PP的代价"></a>PP的代价</h3><p>PP 解决了装不下的问题，但它不是免费的。</p><p>因为一旦按层分段，数据在前向传播时就必须这样走：</p><ul><li>输入先进入第 1 段模型</li><li>第 1 段算完，把中间激活传给第 2 段</li><li>第 2 段再继续算</li></ul><p>反向传播也是一样，要一段一段回传。</p><p>所以 PP 带来的代价是：<strong>跨 stage 的激活传输</strong>，以及更重要的：<strong>流水线气泡（pipeline bubble）</strong>。</p><p>因为模型是串行分段的，如果流水线调度不好，就会出现：</p><ul><li>前一段忙</li><li>后一段在等</li><li>某些 GPU 一部分时间没有活干</li></ul><p>这就是为什么PP虽然很适合解决“装不下”，但它并不是性能上永远最优的切法。</p><p>它优先解决的是：<strong>模型太长、显存不够的问题</strong>。</p><p><img src="/images/2026/05/020.png"></p><h2 id="TP"><a href="#TP" class="headerlink" title="TP"></a>TP</h2><p>如果说 PP 像是“把一本书按章节撕成上下两半”，那 TP 更像是：<strong>把同一页内容，左右拆开，让两个人同时看</strong>。</p><p>大模型里的核心计算，本质上大量来自矩阵乘法。<br>比如一个线性层，本来可能是这样：<code>Y = XW</code><br>这里的 W 可能非常大。<br>如果这个矩阵很大，大到一张卡算它已经很吃力，或者你想进一步并行加速，那就可以用 TP。</p><p>TP 的思路是：</p><blockquote><p>不是把“前面几层”和“后面几层”分开。<br>而是让“同一层”本身，拆成几块，放到多张卡一起算。</p></blockquote><h3 id="在层内切矩阵"><a href="#在层内切矩阵" class="headerlink" title="在层内切矩阵"></a>在层内切矩阵</h3><p>比如一个很大的权重矩阵 W，你可以按列切成两半，或者按行切成两半。<br>这样：</p><ul><li>GPU0 算一部分</li><li>GPU1 算另一部分</li><li>最后再把结果拼起来，或者做归约</li></ul><p>这就是 TP 的核心。</p><p>所以 TP 不是“层和层之间分工”。而是：<strong>同一层内部，多张卡协同完成</strong>。</p><h3 id="为什么要做-TP？"><a href="#为什么要做-TP？" class="headerlink" title="为什么要做 TP？"></a>为什么要做 TP？</h3><p>主要有两个原因：<br><strong>原因一：单层太大</strong><br>有些单层参数和中间计算规模本来就很大。即使整模型通过 PP 已经分段了，某一段内部的某些层仍然很重。</p><p><strong>原因二：想提升单层并行度</strong><br>即便显存够，你也可能想让同一层在多张卡上并行算，加快速度。所以 TP 解决的不是“整本书放不下”。而是：<strong>同一页太宽，一个人看不过来</strong>。</p><h3 id="代价"><a href="#代价" class="headerlink" title="代价"></a>代价</h3><p>TP 最核心的代价是：<strong>层内通信</strong>。</p><p>为什么？因为同一层被拆到了多张卡上。那每次前向和反向过程中，多张卡之间都要交换部分结果。</p><p>比如常见的：</p><ul><li>AllReduce</li><li>AllGather</li><li>ReduceScatter</li></ul><p>你一旦做了 TP，这些 collective 通信几乎就绕不过去。也就是说，TP 带来的好处是：</p><ul><li>同一层可以多卡一起算</li><li>单卡显存压力更小</li><li>单层算力更容易并行起来</li></ul><p>但它的代价就是：<strong>通信更频繁</strong>。</p><p>而这件事一旦放到多机环境，就会和 NCCL、AllReduce、Incast、ECN 这些网络问题直接撞上。<br>这也是为什么很多人后来会发现：</p><blockquote><p>TP 不是“白赚性能”。<br>它经常是在“更多并行”和“更多通信”之间做权衡。</p></blockquote><p><img src="/images/2026/05/021.png"></p><h2 id="DP"><a href="#DP" class="headerlink" title="DP"></a>DP</h2><p>如果 PP 解决“装不下”，TP 解决“同一层太大或想并行更快”，那 DP 解决的是什么？答案是：<strong>吞吐</strong>。</p><p>也就是：</p><blockquote><p>我希望同一时间处理更多样本，让更多 GPU 一起吃不同数据。</p></blockquote><p>这就是 Data Parallelism。</p><h3 id="DP的本质：每份模型副本都长一样"><a href="#DP的本质：每份模型副本都长一样" class="headerlink" title="DP的本质：每份模型副本都长一样"></a>DP的本质：每份模型副本都长一样</h3><p>这是理解 DP 的第一关键点。</p><p>DP 不会把模型切开，它会做的，是把<strong>同一份模型复制多份</strong>。</p><p>比如你有两个 DP 副本，那就是：</p><ul><li>副本 A：一整份模型</li><li>副本 B：一整份模型</li></ul><p>这两份模型参数初始相同，但它们分别处理不同的数据 batch。</p><h3 id="为什么最后还要同步？"><a href="#为什么最后还要同步？" class="headerlink" title="为什么最后还要同步？"></a>为什么最后还要同步？</h3><p>因为虽然两份模型处理的是不同数据，但你希望训练完之后，它们还是同一个模型。<br>所以每一轮反向传播结束后，不同副本的梯度要做同步。<br>这一步最经典的就是：<strong>AllReduce</strong>。</p><p>也正因为如此，DP 一旦规模上去，网络压力会迅速增大。因为它不是偶尔同步，而是每一步都在同步。</p><h3 id="在我们的这个场景里，单机8卡适合直接做DP吗？"><a href="#在我们的这个场景里，单机8卡适合直接做DP吗？" class="headerlink" title="在我们的这个场景里，单机8卡适合直接做DP吗？"></a>在我们的这个场景里，单机8卡适合直接做DP吗？</h3><p>如果你只从“模型装不装得下”看，答案是：<strong>不能先直接谈 DP</strong>。</p><p>为什么？因为 DP 的前提是：<strong>每个副本本身得先成立</strong>。</p><p>也就是说，你得先能把“一份模型副本”放到某些卡上，然后才谈复制成多份。</p><p>而我们这里，70B 模型 140GB，单卡 80GB，单卡本身放不下。</p><p>所以如果没有 PP 或 TP 先把单副本做成立，你根本没法谈 DP，这一步非常关键。</p><p>很多人第一次接触时，会本能问：</p><blockquote><p>8 张卡，DP&#x3D;8 行不行？</p></blockquote><p>不行。因为 DP&#x3D;8 的含义是：你要有 8 份完整模型副本。</p><p>而完整模型 140GB，单卡 80GB 放不下，所以你不能把一份完整模型直接塞到一张卡上，再复制 8 份。</p><p>这也是为什么我们说：</p><blockquote><p>DP 不是第一步。<br>它通常建立在 TP&#x2F;PP 已经先让“单副本可运行”之上。</p></blockquote><p><img src="/images/2026/05/022.png"></p><h2 id="具体例子"><a href="#具体例子" class="headerlink" title="具体例子"></a>具体例子</h2><p>直接回到这个问题：</p><blockquote><p>70B 模型，FP16，140GB。<br>单机 8 张 GPU，每张 80GB。<br>TP、PP、DP 到底该怎么理解？</p></blockquote><p>最关键的是先建立一个顺序。</p><p>第一步：<strong>先解决“单副本能不能成立”</strong><br>这个问题最先决定的是：<strong>不能先想 DP</strong>。<br>因为 DP 是复制副本。但一份副本本身都放不下，复制无从谈起，所以要先考虑 TP 和 PP。</p><p>第二步：最直观的第一刀，通常是 PP&#x3D;2<br>因为 140GB ÷ 2 &#x3D; 70GB。</p><p>从参数体量上看，每段 70GB，可以落进 80GB 卡，所以从“装下参数”这件事出发，PP&#x3D;2 是一个非常自然的第一步。</p><p>但这里你要立刻意识到：这只是“参数大致装下”，并不等于实际运行一定完美，因为还有激活、缓存、框架开销等。</p><p>第三步：再考虑每一段内部要不要 TP<br>如果某个 stage 内部的单层计算仍然很重，或者你希望更高的并行度，那就可以在每个 stage 内部再做 TP。</p><p>比如一个很常见的思路：</p><ul><li>PP&#x3D;2</li><li>每个 stage 再 TP&#x3D;2</li></ul><p>那总共就用到：<code>2 × 2 = 4 张 GPU</code>，这时，每个模型副本占 4 张卡。</p><p>这一步很重要，因为它让你第一次看到：<strong>PP 和 TP 是可以叠加的</strong>。不是二选一，而是先按层分段，再在段内继续拆张量。</p><p>第四步：剩余 GPU 才有可能拿来做 DP<br>如果你总共有 8 张卡，而一份模型副本用了 4 张卡（PP&#x3D;2 × TP&#x3D;2），那你剩下的还能干什么？</p><p>答案是：可以再复制一份副本。</p><p>于是就得到：</p><ul><li>PP&#x3D;2</li><li>TP&#x3D;2</li><li>DP&#x3D;2</li></ul><p>因为：<code>PP × TP × DP = 2 × 2 × 2 = 8</code><br>刚好用满 8 张卡。</p><p>这就是为什么很多人第一次听到这种组合时，会突然“通了”，它不是在背一个缩写组合，而是在做一件非常具体的资源分解：</p><ol><li>先按 PP&#x3D;2 解决模型分段装载</li><li>再按 TP&#x3D;2 解决段内并行与层内切分</li><li>最后按 DP&#x3D;2 利用剩余资源复制两份副本，提升吞吐</li></ol><p>这就是一个完整的思考链。<br><img src="/images/2026/05/023.png"></p><h2 id="DP的计算"><a href="#DP的计算" class="headerlink" title="DP的计算"></a>DP的计算</h2><p>比如你可能会听到这样的问题：</p><blockquote><p>70B 模型，8 张卡，PP&#x3D;2，TP&#x3D;2，那 DP 到底是不是 2？</p></blockquote><p>答案是：<strong>如果你总共只有 8 张卡，而且一份副本占 4 张卡，那 DP 就是 2</strong>。</p><p>因为 DP 的定义不是“我想设几就设几”，而是由总卡数和单副本占卡数一起决定的。</p><p>公式其实非常简单：<code>DP = 总GPU数 / (TP × PP)</code></p><p>在这个例子里：<code>DP = 8 / (2 × 2) = 2</code></p><p>所以很多人真正没想明白的，不是数学，而是逻辑顺序：</p><ul><li>TP × PP<br>先定义了“一份模型副本需要多少卡”</li><li>DP<br>再定义“在总卡数里，可以复制多少份副本”</li></ul><p>也就是说：DP 不是凭空来的，DP 是在 TP&#x2F;PP 先把单副本做成立之后，剩余资源所能容纳的副本数。</p><p><img src="/images/2026/05/024.png"></p><h2 id="代价-1"><a href="#代价-1" class="headerlink" title="代价"></a>代价</h2><p>真正理解这三个东西，不能只看它们“怎么切”,还要看：<strong>它们分别引入了什么代价</strong>。</p><p>因为工程上最难的，从来不是名词,而是代价。</p><h3 id="PP-的代价：段间依赖-流水线气泡"><a href="#PP-的代价：段间依赖-流水线气泡" class="headerlink" title="PP 的代价：段间依赖 + 流水线气泡"></a>PP 的代价：段间依赖 + 流水线气泡</h3><p>PP 把模型按层切成几段,它最大的优点是：</p><ul><li>最直观地解决“装不下”</li><li>每段模型相对独立</li></ul><p>但它的问题是：</p><ul><li>前后段天然串行依赖</li><li>要传激活</li><li>流水线不饱满时容易空等</li></ul><p>所以 PP 更像是在说：<strong>我接受一定的串行和调度复杂度，换模型能装下</strong>。</p><h3 id="TP-的代价：层内高频通信"><a href="#TP-的代价：层内高频通信" class="headerlink" title="TP 的代价：层内高频通信"></a>TP 的代价：层内高频通信</h3><p>TP 的优点是：</p><ul><li>同一层可以多卡一起算</li><li>单层规模可拆</li><li>层内并行度提高</li></ul><p>但它的问题是：</p><ul><li>每层都要频繁 collective 通信</li><li>通信会非常密</li><li>一旦跨机，网络压力会快速放大</li></ul><p>所以 TP 更像是在说：<strong>我接受更多层内通信，换单层并行和显存拆分</strong>。</p><h3 id="DP-的代价：每一步都要梯度同步"><a href="#DP-的代价：每一步都要梯度同步" class="headerlink" title="DP 的代价：每一步都要梯度同步"></a>DP 的代价：每一步都要梯度同步</h3><p>DP 的优点是：</p><ul><li>思路最直观</li><li>最容易提高吞吐</li><li>每个副本内部逻辑一致</li></ul><p>但它的问题是：</p><ul><li>训练时每一步都要 AllReduce 梯度</li><li>副本越多，同步压力越大</li><li>网络会直接成为性能关键因素</li></ul><p>所以 DP 更像是在说：<strong>我接受更大的梯度同步压力，换更高吞吐</strong>。</p><h3 id="网络通信"><a href="#网络通信" class="headerlink" title="网络通信"></a>网络通信</h3><p>因为一旦你不再是单卡，而是 TP&#x2F;PP&#x2F;DP 混合，通信就不再只是“有一点”。</p><p>而是会分成不同形态：</p><ul><li>TP：层内高频通信</li><li>PP：段间激活传输</li><li>DP：副本间梯度同步</li></ul><p>这些通信最终都会落到：</p><ul><li>NCCL</li><li>AllReduce</li><li>AllGather</li><li>ReduceScatter</li><li>P2P</li><li>Incast 风险</li><li>ECN &#x2F; PFC &#x2F; queue 管理</li></ul><p>也就是说，TP&#x2F;PP&#x2F;DP 不只是“模型切法”。<br>它们本质上也在定义：<strong>你的网络压力会长成什么样</strong>。</p><p><img src="/images/2026/05/025.png"></p><h2 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h2><p>到这里，我们把全文压缩成三句话。</p><p><strong>PP 在回答：模型太长、整份装不下，怎么办？</strong><br>答案是：按层分段。</p><p><strong>TP 在回答：同一层太大，或者想让同一层多卡并行，怎么办？</strong><br>答案是：把同一层内部的张量拆开。</p><p><strong>DP 在回答：单副本已经能跑了，但我想同时处理更多数据，怎么办？</strong><br>答案是：复制模型副本，切数据。</p><p><img src="/images/2026/05/026.png"></p><hr><p>参考资料:</p><ol><li><a href="https://mp.weixin.qq.com/s/XPHJTQVWSLLVBqiHbwdMkw">不是多卡，而是多种“切法”：一文讲透 TP、PP、DP</a></li></ol>]]>
    </content>
    <id>http://liujunming.github.io/2026/05/24/Notes-about-TP-PP-DP/</id>
    <link href="http://liujunming.github.io/2026/05/24/Notes-about-TP-PP-DP/"/>
    <published>2026-05-24T11:55:46.000Z</published>
    <summary>
      <![CDATA[<p>本文将mark下TP(Tensor Parallelism)、PP(Pipeline Parallelism)和DP(Data Parallelism)的相关notes，本文内容转载自<a href="https://mp.weixin.qq.com/s/XPHJTQVWSLLVBqiHbwdMkw">不是多卡，而是多种“切法”：一文讲透 TP、PP、DP</a>。]]>
    </summary>
    <title>
      <![CDATA[Notes about TP && PP && DP]]>
    </title>
    <updated>2026-05-24T14:14:22.869Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="计算机网络" scheme="http://liujunming.github.io/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
    <category term="计算机网络" scheme="http://liujunming.github.io/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
    <content>
      <![CDATA[<p>本文将mark下INT(In-band Network Telemetry)的相关notes。<span id="more"></span></p><p><strong>telemetry</strong>: the science or process of collecting information about objects that are far away and sending the information somewhere electronically.</p><p>In-band Network Telemetry中文翻译:带内网络遥测</p><h2 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h2><p>In-band Network Telemetry (INT) is a network monitoring framework for collecting and reporting network status, which <strong>collected by a data forwarding plane without the intervention of control plane</strong>. In an INT-defined framework, the messages can carry INT instructions which can be interpreted by the intermediate forwarding device and filled in with the expected data, and then continue to be forwarded until the destination. INT could be able to observe flow pattern changes caused by microbursts, packet transmission delays, delays per node, and new ports in the flow path.</p><p><img src="/images/2026/05/013.png"></p><h2 id="Motivation"><a href="#Motivation" class="headerlink" title="Motivation"></a>Motivation</h2><p>Challenges encountered in traditional network</p><p><img src="/images/2026/05/017.png"></p><h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><p>通常，一个 INT 域包含三类核心功能节点：INT 源节点（INT Source）、INT 宿节点（INT Sink）、INT 中转节点（INT Transit Hop）。</p><p><img src="/images/2026/05/015.png"></p><p>对于遥测运维人员，需要被遥测的流量会在源节点添加 INT 头部，头部中包含指示采集信息的指令集（INT Instruction），从而成为 INT 报文。当报文经过 INT 中转节点时，设备会按照指令集将采集到的信息（INT 元数据）插入 INT 报文，最终在 INT 宿节点弹出所有 INT 信息，并上报至监控设备。</p><p>对用户而言，流量的 INT 处理全程透明，用户无需感知该过程。</p><p>详细流程如下：<br><img src="/images/2026/05/016.png"></p><ul><li>Host H1 sends a data packet to H2</li><li>SW1 inserts the INT header into the packet, the header source is SW1 and the sink is SW3</li><li>SW1’s instruction is to collect SW ID and forwarding delay</li><li>SW1 inserts its own ID and forwarding delay from ingress to exgress</li><li>the middle switch SW2, SW3 repeat this process</li><li>SW3 is the INT sink, which is responsible for summarizing the INT collection information and sending it to the report server.</li></ul><h2 id="HPCC"><a href="#HPCC" class="headerlink" title="HPCC"></a>HPCC</h2><p><a href="https://liyuliang001.github.io/publications/hpcc.pdf">SIGCOMM’19</a></p><p>传统的拥塞控制，如果交换机上遇到了拥塞，就等到拥塞信号发到对端去，然后再返回回来，然后这个时候再去降速。但是这带来一个问题，就是降速可能并不是特别准确。所以HPCC里面的HP就是high precision，高精确度，也就是说交换机上面它能够有一个很高的精确度，然后去判断到底它拥塞的程度是什么样子，那就是用的可编程交换机的INT（In-band Network Telemetry）的能力，然后它把拥塞程度反馈的更准确，这样它调整带宽的时候就调的更准确。</p><p><img src="/images/2026/05/014.png"></p><p>HPCC is a sender-driven CC framework. As shown in Figure 4, each packet a sender sends will be acknowledged by the receiver. During the propagation of the packet from the sender to the receiver, each switch along the path leverages the INT feature of its switching ASIC to insert some meta-data that reports the current load of the packet’s egress port, including timestamp (ts), queue length (qLen), transmitted bytes (txBytes), and the link bandwidth capacity (B).</p><p>When the receiver gets the packet, it copies all the meta-data recorded by the switches to the ACK message it sends back to the sender. The sender decides how to adjust its flow rate each time it receives an ACK with network load information.</p><hr><p>参考资料:</p><ol><li><a href="https://mp.weixin.qq.com/s/iwJ3aNApMk83sBDappX51Q">带内网络遥测 INT</a></li><li><a href="https://01.me/2023/09/network-intelligence/">网络的智能应该放在哪里：网卡、交换机还是 xPU</a></li><li><a href="https://cloudswit.ch/blogs/what-is-int%EF%BC%88in-band-network-telemetry%EF%BC%89/">What is INT (In-band Network Telemetry)</a></li><li><a href="https://nkatta.github.io/papers/int-hula.pdf">In-band Network Telemetry(INT)</a></li><li><a href="https://www.opencompute.org/files/INT-In-Band-Network-Telemetry-A-Powerful-Analytics-Framework-for-your-Data-Center-OCP-Final3.pdf">In-Band Network Telemetry - A Powerful Analytics Framework for your Data Center</a></li></ol>]]>
    </content>
    <id>http://liujunming.github.io/2026/05/10/Notes-about-INT-In-band-Network-Telemetry/</id>
    <link href="http://liujunming.github.io/2026/05/10/Notes-about-INT-In-band-Network-Telemetry/"/>
    <published>2026-05-10T02:48:54.000Z</published>
    <summary>
      <![CDATA[<p>本文将mark下INT(In-band Network Telemetry)的相关notes。]]>
    </summary>
    <title>Notes about INT(In-band Network Telemetry)</title>
    <updated>2026-05-10T03:45:02.871Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="经验" scheme="http://liujunming.github.io/categories/%E7%BB%8F%E9%AA%8C/"/>
    <category term="经验" scheme="http://liujunming.github.io/tags/%E7%BB%8F%E9%AA%8C/"/>
    <content>
      <![CDATA[<h2 id="DPU-Socket-Direct和Multi-host技术"><a href="#DPU-Socket-Direct和Multi-host技术" class="headerlink" title="DPU Socket-Direct和Multi-host技术"></a>DPU Socket-Direct和Multi-host技术</h2><span id="more"></span><p><a href="https://mp.weixin.qq.com/s/BE51x2wBc9tBZyaRjRqG0w">https://mp.weixin.qq.com/s/BE51x2wBc9tBZyaRjRqG0w</a></p><h3 id="Socket-Direct技术"><a href="#Socket-Direct技术" class="headerlink" title="Socket Direct技术"></a>Socket Direct技术</h3><p><img src="/images/2026/05/011.png"><br>其实就是DPU模式下的<a href="/2024/11/17/%E8%BD%AC%E8%BD%BD-Linux-NUMA-Optimization-1/#3-2-Device-NUMA-Affinity">Device NUMA Affinity</a></p><h3 id="Multi-host技术"><a href="#Multi-host技术" class="headerlink" title="Multi-host技术"></a>Multi-host技术</h3><p><img src="/images/2026/05/012.png"></p><p>多个host直接连接到一个集中式的DPU上，无需每台host单独配备DPU和交换机端口，实现“多机共用一卡”的高效架构。</p><h2 id="计算机科学专业第三次大衰退？原因：AI"><a href="#计算机科学专业第三次大衰退？原因：AI" class="headerlink" title="计算机科学专业第三次大衰退？原因：AI"></a>计算机科学专业第三次大衰退？原因：AI</h2><p><a href="https://mp.weixin.qq.com/s/i5ODa4S3FHj2GeIQlGLgBA">https://mp.weixin.qq.com/s/i5ODa4S3FHj2GeIQlGLgBA</a></p><h2 id="EROFS：为沙箱而生的高性能只读文件系统"><a href="#EROFS：为沙箱而生的高性能只读文件系统" class="headerlink" title="EROFS：为沙箱而生的高性能只读文件系统"></a>EROFS：为沙箱而生的高性能只读文件系统</h2><p>一个诞生于华为实验室、被Linux内核接纳、被 gVisor&#x2F;Kata&#x2F;containerd&#x2F;Nydus全面采用的现代文件系统——EROFS，正在悄悄改变容器与沙箱的启动速度。</p><p><a href="https://mp.weixin.qq.com/s/GLXS2ATuUTWZdQ0o2jVmzQ">https://mp.weixin.qq.com/s/GLXS2ATuUTWZdQ0o2jVmzQ</a></p><p>EROFS不是一个小众的”压缩文件系统”，它是 Linux内核中专为只读、高性能、安全隔离场景设计的现代文件系统。</p><p>从Android固件到Kubernetes容器，从gVisor安全沙箱到Kata微型虚拟机，EROFS已经成为云原生基础设施中最重要的隐形加速器之一。</p><h2 id="Manus交易失败了，但创始人依然在谈成功学"><a href="#Manus交易失败了，但创始人依然在谈成功学" class="headerlink" title="Manus交易失败了，但创始人依然在谈成功学"></a>Manus交易失败了，但创始人依然在谈成功学</h2><p><a href="https://mp.weixin.qq.com/s/32EK7T42kEjKefg9VV4Z3g">https://mp.weixin.qq.com/s/32EK7T42kEjKefg9VV4Z3g</a></p><h2 id="把-eBPF-带进-GPU-Kernel-内部"><a href="#把-eBPF-带进-GPU-Kernel-内部" class="headerlink" title="把 eBPF 带进 GPU Kernel 内部"></a>把 eBPF 带进 GPU Kernel 内部</h2><p><a href="https://mp.weixin.qq.com/s/Pm58fWQDwpqj1x3Df0XMmQ">把 eBPF 带进 GPU Kernel 内部：bpftime for GPU 的一次关键突破</a></p><p>bpftime for GPU 所代表的，并不只是一个新工具。<br>它更像是在提出一种新的可能性：</p><blockquote><p>如果 eBPF 可以深入 CPU 内核与用户态程序，那么它为什么不能继续进入 GPU kernel？</p></blockquote><p>通过 PTX 级插桩、per-thread 粒度观测、零拷贝数据回传以及标准 eBPF 工具链兼容，bpftime for GPU 正在把这个问题变成现实答案。</p><p>这意味着，我们第一次开始具备这样的能力：</p><ul><li>在 GPU kernel 内部布置可编程探针</li><li>以线程级精度观察执行行为</li><li>用 eBPF 的方式描述 GPU 可观测性</li><li>在不侵入业务代码的前提下，为 AI 与高性能计算场景提供更强的调试与分析能力</li></ul><p>随着 AI 时代 GPU 负载持续增长，这类能力的价值只会越来越大。</p><h2 id="Geoffrey-Hinton成为第二位引用量破百万的科学家"><a href="#Geoffrey-Hinton成为第二位引用量破百万的科学家" class="headerlink" title="Geoffrey Hinton成为第二位引用量破百万的科学家"></a>Geoffrey Hinton成为第二位引用量破百万的科学家</h2><p><a href="https://mp.weixin.qq.com/s/ib9TcSS9XvCoq9mC-XCSSg">https://mp.weixin.qq.com/s/ib9TcSS9XvCoq9mC-XCSSg</a></p><h2 id="天才少年姚顺雨入职腾讯后首发声：人与人差距在-AI-工具"><a href="#天才少年姚顺雨入职腾讯后首发声：人与人差距在-AI-工具" class="headerlink" title="天才少年姚顺雨入职腾讯后首发声：人与人差距在 AI 工具"></a>天才少年姚顺雨入职腾讯后首发声：人与人差距在 AI 工具</h2><p><a href="https://mp.weixin.qq.com/s/r_MLUX9pDggXz97oQOofDw">https://mp.weixin.qq.com/s/r_MLUX9pDggXz97oQOofDw</a></p><h2 id="英伟达CUDA迎来史上最大更新"><a href="#英伟达CUDA迎来史上最大更新" class="headerlink" title="英伟达CUDA迎来史上最大更新"></a>英伟达CUDA迎来史上最大更新</h2><p><a href="https://mp.weixin.qq.com/s/lDlVLpFHWwd-HE3Muaa-kQ">https://mp.weixin.qq.com/s/lDlVLpFHWwd-HE3Muaa-kQ</a></p><h2 id="高考最看重的能力，成了AI时代最不重要的能力"><a href="#高考最看重的能力，成了AI时代最不重要的能力" class="headerlink" title="高考最看重的能力，成了AI时代最不重要的能力"></a>高考最看重的能力，成了AI时代最不重要的能力</h2><p><a href="https://mp.weixin.qq.com/s/gLc3uK9ihcz8qK0OmerOhw">https://mp.weixin.qq.com/s/gLc3uK9ihcz8qK0OmerOhw</a></p><h2 id="斯坦福CS336：大模型从0到1"><a href="#斯坦福CS336：大模型从0到1" class="headerlink" title="斯坦福CS336：大模型从0到1"></a>斯坦福CS336：大模型从0到1</h2><p><a href="https://www.bilibili.com/video/BV13SV9zdEhX">https://www.bilibili.com/video/BV13SV9zdEhX</a></p>]]>
    </content>
    <id>http://liujunming.github.io/2026/05/10/%E6%AF%8F%E5%91%A8%E5%88%86%E4%BA%AB%E7%AC%AC39%E6%9C%9F/</id>
    <link href="http://liujunming.github.io/2026/05/10/%E6%AF%8F%E5%91%A8%E5%88%86%E4%BA%AB%E7%AC%AC39%E6%9C%9F/"/>
    <published>2026-05-10T02:22:33.000Z</published>
    <summary>
      <![CDATA[<h2 id="DPU-Socket-Direct和Multi-host技术"><a href="#DPU-Socket-Direct和Multi-host技术" class="headerlink" title="DPU Socket-Direct和Multi-host技术"></a>DPU Socket-Direct和Multi-host技术</h2>]]>
    </summary>
    <title>每周分享第39期</title>
    <updated>2026-05-10T12:58:54.833Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="GPU" scheme="http://liujunming.github.io/categories/GPU/"/>
    <category term="GPU" scheme="http://liujunming.github.io/tags/GPU/"/>
    <content>
      <![CDATA[<p>本文将mark下CUDA Green Context的相关notes。<span id="more"></span></p><h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><p>Green contexts are a lightweight alternative to traditional contexts, that can be used to select a subset of device resources. This allows the developer to, for example, select <a href="/2025/11/30/Notes-of-Basic-Concepts-in-GPU-Computing/#Hardware%E2%80%99s-View">SMs</a> from distinct spatial partitions of the GPU and target them via CUDA stream operations, kernel launches, etc.</p><p>CUDA 中的 Green Context 是一种轻量级的上下文形式，可作为传统 CUDA 上下文的替代方案，为开发者提供更细粒度的 GPU 空间划分与资源分配能力。</p><p>Green Context 使用户能够定义和管理 GPU 资源的独立分区，主要是Streaming Multiprocessors（SM)。你可以将特定数量的 SM 分配给某个特定的 Green Context ，然后在该 context 所拥有的资源范围内启动 CUDA kernel 并管理只在此 context 内运行的 stream。</p><h2 id="技术定位"><a href="#技术定位" class="headerlink" title="技术定位"></a>技术定位</h2><p>Green Contexts 介于 CUDA Streams（动态无分区）和 MPS（有分区但动态不足）之间，支持<strong>单进程内动态 SM 资源分区</strong>，提供确定性的非对称执行能力。</p><p>Green Context enables lightweight <strong>intra-process</strong> spatial sharing. Briefly, we can create multiple CUDA streams with dedicated SM allocations for concurrent GPU kernels.</p><p><img src="/images/2026/05/009.png"></p><p>Green Context解决了GPU资源调度的难问题——把”碰运气式”并行升级为”精准规划”。它通过预分配SM资源池，让关键任务能独占专属算力，既保证低延迟响应，又避免多任务互相干扰。</p><h2 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h2><p>一个典型的应用场景是：你的程序中有部分代码对延迟极为敏感，并且需要优先于其他所有 GPU 工作执行。通过为这段代码单独创建一个 Green Context 并分配 SM 资源，而将剩余的 SM 分配给另一个 Green Context 处理其他任务，你就能确保始终有可用的 SM 供高优先级计算使用。</p><p>ASPLOS’26的paper: <a href="https://dl.acm.org/doi/abs/10.1145/3779212.3790236">Towards High-Goodput LLM Serving with Prefill-decode Multiplexing</a>，就是基于Green Context做的工作，blog可参见<a href="https://www.lmsys.org/blog/2025-09-28-pdmux/">PD-Multiplexing: Unlocking High-Goodput LLM Serving with GreenContext</a>。</p><h2 id="创建与使用流程"><a href="#创建与使用流程" class="headerlink" title="创建与使用流程"></a>创建与使用流程</h2><p><img src="/images/2026/05/010.png"></p><ol><li>查询 GPU 的 SM 数量（Blackwell 为 160）</li><li>制定资源分区策略</li><li>创建分区描述符</li><li>生成 Green Context 沙箱</li><li>从 Context 创建 CUDA Stream</li><li>正常向 Stream 提交核函数</li></ol><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>Green Context 的核心价值，不是简单地“限制 kernel 能用多少个 SM”，而是<u>给 CUDA 提供了一种更可控的资源管理方式</u>。它把原本机会式的并行，进一步变成了可规划的并行。通过把 SM资源按 context 进行划分，应用可以在<strong>低时延、吞吐和隔离性</strong>之间做更灵活的权衡。</p><p>对需要保障关键任务响应时间、减少多 stream 相互干扰、或验证不同资源配比效果的场景来说，Green Context 提供了一种几乎不需要修改 kernel、只需少量 host 侧改造就能落地的新手段。</p><hr><p>参考资料:</p><ol><li><a href="https://mp.weixin.qq.com/s/lDlVLpFHWwd-HE3Muaa-kQ">刚刚，英伟达CUDA迎来史上最大更新！</a></li><li><a href="https://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__GREEN__CONTEXTS.html#group__CUDA__GREEN__CONTEXTS">Green Contexts</a></li><li><a href="https://zhuanlan.zhihu.com/p/1928880421606753954">简单了解下CUDA Green Context</a></li><li><a href="https://cloud.tencent.com/developer/article/2653795">一次看懂：CUDA 最新技术与未来 3 年路线图</a></li><li><a href="https://mp.weixin.qq.com/s/daNWAWVamLa7GCI9ug28Tw">cuda13新特性green context</a></li><li><a href="https://mp.weixin.qq.com/s/IPk6P-DzTr2c6KDJVOFdWg">PD 多路复用：SGLang 借助 NVIDIA GreenContext 释放高有效吞吐量的大语言模型服务能力</a></li></ol>]]>
    </content>
    <id>http://liujunming.github.io/2026/05/05/Notes-about-CUDA-Green-Context/</id>
    <link href="http://liujunming.github.io/2026/05/05/Notes-about-CUDA-Green-Context/"/>
    <published>2026-05-05T10:59:21.000Z</published>
    <summary>
      <![CDATA[<p>本文将mark下CUDA Green Context的相关notes。]]>
    </summary>
    <title>Notes about CUDA Green Context</title>
    <updated>2026-05-05T12:53:23.622Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="AI Infra" scheme="http://liujunming.github.io/categories/AI-Infra/"/>
    <category term="AI Infra" scheme="http://liujunming.github.io/tags/AI-Infra/"/>
    <content>
      <![CDATA[<p>本文将mark下AI分布式训练通信原语的相关notes。<span id="more"></span>内容主要转载自<a href="https://mp.weixin.qq.com/s?__biz=Mzk2NDEyMTM1Mg==&mid=2247488493&idx=1&sn=265f12c2ff6ba43b5c9283724aeff550&scene=21&poc_token=HEfc82mjy30qeDoABDAFRwIy3BlLPJnhv7t6AFwi">GPU售前知识—AI分布式训练通信原语</a></p><p>在大型AI模型训练中，单机单GPU的配置已无法满足算力与内存的需求。随着模型参数规模的不断扩大，分布式训练已成为深度学习发展的必由之路。而支撑分布式训练最基础的能力正是分布式训练通信原语（Communication Primitives）。</p><h2 id="分布式训练通信原语的概念"><a href="#分布式训练通信原语的概念" class="headerlink" title="分布式训练通信原语的概念"></a>分布式训练通信原语的概念</h2><p>“通信原语”并非指某个具体的框架或API，而是指各种分布式系统中高度抽象、反复出现的基础通信与协同操作。它们定义了在训练过程中多设备、多进程之间如何交换数据、同步状态、维持一致性。无论你使用PyTorch、TensorFlow还是JAX，也无论采用数据并行、模型并行还是混合并行策略，底层实现都依赖这些AI分布式训练通信原语的高效实现。可以说，通信原语的性能直接决定了分布式训练效率的上限。</p><h2 id="常见分布式训练通信原语详解"><a href="#常见分布式训练通信原语详解" class="headerlink" title="常见分布式训练通信原语详解"></a>常见分布式训练通信原语详解</h2><p>基于实际训练中最常见的通信模式，我们在此介绍八种核心AI训练通信原语：广播Broadcast、分散Scatter、聚合Gather、全聚合AllGather、规约Reduce、规约分散ReduceScatter、全规约AllReduce以及全交换All-to-All。</p><h3 id="广播Broadcast"><a href="#广播Broadcast" class="headerlink" title="广播Broadcast"></a>广播Broadcast</h3><ol><li>广播Broadcast是一对多通信原语，即一个发送者对应多个接收者。在集群中，某个节点（或GPU）将其数据广播至所有其他节点。例如将GPU0的数据同步至GPU1、GPU2与GPU3。</li></ol><p><img src="/images/2026/05/001.png"></p><ol start="2"><li>广播Broadcast是一对多数据同步机制，能将一个GPU的数据同步至所有其他GPU。其应用场景包括：</li></ol><ul><li>并行数据参数初始化，确保所有GPU的初始参数一致性；</li><li>AllReduce组合（广播Broadcast+规约Reduce）中的广播操作；</li><li>分布式训练参数服务器架构中，主节点向工作节点广播Broadcast数据，再由工作节点将数据规约Reduce回主节点的广播环节。</li></ul><h3 id="分散Scatter"><a href="#分散Scatter" class="headerlink" title="分散Scatter"></a>分散Scatter</h3><ol><li>分散Scatter与广播Broadcast虽同属一对多通信原语，但存在本质区别。分散与广播的核心差异在于：</li></ol><ul><li>广播Broadcast：向所有节点发送<strong>相同的完整数据</strong>。</li><li>分散Scatter：将数据<strong>拆分后分别发送</strong>给不同节点。</li></ul><ol start="2"><li>例如，将一份数据DATA拆分为DATA-A、DATA-B、DATA-C与DATA-D：</li></ol><ul><li>DATA-A → GPU0</li><li>DATA-B → GPU1</li><li>DATA-C → GPU2</li><li>DATA-D → GPU3</li></ul><p><img src="/images/2026/05/002.png"></p><ol start="3"><li>分散Scatter的逆操作是聚合Gather，其典型应用场景包括：</li></ol><ul><li>规约分散ReduceScatter组合中的分散Scatter操作；</li><li>模型并行中，初始化时模型参数分散至不同GPU的环节。</li></ul><h3 id="聚合Gather"><a href="#聚合Gather" class="headerlink" title="聚合Gather"></a>聚合Gather</h3><ol><li>聚合Gather是多对一通信原语，即多个数据发送者对应一个数据接收者。它将集群中多个节点的数据汇集到单个节点上。各节点将自身数据发送至主节点，聚合完成后主节点即拥有所有节点的数据。例如：</li></ol><ul><li>GPU0 → DATA-A</li><li>GPU1 → DATA-B</li><li>GPU2 → DATA-C</li><li>GPU3 → DATA-D</li><li>最终主节点将获得完整的DATA-A&#x2F;B&#x2F;C&#x2F;D数据集。</li></ul><p><img src="/images/2026/05/003.png"></p><ol start="2"><li>聚合Gather是多对一数据收集方法，将多张GPU卡的数据收集到单张GPU卡上。其逆操作对应分散Scatter，应用场景即规约分散ReduceScatter组合中的分散Scatter环节。</li></ol><h3 id="全聚合AllGather"><a href="#全聚合AllGather" class="headerlink" title="全聚合AllGather"></a>全聚合AllGather</h3><ol><li>全聚合AllGather是多对多通信原语，实现多张GPU卡之间的数据同步全收集。可将其视作聚合与广播操作的组合，即：</li></ol><ul><li>首先，将各节点数据收集至主节点（Gather）；</li><li>随后，将完整数据广播至所有节点（Broadcast）。</li><li>最终Allgather的结果是每个节点都拥有完整数据。</li></ul><p><img src="/images/2026/05/004.png"></p><ol start="2"><li>全聚合AllGather的逆操作是规约分散ReduceScatter，其典型应用场景包括：</li></ol><ul><li>并行模型训练场景；</li><li>在并行模型训练中，前向计算所需的参数需要全量同步，此时需要通过AllGather将拆分在不同GPU上的参数同步到单张GPU上才能进行前向计算。</li></ul><h3 id="规约Reduce"><a href="#规约Reduce" class="headerlink" title="规约Reduce"></a>规约Reduce</h3><ol><li>规约（Reduce）是多对一通信原语，用于将多个节点的数据进行归约Reduce计算并将结果汇总至单个节点。常见的规约Reduce操作包括：</li></ol><ul><li>求和（SUM）</li><li>求积（PROD）</li><li>最大值&#x2F;最小值（MAX&#x2F;MIN）</li><li>逻辑与&#x2F;或（LAND&#x2F;LOR）</li><li>按位与&#x2F;或&#x2F;异或（BAND&#x2F;BOR&#x2F;BXOR）</li><li>最大值位置&#x2F;最小值位置（MAXLOC&#x2F;MINLOC）</li></ul><p><img src="/images/2026/05/005.png"></p><ol start="2"><li><p>这些规约Reduce操作通常需要GPU或加速卡硬件支持对应的算子（operator）才能实现高性能。</p></li><li><p>规约Reduce的典型应用场景包括：</p></li></ol><ul><li>全规约AllReduce中的规约Reduce环节；</li><li>规约分散ReduceScatter组合通信中的规约Reduce操作；</li><li>分布式训练参数服务器架构中，Master节点先将模型参数广播Broadcast至所有Worker节点，各Worker完成计算后通过规约Reduce操作将结果聚合回Master节点。</li></ul><h3 id="规约分散ReduceScatter"><a href="#规约分散ReduceScatter" class="headerlink" title="规约分散ReduceScatter"></a>规约分散ReduceScatter</h3><ol><li>规约分散ReduceScatter是多对多通信原语，具有多个数据发送者与多个数据接收者。它在集群所有节点上沿相同维度执行规约Reduce操作，随后将结果分发至集群每个节点。规约分散ReduceScatter等效于在每个节点上执行一次规约Reduce操作后，再进行一次分散Scatter分发操作。其逆操作是全聚合AllGather。</li></ol><p><img src="/images/2026/05/006.png"></p><ol start="2"><li>规约分散ReduceScatter是多对多数据操作，采用”先规约Reduce后分发”的方式。它首先对各GPU卡上的数据进行规约（例如求和），再将数据分发（分散Scatter）处理。其应用场景包括：</li></ol><ul><li>规约分散ReduceScatter可同时适用于数据并行与模型并行场景：</li><li>数据并行中规约分散ReduceScatter+全聚合AllGather组合内的规约分散ReduceScatter操作；</li><li>模型并行中前向AllGather后反向计算中的规约分散ReduceScatter环节。</li></ul><h3 id="全规约AllReduce"><a href="#全规约AllReduce" class="headerlink" title="全规约AllReduce"></a>全规约AllReduce</h3><ol><li>全规约AllReduce是多对多通信原语，具有多个数据发送者与多个数据接收者。它在集群所有节点上执行相同的规约Reduce操作，并将各节点的规约结果发送至每个节点。全规约AllReduce可通过在主节点执行规约Reduce+广播Broadcast或通过规约分散ReduceScatter+全聚合AllGather组合实现。</li></ol><p><img src="/images/2026/05/007.png"></p><ol start="2"><li>全规约AllReduce是多对多数据规约操作，将集群中所有GPU的数据规约（如求和）后分发至各GPU。其应用场景包括：</li></ol><ul><li>AllReduce可适用于数据并行场景；</li><li>各类通信拓扑中的数据并行场景，例如环式全规约（Ring AllReduce）与树状全规约（Tree AllReduce）中的全规约AllReduce实现。</li></ul><h3 id="全交换All-to-All"><a href="#全交换All-to-All" class="headerlink" title="全交换All-to-All"></a>全交换All-to-All</h3><ol><li>全交换All-to-All操作中，每个节点将数据分散至集群所有节点，同时各节点也从集群所有节点收集数据。全交换All-to-All是全聚合AllGather的扩展形式，其区别在于：全聚合AllGather中不同节点从给定节点收集相同数据，而全交换All-to-All中不同节点从给定节点收集不同数据，本质上实现了数据转置操作。</li></ol><p><img src="/images/2026/05/008.png"></p><ol start="2"><li>全交换All-to-All是多对多转置操作，将数据从所有GPU卡转置至集群中每张GPU卡。其主要应用场景包括：</li></ol><ul><li>全交换All-to-All适用于模型并行场景；</li><li>模型并行中的矩阵转置环节；</li><li>从数据并行到模型并行的矩阵转置过程。</li></ul><h2 id="从通信原语到通信库：以NCCL为例"><a href="#从通信原语到通信库：以NCCL为例" class="headerlink" title="从通信原语到通信库：以NCCL为例"></a>从通信原语到通信库：以NCCL为例</h2><p>NCCL（NVIDIA Collective Communications Library）是为GPU间通信提供原语的库。它具有拓扑感知能力，可轻松集成到各类应用中。NCCL实现了集体通信与点对点收发原语，并非完整的并行编程框架，而是专注于加速GPU间通信的工具包。</p><p>NCCL提供的集体通信原语包括AllReduce、Broadcast、Reduce、AllGather、ReduceScatter、AlltoAll、Gather与Scatter。此外，NCCL还支持点对点收发通信，可灵活实现Scatter、Gather或All-to-All操作。</p><p>通过NCCL，通信原语在概念层面被转化为可用的库函数。在传统CUDA编程中，集体通信通常通过多次CUDA内存拷贝操作与本地规约计算内核组合实现；NCCL将每个集体通信原语封装在单个内核中，同步处理通信与计算，从而实现更高效的同步并降低资源开销。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>在分布式深度学习训练中，训练框架本身通常不会直接操作底层通信网络，而是通过高性能通信库完成参数同步、梯度规约等操作。这些通信库既屏蔽了复杂的硬件细节，又提升了训练性能。不同的AI芯片和加速卡厂商会提供专属的通信库或扩展方案，例如硬件感知的MPI实现或自研CCL库，以优化底层网络的使用。</p><p>实际分布式训练集群中的网络互联形态极其多样：既可能采用以太网、InfiniBand、RoCE v1&#x2F;v2等通用协议，也可能使用NVLink等专用或半专用高速互联协议。这就需要通信库能够结合厂商提供的SDK和接口，针对特定硬件进行优化，例如CUDA-aware MPI、NCCL、NVSHMEM等。同时，还需要根据实际的网络拓扑和节点布局，选择最合适的通信策略与算法。</p><hr><p>参考资料:</p><ol><li><a href="https://mp.weixin.qq.com/s?__biz=Mzk2NDEyMTM1Mg==&mid=2247488493&idx=1&sn=265f12c2ff6ba43b5c9283724aeff550&scene=21&poc_token=HEfc82mjy30qeDoABDAFRwIy3BlLPJnhv7t6AFwi">GPU售前知识—AI分布式训练通信原语</a></li><li><a href="https://developer.nvidia.com/blog/fast-multi-gpu-collectives-nccl/">Fast Multi-GPU collectives with NCCL</a></li><li><a href="https://naddod.medium.com/in-depth-understanding-of-ai-distributed-training-communication-primitives-eb3b5fcc1f07">In-Depth Understanding of AI Distributed Training Communication Primitives</a></li></ol>]]>
    </content>
    <id>http://liujunming.github.io/2026/05/01/Notes-about-AI%E5%88%86%E5%B8%83%E5%BC%8F%E8%AE%AD%E7%BB%83%E9%80%9A%E4%BF%A1%E5%8E%9F%E8%AF%AD/</id>
    <link href="http://liujunming.github.io/2026/05/01/Notes-about-AI%E5%88%86%E5%B8%83%E5%BC%8F%E8%AE%AD%E7%BB%83%E9%80%9A%E4%BF%A1%E5%8E%9F%E8%AF%AD/"/>
    <published>2026-05-01T09:37:34.000Z</published>
    <summary>
      <![CDATA[<p>本文将mark下AI分布式训练通信原语的相关notes。]]>
    </summary>
    <title>Notes about AI分布式训练通信原语</title>
    <updated>2026-05-01T10:54:14.455Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="体系结构" scheme="http://liujunming.github.io/categories/%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84/"/>
    <category term="体系结构" scheme="http://liujunming.github.io/tags/%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84/"/>
    <content>
      <![CDATA[<p>本文将mark下PIM(Processing-In-Memory)和PNM(Processing-Near-Memory)的相关notes。<span id="more"></span></p><h2 id="Overview"><a href="#Overview" class="headerlink" title="Overview"></a>Overview</h2><p>PIM和PNM是两种用于突破“存储墙”瓶颈的计算架构，旨在通过<strong>将计算能力与内存紧密结合来提高计算效率，减少数据在处理器和内存之间的传输延迟和带宽瓶颈</strong>。</p><p><img src="/images/2026/04/023.png"></p><p>As shown in Figure 3 (a), PIM places a processing unit (PU) alongside each memory bank so that parallel computations can be implemented across all memory banks, exploiting the high internal memory bandwidth. A PU can be a MAC (Multiply and Accumulate) unit, a compute core, or a database query processor, specialized in workloads with low arithmetic intensity. However, due to the constraints stemming from the memory process technology applied for the memory core die, PIM logic faces inherent limitations in supporting more complex computations, thereby restricting it to wimpy processing units. On the other hand, as depicted in Figure 3 (b), PNM places processing modules not in the memory core but rather on the data read path between the host processor and the memory node or local main memory. Similar to PIM, PNM is also suitable for workloads with low arithmetic intensity. However, since PNM logic is fabricated using standard logic process nodes, it allows for a broader range of supported operations. Thus, one can fuse multiple kernels into a single module,and exploit high aggregate bandwidth for task-specific computations.</p><h2 id="PIM-存内处理"><a href="#PIM-存内处理" class="headerlink" title="PIM(存内处理)"></a>PIM(存内处理)</h2><p>PIM是<strong>指将计算单元直接集成到内存芯片内部，使得数据可以在存储位置直接进行处理</strong>。通过这种方式，PIM减少了数据在处理器（如CPU、GPU）和内存之间的传输，降低了延迟和能耗。</p><p>优势:</p><ul><li>高带宽利用：内存内的计算能够充分利用内存的高带宽优势，因为计算和数据都在内存内部完成。</li><li>低延迟：减少数据在内存和处理器之间的往返传输，降低整体延迟。</li><li>节能：通过减少数据移动，PIM能够显著降低系统能耗。</li></ul><h2 id="PNM-近存计算"><a href="#PNM-近存计算" class="headerlink" title="PNM(近存计算)"></a>PNM(近存计算)</h2><p>PNM是将计算单元放置在内存附近的芯片上，而不是直接集成在内存内部。虽然不如PIM直接在内存中处理数据，但PNM仍然通过将处理器靠近内存来减少数据传输的延迟。</p><p>相比于PIM，PNM可以在更广泛的硬件配置中实现，因为计算单元和内存是物理分开的，易于升级和扩展。</p><h2 id="PIM-vs-PNM"><a href="#PIM-vs-PNM" class="headerlink" title="PIM vs PNM"></a>PIM vs PNM</h2><ul><li>PIM (Processing-in-Memory, 存内计算): 计算逻辑和存储单元在<strong>同一个die</strong>上.</li><li>PNM (Processing-Near-Memory, 近存计算): 计算逻辑和存储单元在<strong>不同但邻近的die</strong>上.</li></ul><p>下表总结了PIM和PNM在数据中心LLM推理场景的对比:</p><table><thead><tr><th align="left"></th><th align="left">PIM</th><th align="left">PNM</th><th align="left">胜出者</th></tr></thead><tbody><tr><td align="left">数据移动功耗</td><td align="left">非常低 (片上)</td><td align="left">低 (片外但邻近)</td><td align="left">PIM</td></tr><tr><td align="left">带宽(每瓦)</td><td align="left">非常高 (标准的5-10倍)</td><td align="left">高 (标准的2-5倍)</td><td align="left">PIM</td></tr><tr><td align="left">内存-逻辑耦合</td><td align="left">内存和逻辑在同一个die上</td><td align="left">内存和逻辑在分离的die上</td><td align="left">PNM</td></tr><tr><td align="left">逻辑PPA</td><td align="left">在DRAM工艺下逻辑更慢, 功耗更高</td><td align="left">逻辑工艺有助于性能, 功耗和面积</td><td align="left">PNM</td></tr><tr><td align="left">内存密度</td><td align="left">更差, 因为与逻辑共享面积</td><td align="left">不受影响</td><td align="left">PNM</td></tr><tr><td align="left">商品化内存定价</td><td align="left">否. 量小, 供应商少, 密度低</td><td align="left">是. 不受影响</td><td align="left">PNM</td></tr><tr><td align="left">功耗&#x2F;散热预算</td><td align="left">逻辑在内存die上预算紧张</td><td align="left">逻辑受限较小</td><td align="left">PNM</td></tr><tr><td align="left">软件分片</td><td align="left">需要为内存bank (如32–64 MB) 分片</td><td align="left">分片限制较小 (如16–32 GB)</td><td align="left">PNM</td></tr></tbody></table><h2 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h2><p>针对数据中心LLM <strong>PNM优于PIM</strong>: 尽管PIM在理论带宽和功耗上更有优势, 但PNM在实际应用中更胜一筹.</p><ul><li>软件分片(Sharding): PIM要求将数据切分到极小的内存bank(如32-64MB), 这对于LLM复杂的内存结构来说非常困难. PNM的分片粒度可以大得多(如16-32GB), 易于实现.</li><li>逻辑PPA (性能, 功耗, 面积): PNM的计算逻辑可以使用先进的逻辑工艺制造, PPA更优. 而PIM的计算逻辑受限于DRAM工艺, 性能较差, 功耗较高.</li><li>内存密度与成本: PNM不影响标准内存的密度和商品化定价.</li><li>功耗&#x2F;散热预算: PIM中计算逻辑的功耗和散热受到DRAM die的严格限制.</li></ul><hr><p>参考资料:</p><ol><li><a href="https://mp.weixin.qq.com/s/sRXZU4B_Tb3JHPjwsccP8Q">大语言模型推理硬件的挑战与研究方向</a></li><li><a href="https://mp.weixin.qq.com/s/q8ZJ0K4XwhOI1msK1e3ruA">科普专栏｜PIM PNM对OpenAI O1有多么重要？</a></li><li>Accelerating Retrieval Augmented Language Model via PIM and PNM Integration(MICRO’25)</li><li><a href="https://mp.weixin.qq.com/s/lkaKPrB4jX6_e4pCxzndaw">PIM Is All You Need: A CXL-Enabled GPU-Free System for Large Language Model Inference</a></li><li><a href="https://events.safari.ethz.ch/micro24-memorycentric-tutorial/lib/exe/fetch.php?media=geraldo-micro24-lecture2-processing-near-memory-beforelecture.pdf">Processing-Near-Memory Systems: Developments fro Academia &amp; Industry</a></li><li><a href="https://chat.deepseek.com/share/wlf4c0qgxmv4lysv0x">deepseek对话</a></li></ol>]]>
    </content>
    <id>http://liujunming.github.io/2026/04/26/Notes-about-PIM-Processing-In-Memory-and-PNM-Processing-Near-Memory/</id>
    <link href="http://liujunming.github.io/2026/04/26/Notes-about-PIM-Processing-In-Memory-and-PNM-Processing-Near-Memory/"/>
    <published>2026-04-26T10:10:33.000Z</published>
    <summary>
      <![CDATA[<p>本文将mark下PIM(Processing-In-Memory)和PNM(Processing-Near-Memory)的相关notes。]]>
    </summary>
    <title>Notes about PIM and PNM</title>
    <updated>2026-04-27T00:14:37.562Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="PCI&amp;PCIe" scheme="http://liujunming.github.io/categories/PCI-PCIe/"/>
    <category term="PCI&amp;PCIe" scheme="http://liujunming.github.io/tags/PCI-PCIe/"/>
    <content>
      <![CDATA[<p>简单来说，Flit Mode（Flow Control Unit）将传统模式下的<strong>不定长</strong>数据包，转变为<strong>固定长度</strong>(256字节)的数据包进行传输。<span id="more"></span></p><p>Flit Mode, PAM4(Pulse Amplitude Modulation with 4 levels), and FEC(Forward Error Correction)这三者构成了 PCIe 6.0 能倍增速率的核心“铁三角”。可以这样理解它们的关系：<strong>PAM4 是提速的“发动机”，FEC 是处理高误码的“安全网”，而 Flit Mode 则是为了让 FEC 能顺利工作的“标准化集装箱”</strong>。</p><p>它们是一条环环相扣的因果链：</p><h2 id="起点：PAM4-提速，但引入了严重的误码"><a href="#起点：PAM4-提速，但引入了严重的误码" class="headerlink" title="起点：PAM4 提速，但引入了严重的误码"></a>起点：PAM4 提速，但引入了严重的误码</h2><p>为了将传输速率从前代的 32 GT&#x2F;s 翻倍至 64 GT&#x2F;s，PCIe 6.0 将信号调制方式从 NRZ（1比特&#x2F;时钟周期）换成了 PAM4（2比特&#x2F;时钟周期）。代价是信号幅度被分成4级，对噪声更敏感，导致<strong>位错误率（BER）急剧上升</strong>。</p><h2 id="对策：必须用-FEC-主动纠错"><a href="#对策：必须用-FEC-主动纠错" class="headerlink" title="对策：必须用 FEC 主动纠错"></a>对策：必须用 FEC 主动纠错</h2><p>在如此高的错误率下，如果仍沿用传统的“错了就整包重传”机制，链路大部分时间都将浪费在无效的重传上，实际有效带宽可能不升反降。</p><p>因此，必须引入FEC。它在发送端加入校验数据，让接收端能<strong>直接在原地纠正大部分错误</strong>，只有遇到无法纠正的少量错误时才请求重传，从而将有效带宽保持在极高水平。</p><h2 id="条件：FEC-需要定长数据块，催生了-Flit"><a href="#条件：FEC-需要定长数据块，催生了-Flit" class="headerlink" title="条件：FEC 需要定长数据块，催生了 Flit"></a>条件：FEC 需要定长数据块，催生了 Flit</h2><p>FEC 算法(如低密度奇偶校验 LDPC)必须工作在<strong>固定长度的数据块</strong>上，通过计算整个数据块的校验位来纠错。而传统 PCIe 的 TLP长度是可变的，短至几个字节，长至几千字节，完全无法匹配 FEC 的要求。</p><p>于是，Flit应运而生。它把所有数据都打包进**固定的 256 字节“集装箱”**里，FEC 就能以此为基本单位进行编解码和纠错。Flit Mode 便是围绕这种固定包结构建立的全新数据交换规则。</p><h2 id="协同工作的简化流程"><a href="#协同工作的简化流程" class="headerlink" title="协同工作的简化流程"></a>协同工作的简化流程</h2><p>一条数据在 PCIe 6.0 链路中的旅程如下：</p><ol><li>打包：各种不定长的 TLP 被按照 256 字节标准封装进 Flit</li><li>编码纠错：每个 Flit 被加上 FEC 校验位，信号以 PAM4 形式高速发出</li><li>接收纠错：接收端对 PAM4 信号采样，并利用 FEC 对每个 Flit 进行纠错<ul><li>多数情况：FEC 成功纠正了 PAM4 传输引入的错误，数据直接通过</li><li>极少数情况：错误超出了 FEC 能力，则触发选择性重传，只重传出问题的那个 Flit</li></ul></li></ol><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li>PAM4 是“因”：提速需求催生了 PAM4，也带来了高误码这个必须解决的技术挑战。</li><li>FEC 是“核心手段”：为对抗高误码而引入的根本性纠错方案。</li><li>Flit 是“必须的结构”：为满足 FEC 对定长数据块的要求而设计的新一代数据传输格式。</li></ul><p>三者共同将 PCIe 6.0 打造成了一个速度极快、纠错极强且效率极高的互连通道。</p><hr><p>参考资料:</p><ol><li><a href="https://pcisig.com/blog/pcie%C2%AE-60-specification-webinar-qa-deeper-dive-flit-mode-pam4-and-forward-error-correction-fec">A Deeper Dive into FLIT Mode, PAM4, and Forward Error Correction (FEC)</a></li><li><a href="https://chat.deepseek.com/share/6gy0mgd8v8obhkp381">deepseek对话</a></li></ol>]]>
    </content>
    <id>http://liujunming.github.io/2026/04/25/Notes-about-PCIe-flit-mode/</id>
    <link href="http://liujunming.github.io/2026/04/25/Notes-about-PCIe-flit-mode/"/>
    <published>2026-04-25T14:17:26.000Z</published>
    <summary>
      <![CDATA[<p>简单来说，Flit Mode（Flow Control Unit）将传统模式下的<strong>不定长</strong>数据包，转变为<strong>固定长度</strong>(256字节)的数据包进行传输。]]>
    </summary>
    <title>Notes about PCIe Flit Mode, PAM4, and FEC</title>
    <updated>2026-06-27T11:55:59.207Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="PCI&amp;PCIe" scheme="http://liujunming.github.io/categories/PCI-PCIe/"/>
    <category term="PCI&amp;PCIe" scheme="http://liujunming.github.io/tags/PCI-PCIe/"/>
    <content>
      <![CDATA[<p>本文将mark下PCIe domain和segment的相关notes。<span id="more"></span>内容转载自<a href="https://mp.weixin.qq.com/s/n18cF9zP7WzCKJFLDQypmA">PCIe domain和segment介绍</a></p><p>PCIe Segment（段）是一个硬件层面的概念，指一组完全独立的PCIe总线域；而PCIe Domain（域）是Linux内核在软件层面为每个Segment分配的编号。</p><ul><li>PCIe Segment：是硬件概念。它代表一个完全独立的<strong>PCIe总线树，由独立的Root Complex（根复合体）管理</strong>。不同Segment之间的地址空间和配置空间是互相隔离的。多Segment常见于大型服务器，用于突破单个总线域只能有256个总线的限制。</li><li>PCIe Domain：是Linux内核中的软件表示。内核使用域号来区分不同的硬件Segment。因此，可以在Linux的lspci -t命令的输出中看到“Domain: Bus: Device. Function”的格式，这个Domain号实际上对应的就是硬件Segment。</li></ul><p><img src="/images/2026/04/022.png"></p><p>Segment和Domain的核心区别在于：一个Segment是物理上独立的总线树，而一个Domain则是内核给这个树分配的软件编号。举个例子，如果一台服务器有4个物理的PCIe Segment，在Linux中我们就会看到4个Domain（通常编号为0、1、2、3）。</p><p><strong>一个系统中的每个PCIe 设备使用Domain: Bus: Device. Function来唯一标识</strong>，具体含义与取值范围如下：</p><table><thead><tr><th align="left">标识符</th><th align="left">名称</th><th align="left">含义</th><th align="left">范围</th></tr></thead><tbody><tr><td align="left">Domain</td><td align="left">域号</td><td align="left">区分不同主机&#x2F;根复合体（Host Bridge）。</td><td align="left">0000 - FFFF</td></tr><tr><td align="left">Bus</td><td align="left">总线号</td><td align="left">设备所连接的总线号（总线 0 为起点）。</td><td align="left">00 - FF</td></tr><tr><td align="left">Device</td><td align="left">设备号</td><td align="left">总线上的设备（槽位 &#x2F; 逻辑设备），最多 32 个。</td><td align="left">00 - 1F</td></tr><tr><td align="left">Function</td><td align="left">功能号</td><td align="left">物理设备上的子功能（例如多功能网卡）。</td><td align="left">0 - 7</td></tr></tbody></table>]]>
    </content>
    <id>http://liujunming.github.io/2026/04/25/Notes-about-PCIe-domain%E5%92%8Csegment/</id>
    <link href="http://liujunming.github.io/2026/04/25/Notes-about-PCIe-domain%E5%92%8Csegment/"/>
    <published>2026-04-25T12:05:08.000Z</published>
    <summary>
      <![CDATA[<p>本文将mark下PCIe domain和segment的相关notes。]]>
    </summary>
    <title>Notes about PCIe domain和segment</title>
    <updated>2026-04-25T12:17:06.923Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="经验" scheme="http://liujunming.github.io/categories/%E7%BB%8F%E9%AA%8C/"/>
    <category term="经验" scheme="http://liujunming.github.io/tags/%E7%BB%8F%E9%AA%8C/"/>
    <content>
      <![CDATA[<h2 id="Prefill-as-a-Service"><a href="#Prefill-as-a-Service" class="headerlink" title="Prefill-as-a-Service"></a>Prefill-as-a-Service</h2><p>核心是将PD分离放在两个或多个Datacenter中的异构GPU集群。</p><p><img src="/images/2026/04/020.png"><span id="more"></span></p><ul><li>跨集群边界将Prefill从Decode中分离出来，通过将Prefill迁移到更快的计算硬件，可以降低长上下文请求的成本和延迟</li><li>这个问题的核心障碍在于 KVCache传输量太大。不过最近的混合注意力（Hybrid-attention）架构从根本上改变了这一局面，大幅减少了 KVCache的增长，通常降低了一个数量级</li><li>在真实的场景下由于请求到达的突发性和长短的不均衡，全部通过跨机房的专线传输还是很容易出现拥塞。 系统上我们需要尽可能识别值得传的 KVCache，从而实现跨机房异构 PD 不仅是更便宜，TTFT 还更低这一目标</li><li>最基本的原则就包括短的 incremental prefill 就在本地消化，长的才分到远端 prefill，但实际还需要进一步考虑网络拥塞和不同机房 KVCache 复用命中的情况</li></ul><p><a href="https://mp.weixin.qq.com/s/leWZtUDEh8jteB27F6XkQA">Prefill-as-a-Service：KVCache的跨数据中心传输</a><br><a href="https://mp.weixin.qq.com/s/9Nm3DammCwa1Tm2pfBvklw">Prefill-as-a-Service：跨机房异构 PD 分离的最后一块拼图是线性注意力？</a><br><a href="https://mp.weixin.qq.com/s/VvvvYy_c2g4Bnrc2qopiNw">Kimi新论文：把KVCache玩成新商业模式了</a></p><h2 id="IPADS开源形式化方法智能体FM-Agent"><a href="#IPADS开源形式化方法智能体FM-Agent" class="headerlink" title="IPADS开源形式化方法智能体FM-Agent"></a>IPADS开源形式化方法智能体FM-Agent</h2><p><a href="https://mp.weixin.qq.com/s/AQLsy41Fx9yc5dXlokRI6w">上海交大IPADS开源形式化方法智能体FM-Agent：让代码不仅可生成，还可推理</a><br><a href="https://mp.weixin.qq.com/s/A_gKk8ls7-w2xYDTLFuyPQ">编程智能体的隐藏bug，被上交IPADS团队用数学逻辑给揪出来了</a></p><p>如今的Coding Agent生成十万行以上的系统级代码，甚至构建一个完整的编译器，都已不再稀奇。但一个严峻的挑战随之而来：如何保障这些大规模代码的正确性？上海交大IPADS团队推出形式化方法智能体FM-Agent，通过将形式化验证方法与LLM结合，首次实现了面向大规模软件的全自动正确性推理，对27万多行已经过层层测试的系统代码进行“深度体检”，找到522个被遗漏的隐蔽bug。</p><p>霍尔逻辑由图灵奖得主托尼·霍尔于约六十年前提出，但受限于人工成本，形式化方法在大规模软件中的应用始终未能普及。FM-Agent并未追求传统形式化验证所依赖的严格公式证明，而是借助大模型的强大推理能力，首次实现了面向大规模系统软件的全自动正确性推理。这一工作延续了霍尔逻辑的核心思想，站在巨人的肩膀上，同时也为形式化方法的实际落地提供了新的可行路径。</p><h2 id="Harness-Engineering"><a href="#Harness-Engineering" class="headerlink" title="Harness Engineering"></a>Harness Engineering</h2><p><a href="https://mp.weixin.qq.com/s/jlYHOardcQaEx4BdMuNOPA">别再说”Prompt工程”了！2026全网爆火的Harness Engineering是什么鬼？</a><br><img src="/images/2026/04/021.png"></p><h2 id="A-Survey-of-Context-Engineering-for-Large-Language-Models"><a href="#A-Survey-of-Context-Engineering-for-Large-Language-Models" class="headerlink" title="A Survey of Context Engineering for Large Language Models"></a>A Survey of Context Engineering for Large Language Models</h2><p><a href="https://mp.weixin.qq.com/s/deBKVO_TpuoYIfaXb6KlWw">https://mp.weixin.qq.com/s/deBKVO_TpuoYIfaXb6KlWw</a></p><p><a href="https://mp.weixin.qq.com/s/9KrnTk9LwWGmsiaypu-AVw">上下文工程（Context Engineering）原理简介</a></p><h2 id="Nsight-Systems"><a href="#Nsight-Systems" class="headerlink" title="Nsight Systems"></a>Nsight Systems</h2><p>NVIDIA Nsys 通常指的是 NVIDIA Nsight Systems（命令行工具<code>nsys</code>），是 NVIDIA 官方的系统级性能分析工具。它主要用于对 CUDA 应用程序进行全系统时间线分析，帮助开发者快速定位 CPU 和 GPU 之间的瓶颈，而不仅仅是 GPU 内核内部的耗时。</p><p><a href="https://chat.deepseek.com/share/kw68e7wlf5s356if9u">deepseek对话</a></p><h2 id="Nsight-Compute"><a href="#Nsight-Compute" class="headerlink" title="Nsight Compute"></a>Nsight Compute</h2><p>Nsight Compute：专攻 GPU 内核内部的微架构分析（如带宽、占用率、指令吞吐），属于“内核级”分析。</p><p><a href="https://developer.nvidia.com/nsight-compute">https://developer.nvidia.com/nsight-compute</a></p><h2 id="manim"><a href="#manim" class="headerlink" title="manim"></a>manim</h2><p>可视化数学<br><a href="https://github.com/3b1b/manim">https://github.com/3b1b/manim</a></p><h2 id="dmidecode"><a href="#dmidecode" class="headerlink" title="dmidecode"></a>dmidecode</h2><p><a href="https://github.com/mirror/dmidecode">https://github.com/mirror/dmidecode</a></p><p>Dmidecode reports information about your system’s hardware as described in your system BIOS according to the SMBIOS&#x2F;DMI standard. This information typically includes system manufacturer, model name, serial number, BIOS version, asset tag as well as a lot of other details of varying level of interest and reliability depending on the manufacturer. This will often include usage status for the CPU sockets, expansion slots (e.g. AGP, PCI, ISA) and memory module slots, and the list of I&#x2F;O ports (e.g. serial, parallel, USB).</p><h2 id="2025阿里云技术年报-基础设施篇"><a href="#2025阿里云技术年报-基础设施篇" class="headerlink" title="2025阿里云技术年报 基础设施篇"></a>2025阿里云技术年报 基础设施篇</h2><p><a href="https://mp.weixin.qq.com/s/ykEFWsM1VrYcDSj9KRdzaQ">https://mp.weixin.qq.com/s/ykEFWsM1VrYcDSj9KRdzaQ</a></p><h2 id="如何在短时间内提升自己"><a href="#如何在短时间内提升自己" class="headerlink" title="如何在短时间内提升自己"></a>如何在短时间内提升自己</h2><p><a href="https://mp.weixin.qq.com/s/LdeBYWcxb3ZSASf6aGo7aQ">https://mp.weixin.qq.com/s/LdeBYWcxb3ZSASf6aGo7aQ</a></p>]]>
    </content>
    <id>http://liujunming.github.io/2026/04/25/%E6%AF%8F%E5%91%A8%E5%88%86%E4%BA%AB%E7%AC%AC38%E6%9C%9F/</id>
    <link href="http://liujunming.github.io/2026/04/25/%E6%AF%8F%E5%91%A8%E5%88%86%E4%BA%AB%E7%AC%AC38%E6%9C%9F/"/>
    <published>2026-04-25T04:03:01.000Z</published>
    <summary>
      <![CDATA[<h2 id="Prefill-as-a-Service"><a href="#Prefill-as-a-Service" class="headerlink" title="Prefill-as-a-Service"></a>Prefill-as-a-Service</h2><p>核心是将PD分离放在两个或多个Datacenter中的异构GPU集群。</p>
<p><img src="/images/2026/04/020.png">]]>
    </summary>
    <title>每周分享第38期</title>
    <updated>2026-04-25T14:14:02.515Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="Intel" scheme="http://liujunming.github.io/categories/Intel/"/>
    <category term="内存管理" scheme="http://liujunming.github.io/tags/%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86/"/>
    <category term="Intel" scheme="http://liujunming.github.io/tags/Intel/"/>
    <content>
      <![CDATA[<p>本文将mark下Intel Flat Memory Mode (FMM) 的相关notes。<span id="more"></span></p><h2 id="Backgrpund"><a href="#Backgrpund" class="headerlink" title="Backgrpund"></a>Backgrpund</h2><p><img src="/images/2026/04/010.png"></p><p><img src="/images/2026/04/011.png"></p><p><img src="/images/2026/04/012.png"></p><h2 id="Overview"><a href="#Overview" class="headerlink" title="Overview"></a>Overview</h2><p><img src="/images/2026/04/013.png"></p><p>This design fully utilizes the capacity of both local DRAM and CXL memory by placing data <code>exclusively</code> at either of them, but not both. For example, once a cacheline is moved to local DRAM, it will no longer occupy any space in CXL memory.</p><p>The associativity between physical memory and local memory is direct-mapped, which means each line in the physical memory address space can only be cached at one location in local memory.</p><p><img src="/images/2026/04/014.png"></p><p><img src="/images/2026/04/015.png"></p><p><img src="/images/2026/04/016.png"></p><p><img src="/images/2026/04/017.png"></p><h2 id="Details"><a href="#Details" class="headerlink" title="Details"></a>Details</h2><p>参考<a href="https://www.usenix.org/system/files/osdi24-zhong-yuhong.pdf">Managing Memory Tiers with CXL in Virtualized Environments</a>中的3.1 即可。</p><p><img src="/images/2026/04/018.png"></p><p><img src="/images/2026/04/019.png"></p><hr><p>参考资料:</p><ol><li><a href="https://www.intel.com/content/www/us/en/content-details/886601/the-advantages-of-managing-the-cxl-memory-tier-in-hardware.html">The Advantages of Managing the CXL Memory Tier in Hardware</a></li><li>Managing Memory Tiers with CXL in Virtualized Environments(OSDI’24)</li><li><a href="https://chat.deepseek.com/share/r66602rkw3x1tc5xdl">deepseek会话</a></li></ol>]]>
    </content>
    <id>http://liujunming.github.io/2026/04/19/Notes-about-Intel-s-flat-memory-mode/</id>
    <link href="http://liujunming.github.io/2026/04/19/Notes-about-Intel-s-flat-memory-mode/"/>
    <published>2026-04-19T02:03:16.000Z</published>
    <summary>
      <![CDATA[<p>本文将mark下Intel Flat Memory Mode (FMM) 的相关notes。]]>
    </summary>
    <title>Notes about Intel's Flat Memory Mode</title>
    <updated>2026-04-19T09:30:25.908Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="AI Infra" scheme="http://liujunming.github.io/categories/AI-Infra/"/>
    <category term="AI Infra" scheme="http://liujunming.github.io/tags/AI-Infra/"/>
    <category term="RDMA" scheme="http://liujunming.github.io/tags/RDMA/"/>
    <content>
      <![CDATA[<p>本文将mark下IB交换机中SHARP(Scalable Hierarchical Aggregation and Reduction Protocol)技术的相关notes。<span id="more"></span></p><h2 id="In-Network-Computing"><a href="#In-Network-Computing" class="headerlink" title="In-Network Computing"></a>In-Network Computing</h2><p>在网计算(In-Network Computing)，可以简单理解为：让网络在传输数据的过程中，顺便完成一部分原本属于主机的计算任务。这里的“计算”通常不是通用计算，而是聚合、归约、统计、筛选，以及某些固定模式的算术或逻辑操作。</p><p><img src="/images/2026/04/008.png"></p><p>在 AI 和 HPC 场景中，最有价值的一类在网计算，就是对集合通信中的归约操作进行卸载。因为归约是最常见、最频繁、也最容易在规模扩展后成为瓶颈的环节；如果交换机能够在数据流经时，先把多个节点发来的数据做一轮聚合，那么继续向上传输的数据量就会变少，最终回到端点的也不再是大量原始中间值，而是更接近结果的数据。</p><p>这会同时带来几个好处：</p><ol><li>降低端点参与归约的负担</li><li>减少网络中传输的中间数据</li><li>缩短集合通信完成时间</li><li>提高系统扩展效率</li></ol><p>而 SHARP，就是这种思想在高性能网络中的代表性实现。</p><h2 id="SHARP的介绍"><a href="#SHARP的介绍" class="headerlink" title="SHARP的介绍"></a>SHARP的介绍</h2><p>传统模式下，交换机只负责收包、转发、出包；即使是 AllReduce 这类高度结构化的操作，交换机本身也并不真正“理解”归约，因此大量中间数据仍需要在端点之间来回流动，归约主要还是由主机侧完成。</p><p>SHARP(Scalable Hierarchical Aggregation and Reduction Protocol)是 NVIDIA 的一种<strong>in-network computing</strong>技术，<u>用来把 AllReduce、Reduce、Broadcast 等集合通信操作从 CPU&#x2F;GPU 卸载到 InfiniBand 交换网络中执行</u>。</p><h2 id="AllReduce"><a href="#AllReduce" class="headerlink" title="AllReduce"></a>AllReduce</h2><p>现代AI训练（如大语言模型）和科学计算严重依赖于<strong>集合通信</strong>，尤其是 <strong>AllReduce</strong> 操作（用于同步梯度、参数等）。AllReduce的通信开销常常成为训练瓶颈。</p><ol><li>传统AllReduce（无SHARP）</li></ol><ul><li>数据在GPU间多次传输。</li><li>最终聚合计算由某个GPU的CUDA核心完成。</li><li>占用宝贵的GPU计算资源（尽管是轻量计算）。</li><li>产生大量网络流量，消耗带宽，增加延迟。</li></ul><ol start="2"><li>使用SHARP的AllReduce</li></ol><ul><li>计算卸载： 聚合计算（如求和）由交换机内部的专用硬件引擎执行。</li><li>流量锐减： 数据在交换机树形网络的每一级就开始聚合，网络中传输的数据量逐级减少，极大降低了最终的网络流量（可降低高达50%以上）。</li><li>解放GPU： GPU无需处理聚合计算，可以更专注于张量核心的计算。</li><li>降低延迟： 在网络中就近计算，减少了端到端的通信延迟。</li></ul><ol start="3"><li>核心价值： 将网络从一个被动的“数据搬运工”，转变为一个主动的“计算参与者”，从而打破集合通信的瓶颈。</li></ol><h2 id="工作原理"><a href="#工作原理" class="headerlink" title="工作原理"></a>工作原理</h2><p><img src="/images/2026/04/009.png"></p><p>以最常见的“求和”AllReduce为例，剖析SHARP工作原理：</p><ol><li>初始化与配置： 集群管理软件（如NCCL）识别到支持SHARP的交换机拓扑，并为本次AllReduce操作在交换机上配置一个“聚合树”。</li><li>Leaf叶子节点发送： 每个GPU（主机）将其需要聚合的数据块发送到它所连接的第一级（Leaf叶子）交换机。</li><li>第一级聚合： Leaf叶子交换机的SHARP引擎接收来自其下所有GPU的数据，在交换机内部硬件中直接进行求和计算，生成一个部分聚合的结果。</li><li>向上传播： Leaf叶子交换机将部分聚合的结果（数据量已小于原始多份数据的总和）发送到上一级（Spine主干）交换机。</li><li>递归聚合： Spine主干交换机的SHARP引擎再次接收来自多个下级交换机的部分聚合结果，并进行第二次聚合。此过程在交换机的层次结构中持续向上，直到到达聚合树的根交换机。</li><li>生成全局结果： 根交换机生成最终的全局聚合结果（所有GPU数据的总和）。</li><li>结果广播： 根交换机再将这个最终结果沿聚合树向下广播到所有参与计算的GPU。</li></ol><p>关键点： 数据在向上传输的过程中不断被“压缩”（聚合），因此从根节点向下广播的数据量是最小的（一份完整结果）。这相比于传统的环状或树状AllReduce算法，显著减少了网络的总数据吞吐量。</p><h2 id="SHARP-vs-GPUDirect-RDMA"><a href="#SHARP-vs-GPUDirect-RDMA" class="headerlink" title="SHARP vs. GPUDirect RDMA"></a>SHARP vs. GPUDirect RDMA</h2><ol><li>GPUDirect RDMA： 解决的是“路径”问题，允许第三方设备（如网卡）直接访问GPU显存，绕过CPU和系统内存拷贝，降低延迟。它优化的是点对点通信。</li><li>SHARP： 解决的是“流量和计算”问题，在网络内对多个数据流进行聚合计算，优化的是集体通信。</li><li>关系： 它们是互补的，可以同时启用。GPUDirect RDMA确保数据从GPU到网络的路径最优，而SHARP确保数据在网络中的聚合效率最高。现代AI集群通常同时启用这两项技术。</li></ol><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>SHARP是构建大规模、高效率GPU计算集群的一项革命性网络技术。它通过“网络内计算”的理念，直接攻克了分布式AI训练中最耗时的集合通信瓶颈，是当今万卡级GPU集群不可或缺的关键技术之一。</p><hr><p>参考资料:</p><ol><li><a href="https://zhuanlan.zhihu.com/p/27394787474">【论文阅读】Scalable Hierarchical Aggregation Protocol (SHArP)</a></li><li><a href="https://conferences.sigcomm.org/events/apnet2019/slides/Industrial_1_3.pdf">Pushing the Limits of AI with In-Network Computing</a></li><li><a href="https://mp.weixin.qq.com/s/GkctRBBjRyhraIfBdTKX4Q">GPU集群性能提升技术——IB交换机中的SHARP详解</a></li><li><a href="https://mp.weixin.qq.com/s/YhD_e6kH9lhA360t5yC2iQ">SHARP与在网计算</a></li><li><a href="https://mp.weixin.qq.com/s/K5o57R5bdW6DjUkme0Gf8g">NCCL 原理详解：让多GPU通信更高效</a></li></ol>]]>
    </content>
    <id>http://liujunming.github.io/2026/04/18/Notes-about-IB%E4%BA%A4%E6%8D%A2%E6%9C%BA%E4%B8%AD%E7%9A%84SHARP%E6%8A%80%E6%9C%AF/</id>
    <link href="http://liujunming.github.io/2026/04/18/Notes-about-IB%E4%BA%A4%E6%8D%A2%E6%9C%BA%E4%B8%AD%E7%9A%84SHARP%E6%8A%80%E6%9C%AF/"/>
    <published>2026-04-18T07:48:27.000Z</published>
    <summary>
      <![CDATA[<p>本文将mark下IB交换机中SHARP(Scalable Hierarchical Aggregation and Reduction Protocol)技术的相关notes。]]>
    </summary>
    <title>Notes about IB交换机中的SHARP技术</title>
    <updated>2026-04-18T14:49:16.749Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="经验" scheme="http://liujunming.github.io/categories/%E7%BB%8F%E9%AA%8C/"/>
    <category term="经验" scheme="http://liujunming.github.io/tags/%E7%BB%8F%E9%AA%8C/"/>
    <content>
      <![CDATA[<h2 id="CubeSandbox"><a href="#CubeSandbox" class="headerlink" title="CubeSandbox"></a>CubeSandbox</h2><ul><li><a href="https://mp.weixin.qq.com/s/d6toVeZzrUc1C4g1EJRPnQ">当 Docker 不再安全：腾讯云 CubeSandbox 如何用 Rust+KVM 重塑 AI Agent 沙箱</a></li><li><a href="https://github.com/TencentCloud/CubeSandbox">TencentCloud&#x2F;CubeSandbox</a><span id="more"></span></li></ul><p><img src="/images/2026/04/005.png"></p><p><strong>核心优势</strong></p><ul><li>极致冷启动： 基于资源池化预置和快照克隆技术，直接跳过耗时初始化流程。整个沙箱服务端到端冷启动一个可服务的沙箱时间平均 &lt; 60ms</li><li>单机千例的高密部署： 基于 CoW 技术实现极致内存复用，用 Rust 重构底层极致裁剪，使得单实例内存开销低至 &lt;5MB，轻松在一台机器上跑起数千个 Agent。</li><li>真正的内核级隔离： 告别不安全的 Docker 共享内核（Namespace）。每个 Agent 拥有独立的 Guest OS 内核，杜绝容器逃逸，放心运行任何大模型生成的未知代码。</li><li>零成本迁移（E2B 完美平替）： 原生兼容 E2B SDK 接口规范。只需替换一个 URL 环境变量，无需业务代码改动就可切换到免费的 Cube Sandbox，并获得更好的性能体验。</li><li>网络安全： 基于 eBPF 的 CubeVS 在内核态实现严格的沙箱间网络隔离，支持细粒度出站流量过滤策略。</li><li>开箱即用： 可一键快速部署，同时支持单机部署和集群部署。</li><li>事件级快照回滚（coming soon）： 百毫秒级的高频快照回滚能力，基于快照快速创建分叉探索环境</li></ul><p>历史文档:</p><ul><li><a href="https://mp.weixin.qq.com/s/r22ECd7t_alO8VfxTWOU_w">Cube轻量虚拟化如何做到100ms交付一个安全容器</a></li></ul><h2 id="IPADS开源SkVM"><a href="#IPADS开源SkVM" class="headerlink" title="IPADS开源SkVM"></a>IPADS开源SkVM</h2><p><a href="https://mp.weixin.qq.com/s/W9zPy0R8eqIPjh3MzWoAbg">上海交大IPADS开源SkVM：让Agent Skill”一次编写，处处高效运行”</a></p><p>Agent时代重新思考编译技术：Skill是代码，LLM+Harness是异构处理器，借鉴语言虚拟机（Language VM）的设计理念，首次将传统编译器与运行时思想系统性地引入Agent技能领域，<strong>SkVM让Skill一次编写，到处高效执行</strong>。</p><p>核心理念：把Skill当作程序来编译</p><p><img src="/images/2026/04/006.png"></p><h2 id="英伟达-GPU-机密计算"><a href="#英伟达-GPU-机密计算" class="headerlink" title="英伟达 GPU 机密计算"></a>英伟达 GPU 机密计算</h2><p><a href="https://mp.weixin.qq.com/s/Zx2ocq9RTSx3AsNch0AUYg">英伟达 GPU 机密计算：AI 时代机密计算边界的延伸</a></p><p>传统机密计算主要保护 CPU 执行环境中的数据与代码，其目标是在操作系统、虚拟化层乃至平台管理员不完全可信的前提下，仍然保证工作负载的机密性与完整性。Intel TDX、AMD SEV-SNP 和 Arm CCA 虽然实现路径不同，但本质上都属于 CPU 侧可信执行环境基础设施：通过硬件隔离、内存保护、启动测量和远程证明，为虚拟机或受保护执行域建立一个不依赖宿主软件信任的安全边界。<strong>它们解决的核心问题是，应用在 CPU 和系统内存中运行时，如何不被宿主环境窥探或篡改</strong>。</p><p>但在 AI 基础设施中，仅有 CPU 侧保护并不充分。因为真正承载高价值数据和核心计算的，往往已经不是 CPU，而是 GPU。<strong>模型权重、推理输入、检索增强数据、中间激活值乃至部分业务逻辑，都会进入 GPU 显存并在加速器上执行</strong>。如果机密计算只覆盖 CPU 虚拟机，而无法覆盖 GPU 执行路径，那么对 AI 而言，这种保护就是不完整的。英伟达 GPU 机密计算的意义，正是在于<strong>把机密计算的保护边界从 CPU 扩展到 GPU，使 GPU 成为整个可信执行体系的一部分，保护模型参数、数据和其他重要客户资产</strong>。</p><p><img src="/images/2026/04/007.png"></p><h2 id="为什么-AI-时代值得重新讨论-OS：从-Linux-Kernel-到-GPU-执行栈的系统重构"><a href="#为什么-AI-时代值得重新讨论-OS：从-Linux-Kernel-到-GPU-执行栈的系统重构" class="headerlink" title="为什么 AI 时代值得重新讨论 OS：从 Linux Kernel 到 GPU 执行栈的系统重构"></a>为什么 AI 时代值得重新讨论 OS：从 Linux Kernel 到 GPU 执行栈的系统重构</h2><p><a href="https://mp.weixin.qq.com/s/dVpd5_99co4wk0L-tOpiJQ">https://mp.weixin.qq.com/s/dVpd5_99co4wk0L-tOpiJQ</a></p><p>AI时代的操作系统革新，核心不在于推翻Linux，而在于围绕<code>tensor</code>、<code>graph</code>、<code>kernel</code>、<code>KV cache</code>等新的一等对象，建立一套从驱动、编译器到运行时服务的全新系统抽象，以管理和调度异构计算资源。</p><h2 id="DPU-PCC"><a href="#DPU-PCC" class="headerlink" title="DPU PCC"></a>DPU PCC</h2><p><a href="https://mp.weixin.qq.com/s/Br_DimiEOPkjBYy4Kw_d9A">DPU PCC：为什么拥塞控制从“固定算法”走向“可编程”</a></p><p>DOCA 官方给出的定义非常直接：<strong>Programmable Congestion Control（PCC）允许用户设计并实现自定义的拥塞控制算法，以更灵活地处理集群中的网络拥塞问题</strong>。<br>PCC 不是一个具体的拥塞控制算法，而是一个<strong>拥塞控制开发框架</strong>。它的目标是提供一套 API 和执行环境，让客户可以自己写算法、加载算法、运行算法，并基于网络反馈动态调整每条流的发送速率。</p><p>DPU PCC 的本质，是把拥塞控制从一个封闭的网卡内置算法，升级成一个运行在 DPU 数据路径加速环境中的可编程控制框架。它让开发者能够围绕真实业务需求，自定义反馈机制、流状态管理和速率调节逻辑，从而在 AI、存储和多租户集群中获得更合适的网络行为。</p><h2 id="Sandlock：最轻量级的-AI-Agent-沙箱"><a href="#Sandlock：最轻量级的-AI-Agent-沙箱" class="headerlink" title="Sandlock：最轻量级的 AI Agent 沙箱"></a>Sandlock：最轻量级的 AI Agent 沙箱</h2><p>The lightest AI sandbox. A process-based sandbox for Linux, no container, no VM, no root.</p><p>应对AI Agent的安全问题，不应盲目堆叠重量级的硬件隔离，而应针对其应用层的行为模式，设计精细、轻量的访问控制策略。Sandlock正是这一理念的实践，它提供了一种更贴合Agent本质的安全方案。</p><ul><li><a href="https://mp.weixin.qq.com/s/ICxIj6ydpftSLwgjA9zZAg">https://mp.weixin.qq.com/s/ICxIj6ydpftSLwgjA9zZAg</a></li><li><a href="https://github.com/multikernel/sandlock">https://github.com/multikernel/sandlock</a></li></ul><p>Lightweight process sandbox for Linux. Confines untrusted code using Landlock (filesystem + network + IPC), seccomp-bpf (syscall filtering), and seccomp user notification (resource limits, IP enforcement, &#x2F;proc virtualization). No root, no cgroups, no containers.</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sandlock run -w /tmp -r /usr -r /lib -m 512M -- python3 untrusted.py</span><br></pre></td></tr></table></figure><h2 id="Bubblewrap-vs-Sandlock"><a href="#Bubblewrap-vs-Sandlock" class="headerlink" title="Bubblewrap vs Sandlock"></a>Bubblewrap vs Sandlock</h2><p>Bubblewrap和Sandlock是两种设计理念和实现路径都截然不同的沙箱工具。简单来说，Bubblewrap是一个通用的、轻量级的“容器构建工具箱”，而Sandlock是一个为AI Agent场景专门设计的、高密度的“进程执行沙箱”。</p><h3 id="目标定位不同"><a href="#目标定位不同" class="headerlink" title="目标定位不同"></a>目标定位不同</h3><p><a href="/2026/03/22/Notes-about-Bubblewrap/">Bubblewrap</a>是一个通用的工具，旨在安全地运行一个浏览器或一个游戏等普通应用。而Sandlock则是专为AI Agent量身定做，它思考的问题是：“如何安全、高效地让大模型生成的上万个代码片段并发执行？”。</p><h3 id="安全模型的根本分歧"><a href="#安全模型的根本分歧" class="headerlink" title="安全模型的根本分歧"></a>安全模型的根本分歧</h3><p>这源于它们对“敌人”的不同理解。</p><ul><li>Bubblewrap默认环境中可能<strong>存在主动的攻击者</strong>，因此首要任务是利用Namespace创建一个与外界隔绝的**“硬边界”**。</li><li>Sandlock则认为Agent不是攻击者，真正的风险是提示词注入。它的核心不是建立边界，而是通过Landlock和Seccomp执行一套精细的访问控制<strong>策略（Policy）</strong>，例如这个Agent只能读<code>/data</code>目录，只能写<code>/tmp</code>。</li></ul><h3 id="效率的极致追求"><a href="#效率的极致追求" class="headerlink" title="效率的极致追求"></a>效率的极致追求</h3><p>在AI场景中，常需同时运行成百上千个沙箱实例。Bubblewrap每个实例都需要独立的初始化过程，资源开销会线性增长。而Sandlock通过其写时复制（COW） 技术，可以瞬间从一个“黄金镜像”克隆出上千个实例，共享内存，极大节省了时间和空间。同时，Sandlock的开销极低，启动仅需5毫秒，远快于传统容器或虚拟机。</p><h3 id="如何选择"><a href="#如何选择" class="headerlink" title="如何选择"></a>如何选择</h3><p>选择Bubblewrap，如果你的目标是：</p><ul><li>为普通桌面应用（如一个闭源游戏或聊天软件）提供一个基础的运行沙箱。</li><li>需要一个无需守护进程、轻量级的通用沙箱工具。</li></ul><p>选择Sandlock，如果你的目标是：</p><ul><li>在AI Agent系统中安全地执行由模型生成的不可信代码。</li><li>需要高并发、低延迟地执行大量短暂任务（如批量代码评估）。</li><li>需要精细到每个工具调用的访问控制策略。</li></ul><h2 id="基于GPU加速的内存回收方案"><a href="#基于GPU加速的内存回收方案" class="headerlink" title="基于GPU加速的内存回收方案"></a>基于GPU加速的内存回收方案</h2><p><a href="https://mp.weixin.qq.com/s/cnAB_8DHaTJRYjVKpcoH_A">vivo的ZRAM异构压缩技术：基于GPU加速的内存回收方案</a></p><p>key idea: 将zram压缩内存数据的操作由CPU offload到GPU以进行加速。</p>]]>
    </content>
    <id>http://liujunming.github.io/2026/04/18/%E6%AF%8F%E5%91%A8%E5%88%86%E4%BA%AB%E7%AC%AC37%E6%9C%9F/</id>
    <link href="http://liujunming.github.io/2026/04/18/%E6%AF%8F%E5%91%A8%E5%88%86%E4%BA%AB%E7%AC%AC37%E6%9C%9F/"/>
    <published>2026-04-18T06:08:42.000Z</published>
    <summary>
      <![CDATA[<h2 id="CubeSandbox"><a href="#CubeSandbox" class="headerlink" title="CubeSandbox"></a>CubeSandbox</h2><ul>
<li><a href="https://mp.weixin.qq.com/s/d6toVeZzrUc1C4g1EJRPnQ">当 Docker 不再安全：腾讯云 CubeSandbox 如何用 Rust+KVM 重塑 AI Agent 沙箱</a></li>
<li><a href="https://github.com/TencentCloud/CubeSandbox">TencentCloud&#x2F;CubeSandbox</a>]]>
    </summary>
    <title>每周分享第37期</title>
    <updated>2026-04-18T07:36:13.972Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="AI Infra" scheme="http://liujunming.github.io/categories/AI-Infra/"/>
    <category term="AI Infra" scheme="http://liujunming.github.io/tags/AI-Infra/"/>
    <content>
      <![CDATA[<p>本文将持续收集LLM KV Cache相关的文章。<span id="more"></span></p><ul><li><a href="https://developer.aliyun.com/article/1713501">当我们谈论 AI 推理的 KV Cache，我们在说什么？</a></li><li><a href="https://mp.weixin.qq.com/s/lE5TX3V7qdvYJIECrdsxAw">阿里云提出基于CXL的KV缓存管理内存架构Beluga</a></li></ul>]]>
    </content>
    <id>http://liujunming.github.io/2026/04/12/LLM-KV-Cache%E6%96%87%E7%AB%A0%E6%B1%87%E6%80%BB/</id>
    <link href="http://liujunming.github.io/2026/04/12/LLM-KV-Cache%E6%96%87%E7%AB%A0%E6%B1%87%E6%80%BB/"/>
    <published>2026-04-12T08:33:41.000Z</published>
    <summary>
      <![CDATA[<p>本文将持续收集LLM KV Cache相关的文章。]]>
    </summary>
    <title>LLM KV Cache文章汇总</title>
    <updated>2026-04-11T08:46:45.880Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="RAS" scheme="http://liujunming.github.io/categories/RAS/"/>
    <category term="RAS" scheme="http://liujunming.github.io/tags/RAS/"/>
    <content>
      <![CDATA[<p>本文将mark下Memory Mirroring相关notes。<span id="more"></span></p><h2 id="Memory-Mirroring"><a href="#Memory-Mirroring" class="headerlink" title="Memory Mirroring"></a>Memory Mirroring</h2><p>The Intel Xeon processor family-based platform offers a RAS (Reliability Availability Serviceability) feature called <strong>Memory Mirroring</strong>. This feature allows users to configure the memory in a highly reliable mode such that system uptime can be maintained—even when a memory component is affected by an uncorrectable fault. When enabled, a trade-off between memory capacity and reliability is required. For example, <u>user-accessible memory may be reduced to half of the total available memory</u>.</p><h2 id="Address-Range-Partial-Memory-Mirroring"><a href="#Address-Range-Partial-Memory-Mirroring" class="headerlink" title="Address Range Partial Memory Mirroring"></a>Address Range Partial Memory Mirroring</h2><p>The Intel Xeon processor E7 v3 product family-based platform introduces a new layer of support for partial memory mirroring called Address Range Mirroring. When Address Range Mirroring is used, the platform allows an OS to specify a subset of total available memory for mirroring (and also to optionally specify whether to mirror the range 0–4 GB). This capability allows a user to make an appropriate trade-off between non-mirrored memory range and mirrored memory range, thus optimizing total available memory while keeping highly reliable memory range available for mission-critical workloads and&#x2F;or kernel space.</p><p>Legacy memory mirroring is transparent to the OS; however, address range mirroring requires a firmware-OS interface for a user to specify the desired subset of memory to mirror. To fully utilize Address Range Mirroring:</p><ul><li>Present partial or total mirrored memory on the platform to the OS.</li><li>Provide the OS a method to request the amount of mirrored memory that takes effect on subsequent boots.</li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ol><li>Legacy Memory Mirroring是将所有内存都做mirror，因而available memory会减半</li><li>Address Range Partial Memory Mirroring是选择部分内存做mirror</li></ol><hr><p>参考资料:</p><ol><li><a href="https://www.intel.com/content/www/us/en/developer/articles/technical/address-range-partial-memory-mirroring.html">Address Range Partial Memory Mirroring</a></li></ol>]]>
    </content>
    <id>http://liujunming.github.io/2026/04/11/Notes-about-Address-Range-Partial-Memory-Mirroring/</id>
    <link href="http://liujunming.github.io/2026/04/11/Notes-about-Address-Range-Partial-Memory-Mirroring/"/>
    <published>2026-04-11T03:49:10.000Z</published>
    <summary>
      <![CDATA[<p>本文将mark下Memory Mirroring相关notes。]]>
    </summary>
    <title>Notes about Memory Mirroring</title>
    <updated>2026-04-10T15:00:19.749Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="RDMA" scheme="http://liujunming.github.io/categories/RDMA/"/>
    <category term="RDMA" scheme="http://liujunming.github.io/tags/RDMA/"/>
    <content>
      <![CDATA[<p>本文将mark RDMA的MPT(Memory Protection Table) and MTT(Memory Translation Table)相关notes。<span id="more"></span></p><h2 id="流程"><a href="#流程" class="headerlink" title="流程"></a>流程</h2><p><img src="/images/2026/04/001.png"></p><p><img src="/images/2026/04/002.png"></p><p>每一次内存访问都会指定一个mkey和一个VA地址，mkey来自WQE或报文里的LKEY&#x2F;RKEY，它是一个32bits的地址空间标识，包含index和key两部分。MTT地址翻译过程如下：</p><ol><li>使用mkey.index索引mkey context table(MPT)得到mkey context。</li><li>Key检查：对比mkey.key和mkey context里的key是否一致。</li><li>PD检查：对比发起内存访问的QP里的PD和mkey context里的PD是否一致。</li><li>地址范围检查：mkey context对应了一个MR，里面包含address和length两个字段用于指明MR的范围，这里需要检查当前正在进行的内存访问是否在这个MR范围内。</li><li>访问权限检查：根据mkey context内的local&#x2F;remote read&#x2F;write&#x2F;atomic权限检查当前操作是否合法。</li><li>当上面的检查通过时，就可以使用mtt pointer了，它是MTT表的基地址。</li><li>访问MTT表需要一个索引MTT index&#x3D;当前访问地址accessing VA - mkey_context.va得到，其中mkey_context.va是MR的起始地址。另外需要考虑PA所指向的page大小，当page为4K时，上面的MTT index还要右移12位。</li><li>用上面的index索引MTT表就能得到一个MTT entry，它里面包含一个PA地址，即page number页号。如果page为4KB则最终的PA &#x3D; (page number &lt;&lt; 12) + (accessing VA &amp; 0xfff)。</li></ol><p>上述步骤均由RDMA硬件完成。</p><h2 id="MTT-entry"><a href="#MTT-entry" class="headerlink" title="MTT entry"></a>MTT entry</h2><p>MTT entry的细节可以<a href="https://network.nvidia.com/files/doc-2020/ethernet-adapters-programming-manual.pdf">Mellanox Adapters Programmer’s Reference Manual (PRM)</a>:</p><p><img src="/images/2026/04/003.png"></p><h2 id="State-Machine"><a href="#State-Machine" class="headerlink" title="State Machine"></a>State Machine</h2><p><img src="/images/2026/04/004.png"></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul><li>MTT:用于记录本地内存的物理地址映射，以便远端节点可以直接访问这些内存区域。</li><li>MPT:用于定义哪些本地内存区域可以被远端节点访问，以及它们的访问权限（如读&#x2F;写）。每当有新的 RDMA 操作请求到来时，RDMA硬件会检查 MPT，以确定是否允许此次访问。</li><li>MTT 和 MPT 是 RDMA 技术中不可或缺的组成部分，前者负责将虚拟地址转换为物理地址，后者则提供内存访问的安全性和隔离性。两者共同作用，确保了 RDMA 操作既高效又安全。</li></ul><hr><p>参考资料:</p><ol><li><a href="https://zhuanlan.zhihu.com/p/567720023">RDMA 高级</a></li><li><a href="https://network.nvidia.com/files/doc-2020/ethernet-adapters-programming-manual.pdf">Mellanox Adapters Programmer’s Reference Manual (PRM)</a></li><li><a href="https://mp.weixin.qq.com/s/crz1PG0sLnQDVn03pokV1Q">RDMA 关键技术研究：Memory Region</a></li><li><a href="https://mp.weixin.qq.com/s/w2wBzx1nkR6S6Cb68PkaEw">从阿里云eRDMA看如何解决RDMA在数据中心大规模使用问题</a></li><li><a href="https://mp.weixin.qq.com/s/fdYYkc8hAarrsvclyU2LUQ">【RDMA MPT &amp;&amp; MTT简介】</a></li><li><a href="https://www.ctyun.cn/developer/article/491807389945925">自研RDMA的地址翻译表的实现技术</a></li></ol>]]>
    </content>
    <id>http://liujunming.github.io/2026/04/11/Notes-about-RDMA-MPT-and-MTT/</id>
    <link href="http://liujunming.github.io/2026/04/11/Notes-about-RDMA-MPT-and-MTT/"/>
    <published>2026-04-11T00:58:21.000Z</published>
    <summary>
      <![CDATA[<p>本文将mark RDMA的MPT(Memory Protection Table) and MTT(Memory Translation Table)相关notes。]]>
    </summary>
    <title>Notes about RDMA MPT and MTT</title>
    <updated>2026-04-11T12:00:11.148Z</updated>
  </entry>
  <entry>
    <author>
      <name>liujunming</name>
    </author>
    <category term="GPU" scheme="http://liujunming.github.io/categories/GPU/"/>
    <category term="GPU" scheme="http://liujunming.github.io/tags/GPU/"/>
    <category term="CUDA" scheme="http://liujunming.github.io/tags/CUDA/"/>
    <content>
      <![CDATA[<p>本文将mark下CUDA Zero-Copy Memory的相关notes。<span id="more"></span></p><h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><ul><li>pin住host的dram内存</li><li>host CPU mmu建立VA到pinned memory的映射</li><li>GPU mmu建立VA到pinned memory的映射</li></ul><p>通过将一段pin住的物理内存，同时映射到CPU和GPU的虚拟地址空间来实现。这样一来，GPU内核就能像访问自己的显存一样，直接读写这块主机内存，省去了显式的<code>cudaMemcpy</code>操作。</p><h2 id="Advantages"><a href="#Advantages" class="headerlink" title="Advantages"></a>Advantages</h2><p>GPU threads can directly access zero-copy memory. There are several advantages to using zero-copy memory in CUDA kernels, such as:</p><ul><li>Leveraging host memory when there is insufficient device memory</li><li>Avoiding explicit data transfer between the host and device</li><li>Improving PCIe transfer rates</li></ul><h2 id="劣势与挑战"><a href="#劣势与挑战" class="headerlink" title="劣势与挑战"></a>劣势与挑战</h2><ul><li>带宽瓶颈: 访问速度受限于PCIe总线带宽。对频繁访问的数据，效率远低于本地显存。</li><li>高延迟: 每次访问都需经过PCIe总线，延迟比本地显存高几个数量级。</li></ul><h2 id="实践"><a href="#实践" class="headerlink" title="实践"></a>实践</h2><p><strong>分配并映射新的pinned内存</strong><br>这种方式直接分配并映射一块全新的host pinned内存，是最常见的方法。</p><ul><li>核心API: <code>cudaHostAlloc</code></li><li>关键步骤:<ul><li>分配：使用 <code>cudaHostAlloc</code> 并指定 <code>cudaHostAllocMapped</code> 标志。</li><li>获取设备指针：通过 <code>cudaHostGetDevicePointer</code> 获取该内存对应的GPU侧有效指针。</li><li>在核函数中使用：将设备指针传入核函数。</li></ul></li></ul><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;cuda_runtime.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">// GPU 核函数：直接对主机上的锁页内存进行操作</span></span><br><span class="line"><span class="function">__global__ <span class="type">void</span> <span class="title">vector_add_kernel</span><span class="params">(<span class="type">float</span> *a, <span class="type">float</span> *b, <span class="type">float</span> *c, <span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> idx = blockIdx.x * blockDim.x + threadIdx.x;</span><br><span class="line">    <span class="keyword">if</span> (idx &lt; n) &#123;</span><br><span class="line">        <span class="comment">// 直接访问主机上的锁页内存，如同访问显存</span></span><br><span class="line">        c[idx] = a[idx] + b[idx];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> n = <span class="number">1024</span>;</span><br><span class="line">    <span class="type">size_t</span> size = n * <span class="built_in">sizeof</span>(<span class="type">float</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 1. 在主机上分配并映射锁页内存</span></span><br><span class="line">    <span class="type">float</span> *h_a, *h_b, *h_c;</span><br><span class="line">    <span class="built_in">cudaHostAlloc</span>((<span class="type">void</span> **)&amp;h_a, size, cudaHostAllocMapped);</span><br><span class="line">    <span class="built_in">cudaHostAlloc</span>((<span class="type">void</span> **)&amp;h_b, size, cudaHostAllocMapped);</span><br><span class="line">    <span class="built_in">cudaHostAlloc</span>((<span class="type">void</span> **)&amp;h_c, size, cudaHostAllocMapped);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 2. 在主机端初始化数据</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; ++i) &#123;</span><br><span class="line">        h_a[i] = <span class="number">1.0f</span>;</span><br><span class="line">        h_b[i] = <span class="number">2.0f</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 3. 获取该主机内存对应的GPU设备指针</span></span><br><span class="line">    <span class="type">float</span> *d_a, *d_b, *d_c;</span><br><span class="line">    <span class="built_in">cudaHostGetDevicePointer</span>((<span class="type">void</span> **)&amp;d_a, (<span class="type">void</span> *)h_a, <span class="number">0</span>);</span><br><span class="line">    <span class="built_in">cudaHostGetDevicePointer</span>((<span class="type">void</span> **)&amp;d_b, (<span class="type">void</span> *)h_b, <span class="number">0</span>);</span><br><span class="line">    <span class="built_in">cudaHostGetDevicePointer</span>((<span class="type">void</span> **)&amp;d_c, (<span class="type">void</span> *)h_c, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 4. 配置核函数并启动</span></span><br><span class="line">    <span class="type">int</span> threadsPerBlock = <span class="number">256</span>;</span><br><span class="line">    <span class="type">int</span> blocksPerGrid = (n + threadsPerBlock - <span class="number">1</span>) / threadsPerBlock;</span><br><span class="line">    vector_add_kernel&lt;&lt;&lt;blocksPerGrid, threadsPerBlock&gt;&gt;&gt;(d_a, d_b, d_c, n);</span><br><span class="line">    <span class="built_in">cudaDeviceSynchronize</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 5. 检查结果，此时结果已直接写入主机的 h_c 中</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; ++i) &#123;</span><br><span class="line">        <span class="keyword">if</span> (h_c[i] != <span class="number">3.0f</span>) &#123;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;Error: h_c[%d] = %f\n&quot;</span>, i, h_c[i]);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;Test passed!\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 6. 清理资源</span></span><br><span class="line">    <span class="built_in">cudaFreeHost</span>(h_a);</span><br><span class="line">    <span class="built_in">cudaFreeHost</span>(h_b);</span><br><span class="line">    <span class="built_in">cudaFreeHost</span>(h_c);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><hr><p>参考资料:</p><ol><li><a href="https://github.com/NVIDIA/cuda-samples/tree/master/Samples/0_Introduction#simplezerocopy">cuda-samples</a></li><li><a href="https://docs.nvidia.com/cuda/cuda-programming-guide/02-basics/understanding-memory.html#page-locked-host-memory">Page-Locked Host Memory</a></li><li>Professional CUDA C Programming</li><li><a href="https://chat.deepseek.com/share/ran9srj0hi8t35xmsw">https://chat.deepseek.com/share/ran9srj0hi8t35xmsw</a></li></ol>]]>
    </content>
    <id>http://liujunming.github.io/2026/04/05/Notes-about-CUDA-Zero-MemCopy/</id>
    <link href="http://liujunming.github.io/2026/04/05/Notes-about-CUDA-Zero-MemCopy/"/>
    <published>2026-04-05T08:24:56.000Z</published>
    <summary>
      <![CDATA[<p>本文将mark下CUDA Zero-Copy Memory的相关notes。]]>
    </summary>
    <title>Notes about CUDA Zero-Copy Memory</title>
    <updated>2026-04-05T23:47:11.346Z</updated>
  </entry>
</feed>
