CSAPP-Archlab

实验准备

实验环境:Ubuntu 24.04.3 LTS

按照说明编译工具

Y86-64 工具链概览

刚编译的四个工具的作用:

工具 文件位置 功能 输入 输出
yas misc/yas Y86-64汇编器 .ys 汇编源文件 .yo 目标文件(机器码)
yis misc/yis ISA模拟器(参考实现) .yo 目标文件 执行结果(寄存器/内存状态)
ssim seq/ssim 顺序处理器模拟器 .yo 目标文件 逐周期执行结果
psim pipe/psim 流水线处理器模拟器 .yo 目标文件 逐周期执行结果(5级流水线)

工作流程

复制代码
.ys 源程序 ──[yas]──> .yo 机器码 ──┬──[yis]──> 正确的结果(参考)
                                  ├──[ssim]──> 顺序处理器结果
                                  └──[psim]──> 流水线处理器结果
                        
                         对比验证 yis 和 psim 是否一致

Part A - 编写Y86-64程序

实验要求(源自实验说明文档)

概述

在本部分中,编写三个Y86-64程序实现链表操作和数据块拷贝,深入理解x86-64调用约定、内存管理和处理器模拟。

三个任务概览:

程序 功能 数据结构 执行步骤 预期结果
sum.ys 链表迭代求和 单向链表(3元素) 25步 %rax = 0xcba
rsum.ys 链表递归求和 单向链表(3元素) 39步 %rax = 0xcba
copy.ys 数据块拷贝+XOR 数组拷贝(3元素) 35步 %rax = 0xcba

关键要求:

  • 遵循x86-64调用约定(%rdi、%rsi、%rdx传递参数)
  • 正确使用栈进行函数调用和局部变量存储
  • 保存和恢复被调用方保存的寄存器(callee-save)
C 语言参考实现

以下是三个程序的 C 语言版本(来自 examples.c):

c 复制代码
/* 链表元素结构 */
typedef struct ELE {
    long val;
    struct ELE *next;
} *list_ptr;

/* sum_list - 迭代求和链表元素 */
long sum_list(list_ptr ls) {
    long val = 0;
    while (ls) {
        val += ls->val;
        ls = ls->next;
    }
    return val;
}

/* rsum_list - 递归求和链表元素 */
long rsum_list(list_ptr ls) {
    if (!ls)
        return 0;
    else {
        long val = ls->val;
        long rest = rsum_list(ls->next);
        return val + rest;
    }
}

/* copy_block - 复制数据块并计算校验和 */
long copy_block(long *src, long *dest, long len) {
    long result = 0;
    while (len > 0) {
        long val = *src++;
        *dest++ = val;
        result ^= val;
        len--;
    }
    return result;
}

测试验证

所有三个程序使用相同的数据集进行测试,确保结果可比较:

数据结构:

yaml 复制代码
链表元素结构 (8字节值 + 8字节指针):
ele1 → val=0x00a(10),  next→ele2
ele2 → val=0x0b0(176), next→ele3
ele3 → val=0xc00(3072),next=NULL

期望求和:0x00a + 0x0b0 + 0xc00 = 0xcba (3258)
期望XOR:  0x00a ^ 0x0b0 ^ 0xc00 = 0xcba (3258)

验证方法:

bash 复制代码
# 汇编
./sim/misc/yas ./PartA/sum.ys    # 生成sum.yo
./sim/misc/yas ./PartA/rsum.ys   # 生成rsum.yo
./sim/misc/yas ./PartA/copy.ys   # 生成copy.yo

# 验证(ISA参考实现)
./sim/misc/yis ./PartA/sum.yo    # 应输出: 0xcba
./sim/misc/yis ./PartA/rsum.yo   # 应输出: 0xcba
./sim/misc/yis ./PartA/copy.yo   # 应输出: 0xcba

# GUI可视化调试
cd ./sim/seq && ./ssim -g ../../PartA/sum.yo   # 交互式调试

任务 1: sum.ys(链表迭代求和)

功能描述: 实现C语言中sum_list函数的Y86-64版本,使用迭代方式遍历链表

算法流程:

yaml 复制代码
sum_list(ls):
  val = 0
  while (ls != NULL):
    val += ls->val        # 累加当前节点的值
    ls = ls->next         # 移动到下一个节点
  return val

Y86-64实现详解:

指令段 功能 关键代码
初始化 设置栈、参数 irmovq stack,%rsp 初始化栈指针 irmovq ele1,%rdi 传递链表首地址
sum函数 迭代求和逻辑 irmovq $0,%rax 初始化累加器 andq %rdi,%rdi 测试指针非空 jmp test 跳转到循环条件测试
loop标签 循环体 mrmovq (%rdi),%r9 读取val addq %r9,%rax 累加 mrmovq 8(%rdi),%rdi 读取next
test标签 循环条件 jne loop 如果指针非NULL继续循环

Y86-64代码:

yaml 复制代码
# sum.ys - 链表迭代求和
# 作者: JianBai Sun (1320240204)
# 功能: 对链表元素进行迭代求和

.pos 0
    irmovq stack,%rsp       # 初始化栈指针
    irmovq ele1,%rdi        # %rdi = 首元素地址(参数1)
    call sum                # 调用sum函数
    halt                    # 停机

.align 8
# 链表数据: 每个节点16字节(8字节值 + 8字节指针)
ele1:
    .quad 0x00a             # 节点1值: 10
    .quad ele2              # 节点1的next指针
ele2:
    .quad 0x0b0             # 节点2值: 176
    .quad ele3              # 节点2的next指针
ele3:
    .quad 0xc00             # 节点3值: 3072
    .quad 0                 # 节点3的next指针(NULL)

# sum函数: 迭代求和
sum:
    irmovq $0,%rax          # %rax = 累加器 (初始为0)
    irmovq $8,%r8           # %r8 = 8 (节点值在偏移0处)
    andq %rdi,%rdi          # 测试%rdi是否为NULL
    jmp test                # 跳转到循环条件

loop:
    mrmovq (%rdi),%r9       # %r9 = 当前节点的值 [%rdi+0]
    addq %r9,%rax           # %rax += %r9 (累加)
    mrmovq 8(%rdi),%rdi     # %rdi = 下一个节点 [%rdi+8]
    andq %rdi,%rdi          # 更新条件码(ZF=1 if %rdi==0)

test:
    jne loop                # 如果指针非NULL, 继续循环
    ret                     # 返回(%rax保存结果)

.pos 0x200
stack:                      # 栈起始地址

执行结果验证:

