GPU Gems 2 · Chapter 29. Streaming Architectures and Technology Trends

从算力与通信增速差异出发,解释 GPU 为什么用长数据流、独立 kernel、三级并行、片上局部性与深流水换取高吞吐。

学习目标

  • 能解释 stream architecture 在 GPU 执行与存储层次中的位置,以及算力、通信、延迟和功耗趋势为何推动这种组织
  • 能把一段批处理改写为 stream 与独立 kernel,标出 task/data/instruction parallelism、片上局部性和外部流量
  • 能回答:给定元素数、每元素运算量、全局读写量与局部性时,工作负载更可能受计算还是通信限制,应先改算法、数据布局还是并行规模

更多机器,不等于工厂自动更快

工厂可以不断增加工作台,但原料如果仍从远处逐件运入,新工作台只会等待。真正的改进是把相似零件成批送入,让多条工序同时运转,并让上一道工序的产物直接交给下一道。

处理芯片也面临同样的不平衡:计算部件增加得快,搬运数据和等待数据的改善却慢。没有暴露足够多的独立工作,或让中间结果反复离开芯片,再多计算资源也会闲置。先预测:实验里保持元素数不变,把每元素全局读取翻倍并把局部性降到零,单纯增加算术操作能否恢复吞吐?

Streaming Architectures 实验

先预测:保持元素数不变,把 global words 加倍、locality 降低,增加算术操作还能否让并行单元持续忙碌?

streaming architecture lab · stream executionelement lanes and communication pathoff-chip traffic · 1,024 wordsmeasured workload factsarithmetic intensity12.0lane utilization100%latency covered100%limiting pathcommunicationtraffic dominates:提高 locality、压缩或增加每次读取后的有效计算

长 stream 暴露数据并行;局部中间结果和多个 resident waves 共同隐藏外部延迟。

本章主题 Streaming Architectures and Technology Trends 的核心是一条因果链:技术趋势扩大计算与通信的增速差,架构因此偏向高密度数据通路、显式并行、局部通信和吞吐优先的执行模型。

1. 技术趋势:关键不是都变快,而是斜率不同

原章引用 2003 年 ITRS 路线图,以约 50%/年的芯片能力增长、25%/年的 DRAM bandwidth 增长和仅 5%/年的 DRAM latency 改善解释不平衡。它还列出 GeForce FX 5800、FX 5950 与 GeForce 6800 的可编程浮点能力相对外部带宽约为 2、2.66、接近 6 operations per word。这些数值只描述当时的代际,不是现代 GPU 基准。

三项都在进步,但不同斜率重写了架构约束normalized historical trend from the chapter1×2×3×4×compute capability · ~50%/yearDRAM bandwidth · ~25%/yearDRAM latency improvement · ~5%/year原章 2005 年预测数据用于解释斜率差;现代项目必须重新测量绝对值

三项趋势导出三种压力。第一,跨芯片甚至跨芯片内部区域的通信相对算术更贵,可能值得用 recompute 或专用逻辑替代搬运。第二,带宽改善快于单次访问延迟,硬件必须在等待时继续执行其他工作。第三,晶体管总数增长快于单管功耗下降,原章预见 operations per watt 会比绝对 operations per second 更重要。

2. 芯片面积:control、data path 与 storage 的取舍

同一块 die 上的晶体管大致分给 control、data path 与 storage。复杂分支预测、乱序执行和大缓存提高单线程通用性与低延迟,却占用本可复制更多算术单元的面积;图形工作负载拥有大量同构元素和较规则控制,GPU 可以把更多预算交给并行 data path,并让 rasterization 等稳定任务使用专用单元。

控制、data path、storage 争夺同一块芯片预算latency-orientedcontrolillustrative 44%data pathillustrative 24%storageillustrative 32%throughput-orientedcontrolillustrative 16%data pathillustrative 62%storageillustrative 22%比例是教学示意;关键取舍是单线程灵活性与并行 data path 密度

