一块 Gen 4×4 SSD 接在 Gen 4 主板上,不一定能独占约 8 GB/s:插槽可能只接了两条 lane,也可能通过交换芯片或芯片组与其他设备共享上行。要解释性能,先画出实际拓扑,再区分链路速率、编码后带宽、事务开销和设备处理速度。
要查的问题 本篇入口 关键口径
x4/x16 到底数什么 lane 与链路 一条 lane 有双向差分通道,多 lane 组成一条链路
每代能传多少 速率与编码 单向、十进制 GB/s、是否已扣开销
为什么多设备一起变慢 拓扑与共享上行 每一跳的链路和流量方向
读写为什么性能不同 TLP 与在途请求 posted write、read completion、延迟与并发
设备为何降速或不出现 训练与枚举 电气状态、资源配置、驱动和 DMA
Lane 是物理并行度,Generation 是链路速率级别
一条 lane 包含一对发送差分线和一对接收差分线,两个方向可同时工作。x4 链路使用四条 lane 配合传输,并非四个独立可寻址设备;x16 的高速信号共 16×4=64 根导线,还不包含电源、地、参考时钟和边带控制。
插槽的机械长度、电气布线和当前协商宽度可以不同。一个 x16 长插槽可能只接 x4;主板还可能在插入另一设备后重新分配端口资源。支持 bifurcation 的根端口可把一组 lane 配成多个独立链路,但无源转接板本身不会凭空实现 PCIe Switch 功能,固件与硬件都需支持对应分拆。
带宽先声明单向,再扣除相应代际开销
下面 Gen 1~5 的数值已经扣除线路编码,但尚未扣除事务包、链路控制和其他开销 。GB 使用十进制 10⁹ byte,不是 GiB。
世代 标称传输速率 信号/编码概况 每 lane 单向预算 x16 单向预算
Gen 1 2.5 GT/s NRZ,8b/10b 0.250 GB/s 4.000 GB/s
Gen 2 5 GT/s NRZ,8b/10b 0.500 GB/s 8.000 GB/s
Gen 3 8 GT/s NRZ,128b/130b 0.985 GB/s 15.754 GB/s
Gen 4 16 GT/s NRZ,128b/130b 1.969 GB/s 31.508 GB/s
Gen 5 32 GT/s NRZ,128b/130b 3.938 GB/s 63.015 GB/s
Gen 6 64 GT/s PAM4,FLIT/FEC 原始位率折合 8 GB/s,未扣 FLIT 开销 原始 128 GB/s,未扣 FLIT 开销
Gen 7 128 GT/s PAM4,FLIT/FEC 原始位率折合 16 GB/s,未扣 FLIT 开销 原始 256 GB/s,未扣 FLIT 开销
例如 Gen 4×4:16×10⁹×128/130×4/8≈7.877×10⁹ byte/s。全双工意味着另一方向也有相应能力,不能把双向和 15.754 GB/s 当作一次单向 SSD 读取的上限。
Gen 6 的 64 GT/s 使用 PAM4,每个符号承载两个 bit,符号率与标称 GT/s 不再按 NRZ 的方式一一对应。它采用固定 256 byte FLIT,并加入纠错等机制,不能继续乘 128/130 后称为最终有效带宽。一个 FLIT 的 TLP 区可容纳多个事务包,区内还有事务头和可能的填充,应用净载荷不能直接等于整个 FLIT 大小。PCI-SIG:FLIT、PAM4 与 FEC
PCIe 7.0 的 128 GT/s 规范已于 2025 年 6 月发布。规范发布、控制器支持、量产主板和某台机器实际链路能力是不同状态,不以规范世代直接推断当前设备可用速率。PCI-SIG:PCIe 7.0 发布
拓扑:每段是点到点,系统仍可能共享瓶颈
Root Complex 把处理器/内存系统与 PCIe 层级连接起来,Root Port 是相应端口;Endpoint 提供设备功能。Switch 由上行、下行端口扩展层级并路由事务,不会让所有下行端口各自获得完整独占上行。
Root Port 通向主机内存 PCIe Switch 按路由转发事务 Gen 4 ×4 共享上行 SSD A Gen 4 ×4 SSD B Gen 4 ×4 两条下行 x4,并不把一条上行 x4 变成 x8 两块 SSD 同向访问主机:合计仍受上行约 7.877 GB/s 编码后上限约束 忽略包开销的教学拓扑;设备间 P2P、交换路径与平台路由策略需单独分析。
假设两块 SSD 分别有 Gen 4×4 下行,共用 Gen 4×4 上行,同时向主机内存传输 5 GB/s,总需求 10 GB/s,已经高于上行约 7.877 GB/s 的编码后上限。即使忽略其他开销,每块长期维持 5 GB/s 也不可能;实际分配还取决于仲裁和流量,不一定严格各占一半。
芯片组下挂的存储、网络和 USB 也可能共享主机互连。Intel DMI、不同 AMD 平台的芯片组连接有各自宽度与实现,不能固定画成“DMI 本质就是 PCIe x8”或把 AMD 芯片组上行与 Infinity Fabric 直接等同。查具体处理器和主板框图,比背某个品牌统一 lane 数可靠。
P2P 事务可能在局部层级转发,也可能因平台、ACS、IOMMU 或驱动支持而受限。看到同一个 Switch 并不能保证两个设备之间任何 DMA 都可直接进行。内核中的 PCIe 驱动模型
三层协议:链接收正确,不等于设备已完成业务
层次 主要职责 教学上容易混淆的边界
Transaction 内存读写、配置访问、消息等 TLP 请求完成与数据持久化不是同义词
Data Link 相邻链路可靠传递、流控相关机制 不是端到端业务确认
Physical 串行收发、训练、编码与电气链路 训练成功不代表驱动已准备好
在常见非 FLIT 模式中,链路层使用序号、LCRC 和 ACK/NAK 重放机制;Gen 6 FLIT 模式的保护与重放单元不同,不能将老包格式原样泛化。纠错/重放也会消耗带宽,反复可纠正错误不应被简单当作“完全没有性能影响”。
内存写通常为 posted request,不要求返回对应的事务 Completion;内存读是 non-posted request,需要返回 Completion,通常还带数据。写没有 Completion 不等于写可无序随意越过所有操作,更不等于 SSD 已落盘。寄存器读回、DMA 屏障、设备命令完成及存储持久化分别属于不同保证层次。
Requester Completer Memory Read,Tag 0,请求 256 B Memory Read,Tag 1,请求 256 B Completion with Data,Tag 0 Completion with Data,Tag 1 内存读需要返回 Completion:保持多笔在途才能覆盖往返延迟 Tag 用于匹配未完成请求;一个请求可由多个 Completion 返回,返回顺序与限制按协议处理。
Requester 使用 Tag 管理未完成读请求,一个请求可能拆成多个 Completion。读吞吐不仅受链路速率限制,还受可在途的数据量及往返延迟限制。设允许 32 笔、每笔 256 B,平均往返 2 μs,理想窗口上限约为 32×256/2μs=4.096 GB/s,即使链路可用带宽更高也可能填不满。增加并发可以改善,但还会遇到 Tag、buffer、credit、设备和内存限制。Intel:PCIe 吞吐影响因素
MPS 限制事务包的最大数据载荷,MRRS 限制发起读请求的最大尺寸,二者并非同一个参数。更大请求也可能被拆成多个 Completion;调整这些值需符合整条路径和设备能力,不能保证“一律调最大就最快”。
链路没满,为什么还在等数据?
默认32笔、2 μs只给出4.096 GB/s的窗口预算。增加并发后蓝线最终贴到线路上限,继续增加不会超过它;增大RTT则让拐点右移。它解释的是读Completion窗口,不能直接等同于NVMe业务读取的PCIe事务方向。
R ≤ min ( C l ink , RTT N B re q u es t )
正在呈现知识画面 重新加载
链路没满,为什么还在等数据? 读请求必须等Completion释放在途窗口;窗口/往返时间可能先于线路带宽形成上限。
{"id":"pcie-read-window","title":"链路没满,为什么还在等数据?","summary":"读请求必须等Completion释放在途窗口;窗口/往返时间可能先于线路带宽形成上限。","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><div class=\"control\"><label for=\"count\" data-i18n=\"count\"></label><output for=\"count\" id=\"count-value\"></output><input id=\"count\" type=\"range\" min=\"1\" max=\"128\" value=\"32\" step=\"1\"></div><div class=\"control\"><label for=\"rtt\" data-i18n=\"rtt\"></label><output for=\"rtt\" id=\"rtt-value\"></output><input id=\"rtt\" type=\"range\" min=\"0.5\" max=\"8\" value=\"2\" step=\"0.1\"></div><div class=\"chart-wrap\"><div class=\"chart-label\" data-i18n=\"plot\"></div><svg id=\"plot\" class=\"curve\" viewBox=\"0 0 360 210\" role=\"img\" data-i18n-label=\"plot\"></svg></div><div class=\"metrics\"><div><span data-i18n=\"window\"></span><strong id=\"window\"></strong></div><div><span data-i18n=\"rate\"></span><strong id=\"rate\"></strong></div><div><span data-i18n=\"needed\"></span><strong id=\"needed\"></strong></div></div><p id=\"eq\" class=\"formula\"></p><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:550;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:550 20px ui-monospace,monospace}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:ui-monospace,monospace;font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px ui-monospace,monospace;background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n/* Action labels must remain readable across locales and narrow embeds. */\n.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}\n.actions button{overflow-wrap:anywhere;white-space:normal;min-height:58px;padding:8px}\nbutton{overflow-wrap:anywhere}\n@media(min-width:640px){.actions{grid-template-columns:repeat(3,minmax(0,1fr))}}\n\n.metrics span{overflow-wrap:anywhere}\n.panel{border:1px solid var(--rule);border-radius:9px;padding:12px;margin:14px 0;min-width:0}.panel h3{font-size:13px;margin:0 0 10px;color:var(--muted)}.source{font:13px/1.8 ui-monospace,monospace;white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0}.source mark{background:var(--accent-soft);color:var(--ink);border-bottom:2px solid var(--accent)}.source .fault{background:var(--second-soft);border-color:var(--second)}.diagram{display:block;width:100%;height:auto;max-height:320px;margin:14px 0;overflow:visible}.diagram text{fill:var(--ink);font:12px ui-monospace,monospace}.diagram .edge{stroke:var(--muted);stroke-width:1.5;fill:none}.diagram .edge.on{stroke:var(--accent);stroke-width:3}.diagram .node{fill:var(--surface);stroke:var(--rule);stroke-width:1.5}.diagram .node.on{fill:var(--accent-soft);stroke:var(--accent)}.diagram .node.gold{fill:var(--second-soft);stroke:var(--second)}.log{font:12px/1.8 ui-monospace,monospace;white-space:pre-line;overflow-wrap:anywhere;min-height:7.2em}.table{display:grid;gap:5px;margin:14px 0}.table .tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.table .tr>*{padding:8px 5px;background:var(--surface);border-radius:4px;min-width:0;overflow-wrap:anywhere;font-size:12px}.table .tr .on{background:var(--accent-soft)}.table .tr .gold{background:var(--second-soft)}.choices button{min-height:44px}.metrics strong{min-height:1.6em}.code-line{display:block}.code-line.on{background:var(--accent-soft);border-left:2px solid var(--accent);padding-left:6px}.status-badge{font-size:12px;color:var(--accent)}.tile-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.tile-grid .cell{min-height:40px}.actions button{min-height:48px}.form-field{display:grid;gap:8px;margin:14px 0;font-size:13px}.form-field input{width:100%;font-family:ui-monospace,monospace}.metrics.three{grid-template-columns:repeat(3,minmax(0,1fr))}.formula{min-height:3.2em}.presets{flex-wrap:wrap}.presets button{flex-basis:25%;min-height:44px}\n\n.budget{display:flex;height:28px;margin:16px 0;background:var(--surface);border-radius:5px;overflow:hidden}.budget i{display:block;background:var(--accent);transition:width .2s}.budget i.gold{background:var(--second)}.intervals{margin:16px 0}.interval{position:relative;height:36px;background:var(--surface);margin:6px 0;border-radius:4px;overflow:hidden}.interval i{position:absolute;height:100%;background:var(--accent-soft);border-left:2px solid var(--accent)}.interval i.gold{background:var(--second-soft);border-color:var(--second)}.interval span{position:relative;z-index:1;font:12px/36px ui-monospace,monospace;padding-left:7px}.table .tr>*{min-height:4.8em}.formula{white-space:pre-line;min-height:7.2em}.actions:has(>button:only-child){grid-template-columns:1fr}.metrics strong{min-height:3.2em}.rangegrid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:3px;margin:14px 0}.rangegrid .cell{min-height:30px;padding:3px 1px;font-size:10px}.message{min-height:7.2em;white-space:pre-line;font:13px/1.8 ui-monospace,monospace;overflow-wrap:anywhere}\n/* Allow CJK hanging punctuation without overflowing a metric's border box. */\n.metrics>div{padding-right:4px}.metrics span{line-break:anywhere}\n\n.chart-wrap{margin:18px 0}.chart-label{font-size:12px;color:var(--muted);min-height:3.2em}.curve{display:block;width:100%;height:auto;max-height:320px;overflow:visible}.curve text{font:11px ui-monospace,monospace;fill:var(--muted)}.gridline{stroke:var(--rule);stroke-width:1}.metrics strong{font-size:20px;min-height:3.2em}.formula{font:12px/1.8 ui-monospace,monospace;min-height:9em}.readout{min-height:8em}.presets button{flex-basis:30%}.scope p{overflow-wrap:anywhere} .unit-note{font-size:12px;color:var(--muted);margin:10px 0}\n\n.metrics span{word-break:break-all;line-break:strict}.metrics>div{padding-right:6px}\n\n","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}const esc=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));const fmt=s=>'{'+[...s].sort().join(', ')+'}';function graph(id,nodes,edges){const el=$('#'+id);el.setAttribute('viewBox','0 0 360 200');el.innerHTML='<defs><marker id=\"arrow-'+id+'\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto-start-reverse\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"var(--muted)\"/></marker></defs>'+edges.map(e=>{const a=nodes[e[0]],b=nodes[e[1]],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1;if(edges.some(r=>r[0]===e[1]&&r[1]===e[0])){const nx=-dy/d,ny=dx/d;return '<path class=\"edge '+(e[2]?'on':'')+'\" d=\"M '+(a.x+dx/d*22+nx*12)+' '+(a.y+dy/d*22+ny*12)+' Q '+((a.x+b.x)/2+nx*38)+' '+((a.y+b.y)/2+ny*38)+' '+(b.x-dx/d*25+nx*12)+' '+(b.y-dy/d*25+ny*12)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}return '<line class=\"edge '+(e[2]?'on':'')+'\" x1=\"'+(a.x+dx/d*25)+'\" y1=\"'+(a.y+dy/d*25)+'\" x2=\"'+(b.x-dx/d*28)+'\" y2=\"'+(b.y-dy/d*28)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}).join('')+nodes.map(n=>'<g><circle class=\"node '+(n.cls||'')+'\" cx=\"'+n.x+'\" cy=\"'+n.y+'\" r=\"25\"/><text x=\"'+n.x+'\" y=\"'+(n.y+4)+'\" text-anchor=\"middle\">'+esc(n.name)+'</text>'+(n.sub?'<text x=\"'+n.x+'\" y=\"'+(n.y+43)+'\" text-anchor=\"middle\">'+esc(n.sub)+'</text>':'')+'</g>').join('');}function table(id,rows){$('#'+id).classList.add('table');$('#'+id).replaceChildren(...rows.map(r=>{const row=document.createElement('div');row.className='tr';row.style.gridTemplateColumns='repeat('+r.length+',minmax(0,1fr))';r.forEach(x=>{const c=document.createElement('div');if(typeof x==='object'){c.textContent=x.text;c.className=x.cls||'';}else c.textContent=x;row.append(c)});return row;}));}function band(id,parts,total){$('#'+id).replaceChildren(...parts.map((p,i)=>{const e=document.createElement('i');e.style.width=(100*p.value/total)+'%';e.className=i%2?'gold':'on';e.title=p.name+': '+p.value;return e}));}function intervals(id,items,total){$('#'+id).replaceChildren(...items.map(p=>{const row=document.createElement('div');row.className='interval';const bar=document.createElement('i');bar.style.left=(100*p.start/total)+'%';bar.style.width=(100*(p.end-p.start)/total)+'%';bar.className=p.cls||'';const label=document.createElement('span');label.textContent=p.label;row.append(bar,label);return row;}));}function plot(id,series,xr,yr,opts={}){const e=$('#'+id),X=x=>42+(x-xr[0])/(xr[1]-xr[0])*298,Y=y=>175-(y-yr[0])/(yr[1]-yr[0])*148;let out='<defs><clipPath id=\"clip-'+id+'\"><rect x=\"42\" y=\"27\" width=\"298\" height=\"148\"/></clipPath></defs>';for(let i=0;i<3;i++){let x=xr[0]+i*(xr[1]-xr[0])/2,y=yr[0]+i*(yr[1]-yr[0])/2;out+='<path class=\"gridline\" d=\"M '+X(x)+' 27V175M42 '+Y(y)+'H340\"/><text x=\"'+X(x)+'\" y=\"194\" text-anchor=\"middle\">'+esc(opts.xfmt?opts.xfmt(x):Number(x.toFixed(2)))+'</text><text x=\"36\" y=\"'+(Y(y)+4)+'\" text-anchor=\"end\">'+esc(opts.yfmt?opts.yfmt(y):Number(y.toFixed(2)))+'</text>';}out+='<g clip-path=\"url(#clip-'+id+')\">';for(const s of series){out+='<path fill=\"none\" stroke=\"'+(s.color||'var(--accent)')+'\" stroke-width=\"2.5\" '+(s.dash?'stroke-dasharray=\"5 4\"':'')+' d=\"'+s.pts.map((p,i)=>(i?'L':'M')+X(p[0]).toFixed(2)+','+Y(p[1]).toFixed(2)).join(' ')+'\"/>';}for(const p of opts.points||[])out+='<circle cx=\"'+X(p[0])+'\" cy=\"'+Y(p[1])+'\" r=\"4\" fill=\"var(--second)\" stroke=\"var(--paper)\" stroke-width=\"1.5\"/>';if(opts.cursor!==undefined)out+='<path d=\"M'+X(opts.cursor)+' 27V175\" stroke=\"var(--muted)\" stroke-dasharray=\"3 3\"/>';e.innerHTML=out+'</g>';e.dataset.curves=JSON.stringify(series.map(s=>s.pts));}const curve=(fn,lo,hi,n=120)=>Array.from({length:n+1},(_,i)=>{const x=lo+(hi-lo)*i/n;return[x,fn(x)]});function inputs(draw){document.querySelectorAll('input,select').forEach(e=>{e.addEventListener('input',draw);e.addEventListener('change',draw)});draw()}const n=id=>+$('#'+id).value;const show=(id,v,d=3)=>set(id,Number(v.toFixed(d)));function draw(){let count=n('count'),rtt=n('rtt'),cap=16*128/130*4/8,rate=x=>Math.min(cap,x*256/rtt/1000);show('count-value',count);show('rtt-value',rtt);plot('plot',[{pts:curve(rate,0,128)},{pts:[[0,cap],[128,cap]],dash:true,color:'var(--second)'}],[0,128],[0,9],{points:[[count,rate(count)]]});show('window',count*256);show('rate',rate(count),4);show('needed',Math.ceil(cap*1000*rtt/256));set('eq','Bwindow = N × 256 B\\nR ≤ min(7.877, Bwindow/RTT) GB/s\\nNneeded = ceil(Clink × RTT / 256 B)');}inputs(draw);","audio":false,"strings":{"count":"在途请求上限","heading":"链路没满,为什么还在等数据?","intro":"调整在途请求数与往返时间,观察窗口和链路两种上限。","needed":"填满所需最少请求数","plot":"横轴请求数,纵轴 GB/s;虚线:链路上限","rate":"预算上限(GB/s)","reset":"重置","rtt":"往返时间(μs)","scope":"Gen4×4编码后7.877 GB/s,每请求256 B;固定平均RTT,忽略TLP、流控、内存和设备开销。","scopeLabel":"模型范围与约定","window":"在途字节(B)"}}
从上电到驱动使用:训练与枚举分两步理解
物理链路经过接收检测、训练序列、宽度/速率建立和相应均衡进入可用状态,常用 LTSSM 描述这些状态转换。两端最高能力、布线、固件策略及信号质量决定最终结果,可能降到更窄或更低速率;恢复状态也可能用于重新训练。
随后固件/操作系统通过配置空间发现设备和桥,分配总线号与资源窗口。BAR 描述设备需要的地址空间区域,主机分配地址后,驱动才能映射并按器件规则访问 MMIO。BAR 不是普通内存条,也不是把物理设备“拷贝进 RAM”。
设备进行 DMA 时还需要总线主控能力、正确映射、IOMMU 权限及驱动同步。链路已经 L0、lspci 能列出设备,却没有数据传输,可能是驱动、资源或 DMA 配置问题,并非必然需要换插槽。IOMMU 与 DMA API
物理设计不能只背一组等长数字
PCIe 串行接收通过 CDR 从数据恢复时序,但许多平台仍提供参考时钟;“嵌入时钟”不等于电路板没有 REFCLK。Common Clock、SRNS、SRIS 是不同参考方案,必须与两端能力及 SSC 配置匹配,不能断言现在全部采用 SRIS。Intel:SRIS 的适用背景
交流耦合通常按发送路径安排,每个方向都有相应发送端;不要在链路两端机械地各加一套电容。电容值、位置、差分阻抗、损耗、过孔残桩和长度偏差要求,应按目标规范、封装与厂商布线指南确定。对内偏斜与 lane 间偏斜是不同约束,统一写“所有 P/N 必须小于 5 mil”既缺少条件,也掩盖更重要的损耗预算。
Redriver 主要补偿模拟信号损耗,Retimer 还进行时钟/数据恢复和重新发送;二者功耗、延迟、兼容与训练支持不同,不能认为加一颗放大器就可无限延长链路。持续参考平面、回流与连接器过渡仍是基础。
NVMe、ReBAR、CXL 分别在 PCIe 上增加什么
NVMe 是存储命令与队列模型,PCIe 是常见本地传输基础之一;NVMe over Fabrics 还可使用其他网络传输。多队列让不同提交者减少争用、维持并发,但规范可表示的队列数/深度不代表每个控制器都实现上限。磁盘读在业务语义上是“读”,控制器把数据送入主机 RAM 时却通常发 PCIe Memory Write,不能混淆业务方向和事务类型。Intel:I/O 数据流示例
Resizable BAR 允许从设备支持的大小中选择 BAR 窗口,更大窗口可能减少显存窗口重映射等开销。256 MiB 是某些旧 GPU 的常见窗口,不是所有 PCIe BAR 的统一上限;ReBAR 也不自动提高 lane 带宽或保证减少所有拷贝。固件地址空间、GPU、驱动和应用共同决定可用性与收益。PCI-SIG:Resizable BAR
CXL 利用 PCIe 物理基础,增加 CXL.io、CXL.cache、CXL.mem 等协议语义,为一致性和内存访问场景服务。普通 PCIe 设备不会仅因插入相似插槽就变成 CXL 内存设备;主机、设备、固件及软件都需对应支持。CXL 联盟:协议与一致性定位
GPU 工作负载对 PCIe 的敏感程度取决于数据是否长期驻留显存、是否频繁交换、显存容量与同步方式。从 x16 降到 x8 的影响需要对真实工作集测量,不能由 GPU 系列名称推算固定损失比例。
在 Linux 先看当前链路,再看最大能力
lspci - t
lspci - nn
lspci - s 0000:01:00.0 - vv
关注 LnkCap(能力)和 LnkSta(当前速度/宽度),并向上检查桥及根端口。读配置空间受到权限限制时,输出可能不完整。Linux PCI sysfs 还提供 current_link_speed、current_link_width、max_link_speed、max_link_width 等只读属性;某些设备或配置下不一定存在全部字段。具体字段及单位可查阅 Linux PCI sysfs ABI 。
不要用一次空闲采样断言负载状态。设备可能动态降速节能,链路异常也可能真正导致降级,应结合负载、AER 日志和路径拓扑分析。这里仅给读取方法,没有修改链路控制、重训练或资源寄存器。
复算编码与在途窗口
from fractions import Fraction as F
per_lane = F ( 16_000_000_000 ) * F ( 128 , 130 ) / 8
x4 = per_lane * 4
assert x4 == F ( 102_400_000_000 , 13 )
assert float ( x4 ) / 1e9 < 8
assert 2 * 5e9 > x4
window_bytes = 32 * 256
window_rate = F ( window_bytes ) / F ( 2 , 1_000_000 )
assert window_rate == 4_096_000_000
assert window_rate < x4
needed = x4 * F ( 2 , 1_000_000 )
assert needed > window_bytes
print ( ' Gen 4×4 编码后单向上限:' , round ( float ( x4 ) / 1e9 , 6 ) , ' GB/s' )
print ( ' 32 笔 256 B、2 μs 往返的窗口上限:' , float ( window_rate ) / 1e9 , ' GB/s' )
以上只验证明确假设下的预算,没有测试实际 GPU、SSD 或 PCIe 吞吐。
继续查阅