bash 复制代码
$ ./sim/misc/yis ./PartA/sum.yo
Stopped in 25 steps at PC = 0x1d.  Status 'HLT', CC Z=1 S=0 O=0
Changes to registers:
%rax:   0x0000000000000000  →  0x0000000000000cba  ✓ 正确
%rsp:   0x0000000000000000  →  0x0000000000000200  ✓ 栈初始化
%r8:    0x0000000000000000  →  0x0000000000000008  
%r9:    0x0000000000000000  →  0x0000000000000c00  (最后一个值)
Changes to memory:
0x01f8: 0x0000000000000000  →  0x000000000000001d  (返回地址)

正确性分析:

  • 执行25步,符合预期
  • 最终%rax = 0xcba = 10 + 176 + 3072 = 3258 ✓
  • 栈指针正确初始化到0x200
  • 条件码Z=1表示最后读取NULL指针,正确终止循环

任务 2: rsum.ys(链表递归求和)

功能描述: 实现C语言中rsum_list函数的Y86-64版本,使用递归方式求和

算法流程:

yaml 复制代码
rsum_list(ls):
  if (ls == NULL):
    return 0
  else:
    val = ls->val
    rest = rsum_list(ls->next)    # 递归调用
    return val + rest

递归机制分析:

  • 每次递归调用会压入返回地址和%rax
  • 栈帧结构:返回地址 上层%rax值
  • 递归深度 = 链表长度 = 3层

Y86-64实现详解:

指令段 功能 关键代码
初始化 设置栈、参数 irmovq $0,%rax 初始化返回值 irmovq ele1,%rdi 传递链表首地址
rsum函数 递归求和逻辑 andq %rdi,%rdi 测试基础情况 jmp test 跳转到条件测试
calc标签 递归调用 pushq %rax 保存返回值 mrmovq (%rdi),%rax 读取当前值 mrmovq 8(%rdi),%rdi 指向下一节点 call rsum 递归调用
返回处理 合并结果 popq %r8 恢复上层返回值 addq %r8,%rax 累加结果

Y86-64代码:

yaml 复制代码
# rsum.ys - 链表递归求和
# 作者: JianBai Sun (1320240204)
# 功能: 对链表元素进行递归求和

.pos 0
    irmovq stack,%rsp       # 初始化栈指针
    irmovq ele1,%rdi        # %rdi = 首元素地址
    irmovq $0,%rax          # %rax = 返回值累加器
    call rsum               # 调用rsum函数
    halt                    # 停机

.align 8
ele1:
    .quad 0x00a             # 节点1值: 10
    .quad ele2              # 节点1的next指针
ele2:
    .quad 0x0b0             # 节点2值: 176
    .quad ele3              # 节点2的next指针
ele3:
    .quad 0xc00             # 节点3值: 3072
    .quad 0                 # 节点3的next指针(NULL)

# rsum函数: 递归求和
rsum:
    andq %rdi,%rdi          # 测试%rdi == NULL (基础情况)
    jmp test                # 跳转到条件判断

calc:
    pushq %rax              # 保存上层%rax值到栈
    mrmovq (%rdi),%rax      # %rax = 当前节点值
    mrmovq 8(%rdi),%rdi     # %rdi = 下一节点(参数)
    call rsum               # 递归: rsum(ls->next)
                            # 返回后: %rax = rest的和
    popq %r8                # %r8 = 恢复的上层值
    addq %r8,%rax           # %rax = 上层val + rest
    ret                     # 返回

test:
    jne calc                # 如果%rdi != NULL, 进行递归
    ret                     # 否则返回(基础情况: %rax=0)

.pos 0x200
stack:                      # 栈起始地址

执行结果验证:

bash 复制代码
$ ./sim/misc/yis ./PartA/rsum.yo
Stopped in 39 steps at PC = 0x27.  Status 'HLT', CC Z=0 S=0 O=0
Changes to registers:
%rax:   0x0000000000000000  →  0x0000000000000cba  ✓ 正确
%rsp:   0x0000000000000000  →  0x0000000000000200  ✓ 栈平衡

Changes to memory:
0x01c8: 0x0000000000000000  →  0x0000000000000082  (递归1: 返回地址)
0x01d0: 0x0000000000000000  →  0x00000000000000b0  (递归1: val=176)
0x01d8: 0x0000000000000000  →  0x0000000000000082  (递归2: 返回地址)
0x01e0: 0x0000000000000000  →  0x000000000000000a  (递归2: val=10)
0x01e8: 0x0000000000000000  →  0x0000000000000082  (递归3: 返回地址)
0x01f8: 0x0000000000000000  →  0x0000000000000027  (主程序: 返回地址)

正确性分析:

  • 最终%rax = 0xcba = 0x00a + 0x0b0 + 0xc00 = 3258 ✓
  • 栈内存显示3层递归调用的栈帧
  • 栈指针恢复到0x200,栈平衡正确
  • 递归深度 = 3(链表长度),符合预期

递归执行流程追踪:

复制代码
主程序: rsum(ele1)
  ├─ rsum(ele1->next=ele2): 
  │   ├─ rsum(ele2->next=ele3):
  │   │   ├─ rsum(ele3->next=NULL):
  │   │   │   return 0                    # 基础情况
  │   │   return 0xc00 + 0 = 0xc00      # 返回
  │   return 0x0b0 + 0xc00 = 0xcb0      # 返回
  return 0x00a + 0xcb0 = 0xcba           # 返回

最终结果: 0x00a + 0x0b0 + 0xc00 = 0xcba ✓

任务 3: copy.ys(数据块拷贝)

功能描述: 实现C语言中copy_block函数的Y86-64版本,复制数据块并计算XOR校验和

算法流程:

yaml 复制代码
copy_block(src, dest, len):
  result = 0
  while (len > 0):
    val = *src++            # 读取源数据
    *dest++ = val           # 写入目标
    result ^= val           # XOR累加校验和
    len--                   # 计数器递减
  return result

参数传递约定(x86-64):

  • %rdi = src(源地址)
  • %rsi = dest(目标地址)
  • %rdx = len(元素个数)

Y86-64实现详解:

指令段 功能 关键代码
初始化 设置参数和临时变量 irmovq $0,%rax result=0 irmovq $8,%r10 每次步进8字节
copy函数 数据拷贝逻辑 mrmovq (%rdi),%r11 读取src rmmovq %r11,(%rsi) 写入 dest addq %r10,%rdi/%rsi src++, dest++
校验计算 XOR校验 xorq %r11,%rax result ^= val
循环控制 计数器管理 subq %r9,%rdx len-- jg loop len > 0继续

Y86-64代码:

