习题
作者:Jason D. Bakos。
习题 3.12
[25] 〈3.11〉 本题探讨三种采用不同多线程(MT)方式的处理器之间的性能取舍。它们都是超标量处理器,采用顺序流水线,所有加载与分支之后都必须固定停顿 3 个周期,且具有相同的 L1 缓存。同一线程在同一周期发射的指令,按程序顺序读取,彼此不能存在任何数据依赖或控制依赖。
应用是一个列表搜索程序,在 x16 与 x17 指定的地址范围内扫描内存区域,寻找保存在 x9 中的特定值。将搜索空间均分为四个大小相等、各自连续的块,每块分配一个搜索线程,从而并行化为四个线程。每个线程的大部分运行时间都花在以下展开后的循环体中:
假定:
- 用屏障保证所有线程同时开始。
- 第一次 L1 缓存未命中发生在循环完成两次迭代之后。
- 所有 BEQAL 分支都不跳转。
- BLT 总是跳转。
- 三种处理器都按轮转方式调度线程。
- 处理器 A 是超标量同时多线程体系结构,每周期最多发射来自两个线程的合计两条指令。
- 处理器 B 是细粒度多线程体系结构,每周期最多从单个线程发射四条指令,遇到任何流水线停顿就切换线程。
- 处理器 C 是粗粒度多线程体系结构,每周期最多从单个线程发射八条指令,在 L1 缓存未命中时切换线程。
求各处理器完成循环前两次迭代所需的周期数。
译注:原题假设中的“BEQAL”与代码的
beq不一致,daddiu及#64写法也是原页内容,均未改写为另一套指令。本题对细粒度切换时机的定义也按题设保留。
习题 3.13
[25/25/25] 〈3.1, 3.2, 3.9〉 本题考察软件技术如何从常见向量循环中开发指令级并行性(ILP)。以下循环称为 DAXPY 循环,即双精度 ,是 Gauss 消元中的核心操作。代码对长度为 100 的向量执行 DAXPY:。初始时,R1 设为数组 X 的基地址,R2 设为 Y 的基地址:
译注:正文使用 R1、R2,代码使用 x1、x2;第一条指令写入 x4,原注释却写“x1 = 上界”。均按原书保留。
假定功能单元延迟如下表。采用一个周期的延迟分支,在 ID 阶段确定分支结果。所有结果都支持完全旁路。
| 产生结果的指令 | 使用结果的指令 | 延迟(时钟周期) |
|---|---|---|
| 浮点乘法 | 浮点 ALU 操作 | 6 |
| 浮点加法 | 浮点 ALU 操作 | 4 |
| 浮点乘法 | 浮点存储 | 5 |
| 浮点加法 | 浮点存储 | 4 |
| 整数操作及所有加载 | 任意 | 2 |
a. [25]〈3.1〉 假定单发射流水线。给出未经编译器调度的循环,以及由编译器针对浮点操作延迟和分支延迟进行调度后的循环,标出所有停顿或空闲时钟周期。未经调度和调度后,结果向量 Y 的每个元素分别需要多少执行周期?如果只依靠处理器硬件,时钟需要快多少,才能达到调度编译器带来的性能提升?忽略提高时钟速度对存储系统性能可能产生的影响。
b. [25]〈3.2〉 假定单发射流水线。充分展开循环并合并循环开销指令,使其能够在没有任何停顿的情况下调度。必须展开多少次?给出指令调度。每个结果元素的执行时间是多少?
c. [25]〈3.9〉 假定 VLIW 处理器的每条指令包含五个操作,如图 3.28 所示。比较两种循环展开程度:先将循环展开 6 次,以开发 ILP,合并循环开销指令,调度到没有任何停顿,即没有发射周期完全空闲;然后重复这一过程,但改为展开 10 次。忽略分支延迟槽。给出两种调度。每种调度下,结果向量每个元素的执行时间是多少?操作槽位的利用率各是多少?两种调度的代码大小相差多少?各自总共需要多少寄存器?
习题 3.14
[20/20] 〈3.3, 3.6〉 本题考察动态调度处理器运行习题 3.13 中循环时的表现。功能单元如下表:
| 功能单元类型 | EX 周期数 | 功能单元数量 | 保留站数量 |
|---|---|---|---|
| 整数 | 1 | 1 | 5 |
| 浮点加法器 | 10 | 1 | 3 |
| 浮点乘法器 | 15 | 1 | 2 |
假定:
- 功能单元没有流水化。
- 功能单元之间没有转发,结果在指令完成时传递。
- 执行阶段 EX 同时完成加载、存储的有效地址计算与内存访问,因此流水线为 IF/ID/DIS/EX/WB。
- 加载需要一个时钟周期。
- 派发阶段 DIS 和结果写回阶段 WB,各需要一个时钟周期。
- 有五个加载缓冲槽位和五个存储缓冲槽位。
- 非零分支指令 BNEZ 需要一个时钟周期。
a. [20]〈3.3, 3.6〉 使用单发射流水线,流水线延迟取上表数值。对于循环的三次迭代,给出每条指令的停顿周期数,以及开始执行、即进入第一个 EX 周期的时钟周期。每次循环迭代需要多少个周期?请用包含以下各列的表格回答:
- 迭代:循环迭代编号。
- 指令。
- 派发:指令派发的周期。
- 执行:指令执行的周期。
- 内存访问:访问内存的周期。
- 完成:结果可供其他指令使用的周期。
- 说明:描述该指令正在等待的事件。
表格应展示三次循环迭代。可以忽略第一条指令。
b. [20]〈3.3, 3.6〉 重做 a,但改为双发射处理器,并假定浮点单元(FPU)完全流水化。
习题 3.15
[10] 〈3.6〉 假定动态调度处理器每周期只能向等待中的指令广播、即转发一个功能单元的结果。使用上一题 3.14 的硬件配置和延迟,找出一个不超过 10 条指令的代码序列,使处理器必须因这一限制产生的结构冒险而停顿。指出停顿发生在序列中的什么位置。
习题 3.16
[20] 〈3.4〉 一个 相关分支预测器,用最近执行的 个分支的行为,从 个预测器中选择一个;每个预测器都是 位预测器。两级局部预测器的工作方式相似,但只跟踪各个分支自身的历史行为,用来预测其未来行为。
这类预测器存在设计取舍:相关预测器只需要很少的存储空间保存历史,因此可以为大量独立分支维护 2 位预测器,降低不同分支指令复用同一预测器的概率;局部预测器则需要更多空间保存历史,因此只能跟踪相对较少的分支指令。
本题比较一个可跟踪四个分支的 相关预测器,需要 16 位,与一个使用相同存储空间、可跟踪两个分支的 局部预测器。对于下面给出的分支结果序列,列出每次预测、所用表项、预测结果导致的所有表项更新,以及每个预测器最终的错误预测率。假定截至此时,所有分支都已跳转。按以下状态初始化预测器。
相关预测器
| 表项 | 分支 | 上次结果 | 预测 |
|---|---|---|---|
| 0 | 0 | T | T,已有一次错误预测 |
| 1 | 0 | NT | NT |
| 2 | 1 | T | NT |
| 3 | 1 | NT | T |
| 4 | 2 | T | T |
| 5 | 2 | NT | T |
| 6 | 3 | T | NT,已有一次错误预测 |
| 7 | 3 | NT | NT |
局部预测器
| 表项 | 分支 | 最近两次结果(右侧为最近一次) | 预测 |
|---|---|---|---|
| 0 | 0 | T,T | T,已有一次错误预测 |
| 1 | 0 | T,NT | NT |
| 2 | 0 | NT,T | NT |
| 3 | 0 | NT | T |
| 4 | 1 | T,T | T |
| 5 | 1 | T,NT | T,已有一次错误预测 |
| 6 | 1 | NT,T | NT |
| 7 | 1 | NT,NT | NT |
译注:T 表示跳转,NT 表示不跳转。原题将局部预测器写为 ,但表中记录最近两次结果;表项 3 又只写一个 NT,均为原页内容。题设“16 位”与是否计入历史位的口径也未进一步说明,译文不补造假设。
| 分支 PC(字地址) | 结果 |
|---|---|
| 454 | T |
| 543 | NT |
| 777 | NT |
| 543 | NT |
| 777 | NT |
| 454 | T |
| 777 | NT |
| 454 | T |
| 543 | T |
习题 3.17
[10] 〈3.4〉 假定有一个深流水线处理器,只为条件分支实现分支目标缓冲区。错误预测代价始终为 4 个周期,缓冲区未命中代价始终为 3 个周期。假定命中率 90%、预测准确率 90%、分支频率 15%。与分支代价固定为两个周期的处理器相比,采用分支目标缓冲区的处理器快多少?假定不含分支停顿的基础 CPI 为 1。
习题 3.18
[10/5] 〈3.4〉 考虑一个分支目标缓冲区:正确预测条件分支、错误预测、缓冲区未命中的代价,分别为 0、2、2 个时钟周期。考虑一种区分条件与无条件分支的设计:对条件分支保存目标地址,对无条件分支保存目标指令。
a. [10]〈3.9〉 在缓冲区中找到一个无条件分支时,其代价是多少个时钟周期?
b. [10]〈3.9〉 求对无条件分支采用分支折叠(branch folding)带来的改进。假定命中率 90%,无条件分支频率 5%,缓冲区未命中代价为两个周期。这项增强带来多少性能改善?命中率至少需要多高,才能获得性能收益?
译注:题首难度标注为 [10/5],b 小问却标为 [10];子问指向 3.9,而题首指向 3.4。编号及难度均照原书保留。
习题 3.19
[10] 〈3.2, 3.7〉 考虑以下代码,它计算稀疏矩阵 A 与稠密向量 x 的乘积。稀疏矩阵采用压缩稀疏行(Compressed Sparse Row,CSR)格式:所有非零元素按行优先顺序保存在连续内存块中,第 个非零元素保存在 val[i],对应列保存在 col[i],其所在行为 ,满足 。也就是说,ptr 数组保存各行第一个元素的索引。
a. [5]〈3.7〉 哪些加载指令可能需要推测式内存消歧?是否可能通过内存存在 RAW 依赖?
b. [5]〈3.2〉 你预计分支预测器对第 10 条指令中的分支,预测效果如何?
译注:原代码的行尾注释写
i!=ptr[i],输出指针递增注释写&x[row];lw后的索引步长为 8,且使用fmac.d、fli、b等写法。
习题 3.20
[10] 〈3.2, 3.5〉 考虑以下代码,它用 Horner 法,对保存在寄存器 f6、f8 中的两个输入值计算多项式。
将循环展开为原来的两倍,并给浮点寄存器使用其他名称进行重命名,避免名依赖引起的性能损失。然后给出最终的寄存器映射。
译注:乘法注释中的
x1、x2,以及第二个乘法仍写temp而后续使用temp2,均为原书注释;指令本身的输入是 f6、f8。
习题 3.21
[5] 〈3.7〉 对于以下代码片段,分别求两种 CPU 执行它所需的周期数:一种不支持加载旁路与推测式内存消歧,另一种同时支持这两项功能。假定存储与加载指令之间没有依赖。
假定使用单发射乱序流水线,包含一个延迟为 3 个周期的整数单元,以及一个延迟为 4 个周期的加载 / 存储单元。指令可以在最后一条产生其依赖结果的指令完成的同一周期发射。请说明你所做的其他假设。