专用化不是“固定功能永远优于可编程”。它用适用范围换单位面积与单位功耗效率;如果需求变化过快,硬连线功能可能失去价值。原章描述的演进正是图形管线从固定功能逐步加入 vertex 与 fragment programmability,同时保留适合专用实现的阶段。

3. 用三级并行填满 data path

原章区分三种可叠加的并行。task parallelism 让顺序阶段同时处理不同批次,例如 vertex stage 处理批次 B 时 raster stage 处理批次 A;data parallelism 让同一阶段同时处理多个独立元素;instruction parallelism 则在单个元素内部重叠互不依赖的简单操作。

同一工作负载可同时暴露 task、data 与 instruction parallelismtask parallelismvertex · batch 1raster · batch 2fragment · batch 3data parallelismsame kernel · independent elementsinstruction parallelismmuladdfetchconvert只有程序显式暴露独立工作,硬件才有机会填满这些并行层级

三级并行不能靠硬件凭空创造。输入太短会使 lanes 不满;跨元素依赖会阻止 data-parallel 映射;长串行依赖会减少 instruction overlap;某个 stage 远慢于其他 stage 又会让整条 task pipeline 堵塞。程序需要把独立性和通信边界写进结构,硬件才能安全调度。

4. stream 与 kernel:把独立性写进程序模型

stream 可以是 numbers、points、triangles 或 matrices。典型 kernel 对每个输入元素执行同一函数,也就是 map;模型还允许 expansion、reduction 与 filter。限制 kernel 输出只依赖输入、且单个 kernel 内元素相互独立,使编译器提前知道局部数据需求,也使表面串行的 element loop 可直接映射到并行硬件。

stream 是有序同类型元素,kernel 是对整条 stream 的批量变换vertex kernelindependent elementsassembly / clipindependent elementsraster kernelindependent elementsfragment kernelindependent elementsmap 是典型操作;expand、reduce、filter 改变 stream 形状kernel 内跨元素依赖会破坏直接的数据并行映射

图形管线天然符合这种模型:vertex、assembly、clipping、rasterization 和 fragment processing 是连接的 kernels,阶段间通过 streams 传递,局部输出很快被下一阶段消费。模型不是让通信消失,而是使“何处传递、传什么、能否保留在片上”变得显式。

const shaded = vertices.map((vertex) => shadeVertex(vertex, uniforms));
const triangles = assembleTriangles(shaded);
const fragments = rasterize(triangles);
const visible = fragments.filter((fragment) => depthPasses(fragment));
const colors = visible.map((fragment) => shadeFragment(fragment, textures));

这段代码是结构镜像,不代表 JavaScript 数组会自动获得 GPU 性能。真实实现必须把每个 stage 映射到目标 API 的 dispatch/draw、buffer、同步和 layout,并确认中间 streams 没有因不必要的 host round-trip 离开设备。

5. arithmetic intensity:每个外部 word 做多少工作

本节符号如下:

  • NN:stream 元素数
  • OO:每元素有效 operations
  • WW:每元素访问的 global words
  • hh:被片上复用、缓存或消除的比例
  • II:相对 off-chip traffic 的 arithmetic intensity

总有效运算和外部 word 数分别是

C=NO,M=NW(1h).C=NO,\qquad M=NW(1-h).

这个式子在说:元素数同时放大计算与原始流量,而片上局部性 hh 只减少真正离开芯片的数据量。

因此

I=CM=OW(1h).I=\frac{C}{M}=\frac{O}{W(1-h)}.

这个式子在说:减少一次外部读取、提高复用或在已读数据上做更多有用工作,都能提升每个外部 word 的计算量;单纯扩大 NN 只增加并行规模,不会改变这个比值。

算力增长快于外部通信,先消灭 traffic,再谈增加算术单元global round-tripskernel Akernel Boff-chip memorylocal stream pathkernel Akernel Bcachecompressrecompute三种方法都在用晶体管或计算换取更少的 off-chip bandwidth

缓存用 storage transistors 换 bandwidth,compression 用压缩/解压计算和逻辑换 bandwidth,recompute 则直接用廉价计算替代查询表或远端传输。选择前要检查数据是否会复用、压缩率是否稳定、重算是否精确,以及新增计算会不会把通信瓶颈反转成 data-path 瓶颈。