yaml 复制代码
# copy.ys - 数据块拷贝并计算XOR校验和
# 作者: JianBai Sun (1320240204)
# 功能: 复制数据块并计算校验和
# 参数: %rdi=src, %rsi=dest, %rdx=len
# 返回: %rax=XOR校验和

.pos 0
    irmovq stack, %rsp      # 初始化栈指针
    irmovq src,%rdi         # %rdi = 源地址(参数1)
    irmovq dest,%rsi        # %rsi = 目标地址(参数2)
    irmovq $3,%rdx          # %rdx = 元素个数(参数3)
    call copy               # 调用copy函数
    halt                    # 停机

.align 8
src:
    .quad 0x00a             # 源数据[0] = 10
    .quad 0x0b0             # 源数据[1] = 176
    .quad 0xc00             # 源数据[2] = 3072
dest:
    .quad 0x111             # 初始目标[0] = 273
    .quad 0x222             # 初始目标[1] = 546
    .quad 0x333             # 初始目标[2] = 819

# copy函数: 拷贝数据并计算XOR校验
copy:
    irmovq $0,%rax          # %rax = result = 0
    irmovq $0,%r8           # %r8 = 临时值0(用于subq)
    irmovq $1,%r9           # %r9 = 步进值1(用于len--)
    irmovq $8,%r10          # %r10 = 8(指针步进)
    subq %r8,%rdx           # 标志位准备(实际不改变%rdx)
    jmp test                # 跳转到循环条件

loop:
    mrmovq (%rdi),%r11      # %r11 = *src (读源数据)
    addq %r10,%rdi          # src++ (src += 8)
    rmmovq %r11,(%rsi)      # *dest = %r11 (写目标数据)
    addq %r10,%rsi          # dest++ (dest += 8)
    xorq %r11,%rax          # result ^= %r11 (XOR校验)
    subq %r9,%rdx           # len-- (len -= 1)

test:
    jg loop                 # 如果len > 0, 继续循环
    ret                     # 返回(%rax=校验和)

.pos 0x200
stack:                      # 栈起始地址

执行结果验证:

bash 复制代码
$ ./sim/misc/yis ./PartA/copy.yo
Stopped in 35 steps at PC = 0x31.  Status 'HLT', CC Z=1 S=0 O=0
Changes to registers:
%rax:   0x0000000000000000  →  0x0000000000000cba  ✓ XOR校验和正确
%rsp:   0x0000000000000000  →  0x0000000000000200  ✓ 栈平衡
%rsi:   0x0000000000000000  →  0x0000000000000068  (dest指向末位+8)
%rdi:   0x0000000000000000  →  0x0000000000000050  (src指向末位+8)
%r9:    0x0000000000000000  →  0x0000000000000001  
%r10:   0x0000000000000000  →  0x0000000000000008  
%r11:   0x0000000000000000  →  0x0000000000000c00  (最后读取的值)

Changes to memory (数据拷贝结果):
0x0050: 0x0000000000000111  →  0x000000000000000a  ✓ src[0]拷贝成功
0x0058: 0x0000000000000222  →  0x00000000000000b0  ✓ src[1]拷贝成功
0x0060: 0x0000000000000333  →  0x0000000000000c00  ✓ src[2]拷贝成功
0x01f8: 0x0000000000000000  →  0x0000000000000031  (返回地址)

正确性分析:

  • 执行35步,符合预期

  • 最终%rax = 0xcba = 0x00a ^ 0x0b0 ^ 0xc00 = 3258 ✓

    复制代码
    验算: 0x00a ^ 0x0b0 = 0x0ba
          0x0ba ^ 0xc00 = 0xcba ✓
  • 内存0x50-0x60处的数据被正确拷贝:

    • dest[0]: 0x111 → 0x00a ✓
    • dest[1]: 0x222 → 0x0b0 ✓
    • dest[2]: 0x333 → 0xc00 ✓
  • 指针%rdi和%rsi正确步进到末位+8

  • 栈指针恢复到0x200


Part B - SEQ处理器扩展

实验要求

任务概述

本部分在目录 sim/seq 中进行。任务是扩展 SEQ 处理器以支持 iaddq 指令。通过修改文件 seq-full.hcl(实现 CS:APP3e 教科书中描述的 SEQ 版本),实现新指令的硬件控制逻辑。该文件包含了解决方案所需的指令编码常量声明。

iaddq指令简介

iaddq 是一条立即数加法指令,结合了 irmovq(立即数传送)和 addq(加法)的功能。

指令格式和功能:

  • iaddq V, rB - 将立即数 V 加到寄存器 rB,结果存回 rB
  • irmovq V, rT 后跟 addq rT, rB 的效果相同
  • 参考 CS:APP3e 教材图 4.18 中 irmovq 和 OPq 的描述实现

示例代码(来自 sim/y86-code/asumi.ys):

yaml 复制代码
sum:
    xorq %rax,%rax          # sum = 0
    andq %rsi,%rsi          # Set condition codes
    jmp test
loop:
    mrmovq (%rdi),%r10      # Get *start
    addq %r10,%rax          # Add to sum
    iaddq $8,%rdi           # start++      <- iaddq指令示例
    iaddq $-1,%rsi          # count--      <- iaddq指令示例
test:
    jne loop                # Stop when 0
    ret
HCL文件修改要求

修改的 HCL 文件必须以注释头开始,包含以下信息:

  • 实验者的姓名和学号
  • iaddq 指令所需计算的详细描述
    • 取指阶段:从内存读取指令码和立即数
    • 译码阶段:识别 rB 寄存器
    • 执行阶段:执行加法操作(ALU)
    • 访存阶段:不需要内存访问
    • 写回阶段:将结果写入 rB 寄存器
    • 更新PC阶段:PC = PC + 10(10字节指令长度)
构建和测试解决方案
1. 修改 seq-full.hcl

修改 seq-full.hcl 文件中的相关部分,添加 iaddq 指令的硬件逻辑。该文件已包含 iaddq 的指令编码声明:

verilog 复制代码
wordsig IIADDQ  'I_IADDQ'  # Instruction code for iaddq instruction
2. 编译新的模拟器

修改完成后,编译新的 SEQ 模拟器:

bash 复制代码
cd ./sim/seq
make VERSION=full

此命令基于修改的 seq-full.hcl 文件构建新的 ssim 二进制文件。

3. 基础功能测试

在 TTY 模式下测试简单程序,与 ISA 模拟器的结果进行对比:

bash 复制代码
./ssim -t ../y86-code/asumi.yo

如果出现错误,可在 GUI 模式下逐步调试实现:

