Cray-1
向量寄存器+多存储体交织内存,向量超算的奠基设计
CS152 · 第 17 课
从这一课开始,课程进入 Unit 4——数据级并行。乱序执行、VLIW、多线程都在想方设法从"看起来顺序"的代码里挖掘并行;向量处理器换了一个思路:让程序员/编译器直接用一条指令表达"对一整个数组做同样的操作",硬件只需要老老实实地把这份显式并行流水化执行。
1970-80 年代,向量机就是超级计算机的代名词。Cray-1(1976)是这一课的核心案例:标量单元 + 向量扩展,内存系统 16 个存储体(64 位字 + 8 位 SECDED 校验),存储体周期 50ns,处理器周期 12.5ns(对应 80MHz,重新验算:1/12.5ns=80MHz,与幻灯片一致);8 个 64 元素向量寄存器(V0-V7,每个还配一个向量掩码寄存器和向量长度寄存器);标量寄存器 S0-S7、地址寄存器 A0-A7;4 个指令缓冲区实现前瞻取指(lookahead)。
相对标量寄存器 x0-x31,向量寄存器 v0-v31 每个能容纳 VLMAX(向量寄存器能容纳的最大元素数)个元素;向量长度寄存器 vl 决定当前向量操作实际作用的元素范围 [0]..[vl-1]。向量算术指令(如 vadd v3,v1,v2)对每个元素索引并行执行同一个运算;向量访存指令(如 vls v1,(x1),x2)用标量寄存器 x1 存基地址、x2 存步长(stride),从内存中加载进向量寄存器 v1。这套指令集天然紧凑(一条指令表达整个循环)、表达力强、且可扩展(同一份向量代码可以在不同硬件实现的向量长度上运行)。
向量运算天然适合流水化:多个功能单元并行处理,吞吐率随功能单元数线性提升——用 1 个 3 级流水化功能单元逐个处理元素对,还是用 4 个并行的 3 级流水化功能单元同时处理 4 组元素,后者吞吐率是前者的 4 倍。真实硬件把这种并行组织成通道(lane):每条 lane 包含一套完整的功能单元流水线,加上按跨步存储的一部分向量寄存器元素(例如 4 通道机器里,lane 0 存元素 0,4,8,…,lane 1 存元素 1,5,9,…)。T0 向量微处理器(UCB/ICSI, 1995)的真实裸片照片直观展示了向量寄存器元素是如何按通道条带化分布的。
存储器同样需要跟上多通道的访存带宽,因此采用交织存储体:Cray-1 有 16 个存储体,每体的存储体忙碌时间(bank busy time)为 4 个周期,访存延迟 12 个周期——地址生成器用基地址+步长依次算出每次访问落在哪个存储体上。
一条向量指令的启动代价由两部分组成:功能单元延迟(流经流水线本身需要的时间)和死区时间(dead time)(下一条向量指令能够开始进入流水线之前,必须等待的额外时间)。
短向量下的效率对比(重新验算):Cray C90(2 条 lane,4 周期死区时间)处理 128 元素向量:128÷2=64 个有效周期,总周期 64+4=68,效率 64/68≈94.1%——与幻灯片给出的"94%"一致;T0(8 条 lane,无死区时间)处理仅 8 元素的向量:8÷8=1 个有效周期,0 个死区周期,效率 1/1=100%——与幻灯片一致。这组对比直观说明:lane 数越多、死区时间越短,向量机在短向量上的效率损失就越小——这对追求短向量高效率的机器(如后来的 RISC-V Vector)是一条重要的设计线索。
向量链接被幻灯片直接定义为"寄存器旁路的向量版本":一条依赖指令不必等生产者指令把整个向量寄存器都写完,只要生产者的第一个元素结果出来,消费者指令就可以立刻开始消费。以 Load → Mult → Add 三级依赖链为例:没有链接时,Load、Mult、Add 三个阶段完全串行;有链接时,三个阶段的执行窗口彼此重叠/错开,Mult 在 Load 刚出第一个结果时就开始,Add 同理——这正是向量机能把一条长依赖链的延迟压缩到接近"单条流水线延迟+若干点缀"而非"三条流水线延迟相加"的关键机制。配合多通道并行,一台 32 元素/向量寄存器、8 通道的机器可以做到每周期发射 1 条短指令、却完成 24 次运算(重新验算:3 个功能单元 × 8 通道 = 24,与 Load/Multiply/Add 三个功能单元同时各自处理 8 宽通道组的图示完全吻合)。
向量掩码寄存器是"谓词寄存器的向量版本",每元素 1 位;掩码为 0 的元素处,向量操作变成气泡(NOP)。两种实现方式:简单实现——照常执行全部 N 个操作,只是根据掩码决定是否真正写回结果;密度时间实现(Density-Time)——直接扫描掩码,只真正执行掩码非零的那些元素,处理的输入更少更密集。压缩(Compress)把某个向量寄存器中掩码为真的元素紧凑地打包到目的寄存器的起始位置(掩码向量的"1 的个数"就是压缩后向量的长度);展开(Expand)做相反的操作。向量规约(如 sum += A[i] 这类循环携带依赖)通过把归约变量重新结合成向量寄存器内的多个部分和,再用二叉树方式两两相加来实现。Gather(聚集)是一种索引化的 load(如 vlx vC,(xC),vD,地址为 xC + vD[i]);Scatter(分散)是对应的索引化 store。
问题 1:Cray C90(2 条 lane,4 周期死区时间)处理一个 128 元素的向量,效率大约是多少?
问题 2:"向量链接(chaining)"具体解决了什么问题?
问题 3:"密度时间实现(Density-Time Implementation)"和"简单实现(Simple Implementation)"处理带掩码向量指令时的关键区别是什么?
向量寄存器+多存储体交织内存,向量超算的奠基设计
每条 lane 一套功能单元流水线+条带化的寄存器元素
功能单元延迟+死区时间,lane 越多、死区越短,短向量效率越高
向量版寄存器旁路,让依赖链上的流水线阶段彼此重叠
sp23/lecture_slides/L17_Vectors.pdf(UC Berkeley CS152/CS252A, Spring 2023, Sophia Shao)