6. latency tolerance:等待时切换到 ready work

高吞吐架构不要求每个元素尽快完成,而是要求单位时间完成尽可能多的元素。某个 wave 发出 memory request 后,若还有其他 ready waves,处理单元可以继续发射它们;数据返回后再恢复原 wave。访问 latency 依然存在,只是被独立工作覆盖。

latency 没有消失:用其他 ready work 覆盖等待窗口processing-unit timelinewave 0memory waitwave 1memory waitwave 2memory waitwave 3memory waitissuecomplete长 stream + 独立元素 + 深流水,才能在等待期间持续发射工作

长 stream 提供更多可切换元素,深 pipeline 让多个阶段重叠,局部状态则避免每次切换都访问外部 memory。若 stream 太短、寄存器占用让 resident waves 太少,或所有 waves 同时依赖同一个远端结果,延迟就会重新暴露为 stall。

7. 从历史架构动机到现代工作负载判断

原章认为 programmable vertex/fragment stages 已让 GPU 成为可处理图形之外任务的 stream processor,并展望更广的 programmability、阶段资源共享、功耗管理,以及 CPU/GPU 功能继续融合。这些预测应作为历史脉络阅读;现代接口和微架构已演进,但判断工作负载的方法仍可复用。

先检查是否有数百个以上同类型、相互独立的元素;再标记 global inputs/outputs 与 stage-local intermediates;然后计算有效 operations 和 bytes/words,观察 communication、compute、control divergence 或 occupancy 哪条限制吞吐;最后才决定 fusion、tiling、compression、recompute 或扩大并行规模。

一个 serial、分支复杂、每步依赖上一步且必须低延迟返回的任务,未必适合吞吐导向 GPU。反过来,长 stream、规则 kernel、高局部性和允许批量完成的任务,正好匹配本章描述的架构契约。选择不是“CPU 或 GPU 谁更强”,而是程序暴露的结构与目标机器资源是否一致。

三步验收:独立性、流量、等待

三个阶段对同一 workload 保留 CPU reference、元素计数、operations、global words 与输出 hash。先证明并行拆分正确,再测量流量,最后观察延迟是否被 ready work 覆盖。

分步1 / 3

第一步:画出 stream、kernel 与依赖边界

为每个 kernel 标出 input/output types、element count 和跨元素读写。任何同一 pass 内的邻居依赖、共享写冲突或全局顺序,都必须先改写为独立 passes 或显式 reduction。

stream 是有序同类型元素,kernel 是对整条 stream 的批量变换vertex kernelindependent elementsassembly / clipindependent elementsraster kernelindependent elementsfragment kernelindependent elementsmap 是典型操作;expand、reduce、filter 改变 stream 形状kernel 内跨元素依赖会破坏直接的数据并行映射

本章小结

  • 算力、带宽、延迟与功耗的不同斜率推动吞吐架构。
  • GPU 用更多 data path 和三级并行处理规则批量工作。
  • stream/kernel 显式表达元素独立性与阶段通信。
  • arithmetic intensity 衡量每个外部 word 的有效工作。
  • latency tolerance 依赖足够多 ready work,而非消除延迟。

练习

问题 1|手算强度。 一个 kernel 处理 4096 个元素,每元素 32 次有效 operations、读写 8 个 global words;tiling 让 75% 数据留在片上。优化前后 arithmetic intensity 各是多少?元素数为何不影响比值?

问题 2|拆分 kernel。 某循环让元素 ii 先读取刚更新的元素 i1i-1,随后所有元素把结果累加到一个全局值。如何改造成可验证的 stream pipeline?

问题 3|修改实验并提出优化。 在 StreamingLab 中选择 serial,把 stream elements 降到 32、global words 调到 12、locality 调到 0。依次改变哪两个参数最能区分“并行不足”和“通信过重”?

名词解释

本章出现的专业名词,用大白话再讲一遍。

data path
data parallelism
stream
kernel
arithmetic intensity
latency tolerance

讨论

评论区加载中…