bash 复制代码
./ssim -g ../y86-code/asumi.yo
4. 基准程序测试

验证对原有指令的兼容性,确保修改未破坏已有功能:

bash 复制代码
cd ../y86-code
make testssim

该命令在基准程序上运行 ssim,通过比对处理器状态与 ISA 模拟器的结果来验证正确性。这些程序不包含 iaddq 指令,仅用于回归测试。详见 sim/y86-code/README 文件。

5. 回归测试

执行完整的回归测试套件验证实现质量。

不含 iaddq 指令的回归测试:

bash 复制代码
cd ../ptest
make SIM=../seq/ssim

包含 iaddq 指令的回归测试:

bash 复制代码
cd ../ptest
make SIM=../seq/ssim TFLAGS=-i
参考资源

详细的 SEQ 模拟器说明见 CS:APP3e Y86-64 处理器模拟器指南(simguide.pdf)。


实验内容

指令跟踪

根据iaddq指令的功能(将立即数V加到寄存器rB),首先对其在SEQ各个阶段进行指令跟踪分析:

阶段 iaddq V, rB 执行的操作 说明
取指 icode:ifun ← M₁[PC] 从内存读取指令码和功能码
rA:rB ← M₁[PC+1] 读取寄存器字节(rA未使用,rB为目标寄存器)
valC ← M₈[PC+2] 读取8字节立即数V
valP ← PC + 10 计算下一条指令地址(10字节指令长度)
译码 valB ← R[rB] 读取rB寄存器的当前值
执行 valE ← valC + valB ALU执行加法:立即数 + 寄存器值
Set CC 更新条件码(ZF、SF、OF)
访存 --- 无需访存操作
写回 R[rB] ← valE 将ALU结果写回rB寄存器
更新PC PC ← valP 更新程序计数器

设计思路:

iaddq指令融合了irmovqaddq的特性:

  • 取指/译码 :类似irmovq,需要读取立即数和寄存器字段
  • 执行 :类似addq,执行ALU加法并更新条件码
  • 写回:将结果写回rB,无条件执行(不像cmovXX需要判断条件)

修改HCL表达式

根据指令跟踪分析,需要在7个关键位置的HCL表达式中添加IIADDQ支持:

1. 取指阶段修改

(1)指令有效性验证

verilog 复制代码
bool instr_valid = icode in 
    { INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ,
           IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ, IIADDQ };  // ← 添加IIADDQ

修改原因: 将iaddq声明为合法指令,避免触发无效指令异常(SINS)。


(2)需要寄存器字节

verilog 复制代码
bool need_regids =
    icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ, 
             IIRMOVQ, IRMMOVQ, IMRMOVQ, IIADDQ };  // ← 添加IIADDQ

修改原因: iaddq指令格式包含rB字段(第2字节),需要取指逻辑解析寄存器编号。


(3)需要立即数常量

verilog 复制代码
bool need_valC =
    icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL, IIADDQ };  // ← 添加IIADDQ

修改原因: iaddq的立即数V占用第3-10字节,需要读取8字节常量到valC。


2. 译码阶段修改

(4)B源寄存器

verilog 复制代码
word srcB = [
    icode in { IOPQ, IRMMOVQ, IMRMOVQ, IIADDQ } : rB;  // ← 添加IIADDQ
    icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
    1 : RNONE;
];

修改原因: 需要读取rB的当前值到valB,作为ALU的一个操作数(被加数)。


3. 写回阶段修改

(5)E目标寄存器

verilog 复制代码
word dstE = [
    icode in { IRRMOVQ } && Cnd : rB;
    icode in { IIRMOVQ, IOPQ, IIADDQ } : rB;  // ← 添加IIADDQ
    icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
    1 : RNONE;
];

修改原因: 指定ALU计算结果(valE)写回rB寄存器。


4. 执行阶段修改

(6)ALU的A输入

verilog 复制代码
word aluA = [
    icode in { IRRMOVQ, IOPQ } : valA;
    icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IIADDQ } : valC;  // ← 添加IIADDQ
    icode in { ICALL, IPUSHQ } : -8;
    icode in { IRET, IPOPQ } : 8;
];

修改原因: 将立即数valC作为ALU第一个操作数(加数)。


(7)ALU的B输入

verilog 复制代码
word aluB = [
    icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, 
              IPUSHQ, IRET, IPOPQ, IIADDQ } : valB;  // ← 添加IIADDQ
    icode in { IRRMOVQ, IIRMOVQ } : 0;
];

修改原因: 将rB的值(valB)作为ALU第二个操作数(被加数)。


(8)条件码更新

verilog 复制代码
bool set_cc = icode in { IOPQ, IIADDQ };  // ← 添加IIADDQ

修改原因: iaddq执行加法后需要更新条件码(ZF、SF、OF),以支持后续条件跳转指令(如jne)的判断。这是最容易遗漏的修改点。

示例说明:

yaml 复制代码
iaddq $-1, %rsi    # count-- (更新条件码)
jne loop           # 如果count≠0继续循环(依赖ZF标志)

HCL修改总结

完整的修改涉及8个表达式,覆盖了SEQ的5个阶段:

修改位置 表达式 添加内容 对应阶段
1 instr_valid IIADDQ 取指
2 need_regids IIADDQ 取指
3 need_valC IIADDQ 取指
4 srcB IIADDQ 译码
5 dstE IIADDQ 写回
6 aluA IIADDQ 执行
7 aluB IIADDQ 执行
8 set_cc IIADDQ 执行

实验结果

基础功能测试:

bash 复制代码
(base) sun@zzz:~/codes/CSAPP/第四章实验-优化Y86-64流水线处理器性能$ cd sim/seq && make VERSION=full
# Building the seq-full.hcl version of SEQ
../misc/hcl2c -n seq-full.hcl <seq-full.hcl >seq-full.c
gcc -Wall -O2 -fcommon -Wl,--no-as-needed -isystem /usr/include/tcl8.6 -I../misc -DHAS_GUI -o ssim \
        seq-full.c ssim.c ../misc/isa.c -L/usr/lib -ltk -ltcl -lm
(base) sun@zzz:~/codes/CSAPP/第四章实验-优化Y86-64流水线处理器性能/sim/seq$ ./ssim -t ../y86-code/asumi.yo
Y86-64 Processor: seq-full.hcl
137 bytes of code read
IF: Fetched irmovq at 0x0.  ra=----, rb=%rsp, valC = 0x100
IF: Fetched call at 0xa.  ra=----, rb=----, valC = 0x38
Wrote 0x13 to address 0xf8
IF: Fetched irmovq at 0x38.  ra=----, rb=%rdi, valC = 0x18
IF: Fetched irmovq at 0x42.  ra=----, rb=%rsi, valC = 0x4
IF: Fetched call at 0x4c.  ra=----, rb=----, valC = 0x56
Wrote 0x55 to address 0xf0
IF: Fetched xorq at 0x56.  ra=%rax, rb=%rax, valC = 0x0
IF: Fetched andq at 0x58.  ra=%rsi, rb=%rsi, valC = 0x0
IF: Fetched jmp at 0x5a.  ra=----, rb=----, valC = 0x83
IF: Fetched jne at 0x83.  ra=----, rb=----, valC = 0x63
IF: Fetched mrmovq at 0x63.  ra=%r10, rb=%rdi, valC = 0x0
IF: Fetched addq at 0x6d.  ra=%r10, rb=%rax, valC = 0x0
IF: Fetched iaddq at 0x6f.  ra=----, rb=%rdi, valC = 0x8
IF: Fetched iaddq at 0x79.  ra=----, rb=%rsi, valC = 0xffffffffffffffff
IF: Fetched jne at 0x83.  ra=----, rb=----, valC = 0x63
IF: Fetched mrmovq at 0x63.  ra=%r10, rb=%rdi, valC = 0x0
IF: Fetched addq at 0x6d.  ra=%r10, rb=%rax, valC = 0x0
IF: Fetched iaddq at 0x6f.  ra=----, rb=%rdi, valC = 0x8
IF: Fetched iaddq at 0x79.  ra=----, rb=%rsi, valC = 0xffffffffffffffff
IF: Fetched jne at 0x83.  ra=----, rb=----, valC = 0x63
IF: Fetched mrmovq at 0x63.  ra=%r10, rb=%rdi, valC = 0x0
IF: Fetched addq at 0x6d.  ra=%r10, rb=%rax, valC = 0x0
IF: Fetched iaddq at 0x6f.  ra=----, rb=%rdi, valC = 0x8
IF: Fetched iaddq at 0x79.  ra=----, rb=%rsi, valC = 0xffffffffffffffff
IF: Fetched jne at 0x83.  ra=----, rb=----, valC = 0x63
IF: Fetched mrmovq at 0x63.  ra=%r10, rb=%rdi, valC = 0x0
IF: Fetched addq at 0x6d.  ra=%r10, rb=%rax, valC = 0x0
IF: Fetched iaddq at 0x6f.  ra=----, rb=%rdi, valC = 0x8
IF: Fetched iaddq at 0x79.  ra=----, rb=%rsi, valC = 0xffffffffffffffff
IF: Fetched jne at 0x83.  ra=----, rb=----, valC = 0x63
IF: Fetched ret at 0x8c.  ra=----, rb=----, valC = 0x0
IF: Fetched ret at 0x55.  ra=----, rb=----, valC = 0x0
IF: Fetched halt at 0x13.  ra=----, rb=----, valC = 0x0
32 instructions executed
Status = HLT
Condition Codes: Z=1 S=0 O=0
Changed Register State:
%rax:   0x0000000000000000      0x0000abcdabcdabcd
%rsp:   0x0000000000000000      0x0000000000000100
%rdi:   0x0000000000000000      0x0000000000000038
%r10:   0x0000000000000000      0x0000a000a000a000
Changed Memory State:
0x00f0: 0x0000000000000000      0x0000000000000055
0x00f8: 0x0000000000000000      0x0000000000000013
ISA Check Succeeds

基准程序测试:

bash 复制代码
(base) sun@zzz:~/codes/CSAPP/第四章实验-优化Y86-64流水线处理器性能/sim/y86-code$ make testssim
Makefile:42: 警告:忽略后缀规则定义的先决条件
Makefile:45: 警告:忽略后缀规则定义的先决条件
Makefile:48: 警告:忽略后缀规则定义的先决条件
Makefile:51: 警告:忽略后缀规则定义的先决条件
../seq/ssim -t asum.yo > asum.seq
../seq/ssim -t asumr.yo > asumr.seq
../seq/ssim -t cjr.yo > cjr.seq
../seq/ssim -t j-cc.yo > j-cc.seq
../seq/ssim -t poptest.yo > poptest.seq
../seq/ssim -t pushquestion.yo > pushquestion.seq
../seq/ssim -t pushtest.yo > pushtest.seq
../seq/ssim -t prog1.yo > prog1.seq
../seq/ssim -t prog2.yo > prog2.seq
../seq/ssim -t prog3.yo > prog3.seq
../seq/ssim -t prog4.yo > prog4.seq
../seq/ssim -t prog5.yo > prog5.seq
../seq/ssim -t prog6.yo > prog6.seq
../seq/ssim -t prog7.yo > prog7.seq
../seq/ssim -t prog8.yo > prog8.seq
../seq/ssim -t ret-hazard.yo > ret-hazard.seq
grep "ISA Check" *.seq
asum.seq:ISA Check Succeeds
asumr.seq:ISA Check Succeeds
cjr.seq:ISA Check Succeeds
j-cc.seq:ISA Check Succeeds
poptest.seq:ISA Check Succeeds
prog1.seq:ISA Check Succeeds
prog2.seq:ISA Check Succeeds
prog3.seq:ISA Check Succeeds
prog4.seq:ISA Check Succeeds
prog5.seq:ISA Check Succeeds
prog6.seq:ISA Check Succeeds
prog7.seq:ISA Check Succeeds
prog8.seq:ISA Check Succeeds
pushquestion.seq:ISA Check Succeeds
pushtest.seq:ISA Check Succeeds
ret-hazard.seq:ISA Check Succeeds
rm asum.seq asumr.seq cjr.seq j-cc.seq poptest.seq pushquestion.seq pushtest.seq prog1.seq prog2.seq prog3.seq prog4.seq prog5.seq prog6.seq prog7.seq prog8.seq ret-hazard.seq

回归测试(不含iaddq):

bash 复制代码
(base) sun@zzz:~/codes/CSAPP/第四章实验-优化Y86-64流水线处理器性能/sim/seq$ cd ../ptest && make SIM=../seq/ssim
./optest.pl -s ../seq/ssim 
Simulating with ../seq/ssim
  All 49 ISA Checks Succeed
./jtest.pl -s ../seq/ssim 
Simulating with ../seq/ssim
  All 64 ISA Checks Succeed
./ctest.pl -s ../seq/ssim 
Simulating with ../seq/ssim
  All 22 ISA Checks Succeed
./htest.pl -s ../seq/ssim 
Simulating with ../seq/ssim
  All 600 ISA Checks Succeed

回归测试(含iaddq):

bash 复制代码
(base) sun@zzz:~/codes/CSAPP/第四章实验-优化Y86-64流水线处理器性能/sim/ptest$ make SIM=../seq/ssim TFLAGS=-i
./optest.pl -s ../seq/ssim -i
Simulating with ../seq/ssim
  All 58 ISA Checks Succeed
./jtest.pl -s ../seq/ssim -i
Simulating with ../seq/ssim
  All 96 ISA Checks Succeed
./ctest.pl -s ../seq/ssim -i
Simulating with ../seq/ssim
  All 22 ISA Checks Succeed
./htest.pl -s ../seq/ssim -i
Simulating with ../seq/ssim
  All 756 ISA Checks Succeed

可以看到前面的测试全部通过,说明实验成功。

Part C - 流水线性能优化

实验要求(源自实验说明文档)

任务概述:

  • 工作目录:sim/pipe
  • 目标:在保持语义正确的前提下,修改 ncopy.yspipe-full.hcl,使 ncopy.ys 在 PIPE 模拟器上尽可能快地运行。

提交物:

  • pipe-full.hcl:如有更改(例如实现 iaddq),需在文件头添加姓名、学号与修改的高层描述。
  • ncopy.ys:函数体优化版本,文件头包含姓名、学号以及对优化思路的高层说明(修改了什么、为何这样做)。

编码规则与约束:

  • 正确性:功能与 YIS 保持一致,必须正确复制非重叠数组块,并在 %rax 返回 src 中正整数个数。
  • 通用性:支持任意数组长度,禁止为固定规模(如 64 元素)硬编码展开且仅适配该规模。
  • 长度限制:组装后的 ncopy.yo 不得超过 1000 字节,可用 check-len.pl 检查。
  • 回归要求:pipe-full.hcl 必须通过 ../y86-code../ptest 的回归测试(默认不带 -i)。
  • 可选扩展:允许实现 iaddq 指令以降低指令数与数据相关延迟;若实现,需使用带 -i 的回归用例验证。

允许的优化方向:

  • 指令层面:重排指令、用更高效指令替换指令组、删除冗余指令、新增有利于流水线的指令。
  • 流水线友好性:指令调度以避免/隐藏 load-use 冒险,利用转发/旁路能力,减少分支开销(如循环展开+集中更新计数)。
  • 结构性优化:适度循环展开(参考 CS:APP3e §5.8),批量处理多元素以提高每迭代吞吐,合并地址更新(如用 iaddq)。

构建与测试

生成驱动程序:

bash 复制代码
cd sim/pipe && make clean && make VERSION=full
make drivers
  • 生成 sdriver.yo(4 元素小数组)与 ldriver.yo(63 元素大数组)。

构建 PIPE 模拟器:

bash 复制代码
make psim VERSION=full
# 或同时重建模拟器与驱动:
make VERSION=full

在 PIPE 上运行(GUI):

bash 复制代码
./psim -g sdriver.yo
./psim -g ldriver.yo

正确性测试:

bash 复制代码
./correctness.pl -p

# 或者 
./psim -t sdriver.yo
./psim -t ldriver.yo

# 或者
../y86-code
make testpsim
  • 结束状态含义:

    • 0xaaaa:全部通过

    • 0xbbbb:计数错误

    • 0xccccncopy 超过 1000 字节

    • 0xdddd:源数据未正确复制

    • 0xeeee:目标区域前后发生破坏

基准测试程序(PIPE):

bash 复制代码
./benchmark.pl

实验内容

优化目标与策略

目标: 这个部分没有特别明确的实验完成的要求,但是想要得到满分需要将CPE降至7.5以下。

基线性能: 原始未优化版本的 CPE 约为 15.0+(基于 Y86-64 基础循环,每次迭代包含多次分支预测失败)。

核心优化思路:

  1. 循环展开:减少分支频率,提高指令级并行度(ILP)
  2. iaddq 指令:替代多条 irmovq + addq,减少关键路径长度
  3. 指令调度:利用 load-use 延迟隐藏,交错 load/store/计算指令
  4. 无分支计数:用条件跳转替代 cmovle,减少控制依赖开销

优化迭代过程
第一版:基线版本(CPE ≈ 15.0)

原本的ncopy.ys:

yaml 复制代码
ncopy:	
	xorq %rax,%rax				# count = 0;
	andq %rdx,%rdx				# len <= 0?
	jle Done					# if so, goto Done:

Loop:	
	mrmovq (%rdi), %r10			# read val from src...
	rmmovq %r10, (%rsi)			# ...and store it to dst
	andq %r10, %r10				# val <= 0?
	jle Npos					# if so, goto Npos:
	irmovq $1, %r10
	addq %r10, %rax				# count++

Npos:	
	irmovq $1, %r10
	subq %r10, %rdx				# len--
	irmovq $8, %r10
	addq %r10, %rdi				# src++
	addq %r10, %rsi				# dst++
	andq %rdx,%rdx				# len > 0?
	jg Loop						# if so, goto Loop:

阅读原本的ncopy 代码,可以分析出它要做的事情是:

  1. 复制数据块 :将源数组 src 中的所有元素逐个复制到目标数组 dest
  2. 计算正整数个数 :统计源数组 src 中有多少个正整数(> 0 的元素)
  3. 返回计数值 :将统计结果返回到寄存器 %rax

对应的伪代码是:

c 复制代码
long ncopy(long *src, long *dest, long len) {
    long count = 0;
    while (len > 0) {
        long val = *src++;
        *dest++ = val;        // 复制元素
        if (val > 0)
            count++;          // 计数正整数
        len--;
    }
    return count;             // %rax = count
}

而优化的目标是:在保持功能正确的前提下,通过以下方式使 ncopy 在流水线处理器(PIPE)上运行尽可能快

  • 循环展开(处理多个元素)
  • 实现 iaddq 指令减少指令数
  • 指令调度优化以隐藏 load-use 延迟
  • 优化分支预测与余数处理

初始代码的运行结果是: CPE ≈ 15.0


第二版:×2 展开版本(CPE → 11.21)

先处于稳妥起见,将循环展开 x2 试试水。

优化策略:

  1. 每轮处理 2 个元素,减少分支频率(1/2)
  2. 循环外预设常数寄存器(rOne=1, rEight=8, rZero=0)
  3. 连发两次 mrmovq 后再使用,隐藏 load-use 延迟

代码框架:

yaml 复制代码
Loop2:	mrmovq (%rdi), %rcx	# val0 = src[0]
	mrmovq 8(%rdi), %rbx	# val1 = src[1]  (load发射)
	
	rmmovq %rcx, (%rsi)	# dst[0] = val0  (此时rcx已就绪)
	andq %rcx, %rcx
	cmovle %r10, %r11	# 若val0<=0,inc=0
	addq %r11, %rax		# count += inc
	
	# ... 类似处理val1 ...
	
	iaddq $-2, %rdx		# len -= 2
	jge Loop2

实际测试结果:

复制代码
CPE = 11.21(平均)
最优 CPE = 9.86(n=63 时)

第三版:×8 展开 + iaddq 版本(CPE → 8.51)

既然 x2展开有效,但效果不好,所以就直接x8展开(因为实验对字节数有要求,所以不能无限制的进行循环展开,x8是当前限制下最大的展开数了)

另外,原来有很多 irmovq 写临时寄存器,再立刻被 addq/subq 读取,这样频繁读写寄存器开销很大,现在换成 iaddq 不用读这个临时寄存器,只读 rB 和立即数。

PIPE 中实现 iaddq 指令:

pipe-full.hcl 中添加以下修改:

verilog 复制代码
# 1. 取指阶段:标记合法指令并解析操作数
bool instr_valid = f_icode in 
    { INOP, IHALT, ..., IIADDQ };           # ← 添加IIADDQ
bool need_regids = f_icode in 
    { ..., IIADDQ };                        # ← 需要解析rB
bool need_valC = f_icode in 
    { ..., IIADDQ };                        # ← 需要读取立即数

# 2. 译码阶段:读取rB的当前值
word d_srcB = [
    D_icode in { ..., IIADDQ } : D_rB;     # ← 读rB的源值
    ...
];

# 3. 执行阶段:加法运算
word aluA = [
    E_icode in { ..., IIADDQ } : E_valC;   # ← 立即数作为A输入
    ...
];
word aluB = [
    E_icode in { ..., IIADDQ } : E_valB;   # ← rB值作为B输入
    ...
];
bool set_cc = E_icode in { IOPQ, IIADDQ }; # ← 更新条件码

# 4. 写回阶段:结果写回rB
word d_dstE = [
    D_icode in { ..., IIADDQ } : D_rB;     # ← 写回目标
    ...
];

×8 展开代码框架:

yaml 复制代码
Loop8:	iaddq $-8, %rdx			# len -= 8(iaddq替代subq)
	jl Remain
	
	# 第一组load(不相邻读)
	mrmovq (%rdi), %r8		# v0
	mrmovq 8(%rdi), %r9		# v1
	...
	mrmovq 56(%rdi), %rcx		# v7
	
	# 交错store+计数(load已就绪)
	rmmovq %r8, (%rsi)
	andq %r8, %r8
	jle L1
	iaddq $1, %rax			# count++(iaddq替代addq)
L1:	...(重复7次)
	
	iaddq $64, %rdi			# src += 64
	iaddq $64, %rsi			# dst += 64
	jmp Loop8

性能改进:

  • 把所有"寄存器 += 常数""寄存器 -= 常数"都从"2 条指令 + 临时寄存器"压缩成"一条 iaddq",大幅缩短循环体、降低指令数与依赖。
  • 8 次循环展开,最大化内存带宽利用和流水线吞吐

实际测试结果:

复制代码
CPE = 8.51(平均)
最优 CPE = 9.86
得分:39.8/60.0

第四版:×9 展开 + 优化收尾版本(CPE → 8.18)

在第三版的基础上,第四版继续提高展开,改到了9层展开,但是字节数已经非常接近上限,测试时好几次都超了

优化思路:

  • 通过从 ×8 升级到 ×9 展开
  • 对计数逻辑与 load/use 的先后进行重新调度,尽量隐藏加载延迟;
  • 设计一套"二分式"收尾流程,使剩余元素的处理尽可能接近主循环的效率。

优化策略:

  1. 从 ×8 升级到 ×9 展开:

    • 主循环每次处理 9 个元素,63 元素大数组刚好满足 63 = 9×7;
    • 对于大规模输入,收尾分支触发次数显著减少。
  2. 计数逻辑与分支优化:

    • 第三版中计数更新使用独立的指令序列,容易与 load-use 冒险形成竞争;
    • 本版将计数更新与条件检测交错为 andq → jle Lx → iaddq $1,在分支预测正确时能更好地填补流水线空隙;
    • 依赖 jle 跳转的即时反馈,减轻深度管线中的条件依赖带来的气泡。
  3. 指令调度重排:

yaml 复制代码
# 原×8版(不理想):
L1: rmmovq %r9, 8(%rsi)
    andq %r9, %r9       # 依赖r9
    jle L2              # 依赖CC,如果预测失败→刷管道
    iaddq $1, %rax

# 新版(交错):
mrmovq 16(%rdi), %r8    # 提前load下一值(不依赖r9)
rmmovq %r9, 8(%rsi)
andq %r9, %r9
jle L2
iaddq $1, %rax          # 在load结果就绪前执行,隐藏延迟
  1. 二分收尾:
yaml 复制代码
Remain:
    iaddq $9, %rdx      # 恢复len
    
R8: iaddq $-2, %rdx     # 每次处理2个(而非1个)
    jl R1
    # ...处理2元素...
    jmp R8
    
R1: iaddq $1, %rdx
    jl Done
    # ...处理1元素...

性能改进分析:

  • 主循环 CPE: 约 6.5--7.0(×9 展开 + 9 次 load 隐藏延迟);
  • 大 n 收尾 CPE: 约 6.5--7.0(二分收尾与主循环性能接近);
  • 小 n CPE: 仍然偏高(11--14),但在整体平均中占比有限;
  • 分支预测表现: 在计数路径上 jle 多次连续失败后,预测器逐渐学习正确方向,减轻误预测代价。

实际测试结果:

复制代码
CPE 分布:
  n=9:   CPE = 8.11
  n=18:  CPE = 7.28
  n=27:  CPE = 6.89
  n=36:  CPE = 6.78
  n=45:  CPE = 6.64
  n=63:  CPE = 6.54

平均 CPE = 8.18
最优 CPE = 6.54
得分:46.4/60.0

有少量的提升,但是不大


第五版:×8 展开 + 三叉搜索树余数优化(CPE → 7.80)

由于 x9 展开提升不大,但字节数不够。所以,第五版重新回到 ×8 展开,但着重针对"余数处理"部分进行结构性改写,使用类似"三叉搜索树"的分支结构快速定位剩余元素个数。(借鉴了一下别人的经验)

优化思路:

  • 保持主循环 ×8 展开,以较低分支频率(1/8)获得稳定吞吐;
  • 用两级分支判断(jg/je/jl)直接跳转到 Remain7..Remain1,用顺序直写替代"小循环收尾";
  • 在余数段沿用主循环中的 load/store + 计数交错调度,尽量复用已有的流水线优化。

关键改动:

  • 主循环仍为 ×8 展开,使用 iaddq 合并地址与计数更新,维持分支频率为 1/8;
  • 余数部分通过 jg/je/jl 组合判断直接跳转到对应的 RemainK 标签,不再使用 while 小循环逐个处理;
  • 在余数路径中继续采用"批量 load → 交错 store + andq/jle + iaddq"的调度方式,尽可能隐藏 load-use 冒险;
  • 只有在确实存在剩余元素时才进入相应路径,减少无谓分支和误预测开销。

测试与评分(PIPE):

复制代码
Average CPE     7.80
Score           54.0/60.0

效果分析:

可以看到有很大的提升

  • 平均 CPE 从 8.18 降至 7.80,主要收益来自余数段取消小循环、减少分支与误预测;
  • 对于较大规模数组,CPE 稳定在 6.4--6.7 之间;
  • 对于较小 n,初始化与分支开销仍然占比较高,是后续优化的主要空间。

第六版:×8 展开 + Extra 风格余数优化(CPE → 7.65,Score 57.0/60.0)

第六版在第五版"三叉余数"的基础上,进一步细化余数路径中的指令序列,采用类似课本 extra 题风格的"紧凑模式",在不改变主循环结构的前提下继续压缩关键路径上的指令与气泡。

优化思路:

  • 保持 ×8 展开与 iaddq 的总体框架不变;
  • 使用 %r8/%r9 交替批量 mrmovq,提高 load 指令之间的并行度;
  • 将余数路径统一到一种"Extra 风格"的基本模板,减少特殊情况分支。

关键改动:

  • 主循环仍然是 ×8 展开,%r8/%r9 交替负责加载数据,分支频率保持 1/8;
  • 余数部分继续依赖三叉树判定快速跳转至 Remain7..Remain1
  • 在每个 RemainX 段中,采用模式:mrmovq → jle ExtraX → iaddq $1,%rax → ExtraX: rmmovq → andq,使"加载、计数、存储、条件设置"紧凑排列;
  • Remain7 的首元素采用直写方式,随后也落入这一统一模板,减少重复代码结构。

测试与评分(PIPE):

  • Average CPE 7.65;
  • Score 57.0/60.0;

第七版:PIPE 控制逻辑微优化(执行期旁路 + load-use 放宽)(CPE → 7.65,Score 57.1/60.0)

在第七版中,不再继续修改 ncopy.ys 的汇编结构,而是从 pipe-full.hcl 的控制逻辑入手,对"加载→存储/压栈"场景下的停顿与气泡进行精细化优化。

优化思路:

  • 利用执行期旁路,在特定模式下直接将访存结果转发到执行阶段,减少一次寄存器读写延迟;
  • 放宽对某些模式下 load-use 冒险的停顿条件,使流水线在保证正确性的前提下更激进地前进。

关键改动(pipe-full.hcl):

verilog 复制代码
# 执行期旁路 e_valA:当要用到刚加载的"数据操作数"时,直接从访存级旁路到执行级
word e_valA = [
        E_icode in { IRMMOVQ, IPUSHQ } && E_srcA == M_dstM : m_valM;
        1 : E_valA;
];

# 放宽 F/D/E 级的 load-use 冒险判定:
# 仅当依赖发生在 d_srcB,或依赖发生在 d_srcA 且译码指令并非 IRMMOVQ/IPUSHQ 时,才停顿/气泡。
bool F_stall =
        E_icode in { IMRMOVQ, IPOPQ } &&
            (E_dstM == d_srcB || (E_dstM == d_srcA && !(D_icode in { IRMMOVQ, IPUSHQ }))) ||
        IRET in { D_icode, E_icode, M_icode };

bool D_stall =
        E_icode in { IMRMOVQ, IPOPQ } &&
            (E_dstM == d_srcB || (E_dstM == d_srcA && !(D_icode in { IRMMOVQ, IPUSHQ })));

bool E_bubble =
        (E_icode == IJXX && !e_Cnd) ||
        (E_icode in { IMRMOVQ, IPOPQ } &&
            (E_dstM == d_srcB || (E_dstM == d_srcA && !(D_icode in { IRMMOVQ, IPUSHQ }))));

测试与评分(PIPE):

  • Average CPE 7.65;
  • Score 57.1/60.0;

效果分析:

  • 与第六版相比,平均 CPE 基本持平,说明主路径已接近瓶颈;
  • 在小规模与余数路径场景下,因停顿条件更精细,性能更稳定、波动更小。
优化总结表
版本 展开因子 iaddq 分支频率 平均CPE 最优CPE
基线 1 1/1 ~15.0 ~15.0
×2展开 2 1/2 11.21 9.86
×8展开+iaddq 8 1/8 8.51 9.86
×9展开+iaddq 9 1/9 8.18 6.54
×8展开+iaddq+三叉余数 8 1/8 7.80 6.40
×8展开+iaddq+三叉余数+Extra 8 1/8 7.65 6.30--6.55
×8展开+iaddq+三叉余数+Extra+PIPE旁路 8 1/8 7.65 6.29--6.55

由于花了太多时间,但最后几版的提升太小了,所以打算停住在这一版。最后也是止步在57.1分了,没能拿到满分。

相关推荐
-大头.2 小时前
SQL性能优化与索引策略实战
数据库·sql·性能优化
张人玉2 小时前
c# Data相关类
数据库·oracle
云和数据.ChenGuang2 小时前
OpenEuler 系统中安装 MySQL
运维·数据库·mysql·adb·运维工程师·运维技术
wniuniu_2 小时前
ceph中的rbd的稀疏写入
java·服务器·数据库
科技块儿2 小时前
如何使用IP数据云数据库接入流量监控?
数据库·网络协议·tcp/ip
5335ld2 小时前
uniapp-APP端table列表左侧第一列固定、头部固定
windows·uni-app
叮咚侠3 小时前
Ubuntu 24.04.3 LTS如何扩容逻辑卷
linux·数据库·ubuntu
Zhou-XueLin3 小时前
虚拟环境(云主机)下使用多显示器环境连接RDP远程桌面提示协议错误0x112f需禁用WDDM驱动
windows·云计算
张人玉3 小时前
c#DataTable类
数据库·c#