CSAPP Architecture Lab PartC满分

news2024/11/24 9:13:07

CSAPP Architecture Lab

此lab涉及Y86-64的实现,具体Y86的内容可查看CSAPP第四章,做完本实验可以提高你对处理器设计以及软件与硬件的理解。

从CMU官网下载完所需实验包后,参考实验所给的官方文档simguide.pdf,首先建立实验环境,解压sim包,你可能会在make时遇到错误,这里对于错误的处理,我参照了这里的解决方法,简单来说,直接执行如下脚本即可(本人当时亲测可行)

wget https://gitee.com/lin-xi-269/csapplab/raw/master/lab4archlab/archlab-handout/installTclTk.sh && bash installTclTk.sh

实验分为三个部分,每个部分都有自己的处理方法。 在 A 部分,您将编写一些简单的 Y86-64程序并熟悉 Y86-64 工具。 在 B 部分,您将使用一个扩展 SEQ 模拟器新指令。 这两部分将为您准备 C 部分,这是实验室的核心,您将在其中进行优化Y86-64基准程序和处理器设计。

Part A

这一部分有三个题需要我们解决。

sum.ys

第一题,要求你根据所给的C语言代码编写对应的Y86-64汇编代码,关于Y86-64汇编代码的编写,可参考simguide.pdf中的Figure1示例或书中示例。本题所给C代码sum_list是一个链表求和的函数,直接模仿示例编写即可。

# Execution begins at address 0
        .pos 0
        irmovq stack,%rsp        # Set up stack pointer
        call main                   # Execute main program
        halt                        # Terminate program

# Sample linked list
.align 8
ele1:
        .quad 0x00a
        .quad ele2
ele2:
        .quad 0x0b0
        .quad ele3
ele3:
        .quad 0xc00
        .quad 0

main:
        irmovq ele1,%rdi
        call sumlist
        ret

sumlist:
        xorq %rax,%rax                  # sum = 0
loop:
        andq %rdi,%rdi                  # Set CC , 设置条件码
        je end                          # 为零,跳转
        mrmovq (%rdi),%rsi               # 解地址
        addq %rsi,%rax                   # val += ls->val
        irmovq $8,%rdx  
        addq %rdx,%rdi                  # rdi移动到ls->next
        mrmovq (%rdi),%rdi              # ls=ls->next
        jmp loop
end:
        ret
# Stack starts here and grows to lower addresses
        .pos 0x200
stack: 

经模拟器运行测试,测试结果如下:
在这里插入图片描述

返回值rax的值如我们所预期的0x00a+0x0b0+0xc00=0Xcba,说明所编写的程序正确。

rsum.ys

第二题,要求我们在第一题的基础上改为递归形式。

代码框架与第一题相同,需要改变的仅有rsum_list函数部分,需要注意的是调用函数时要保存相关的寄存器内容,即要把调用者保存的那些寄存器保存起来。

# Execution begins at address 0
        .pos 0
        irmovq stack,%rsp        # Set up stack pointer
        call main                   # Execute main program
        halt                        # Terminate program

# Sample linked list
.align 8
ele1:
        .quad 0x00a
        .quad ele2
ele2:
        .quad 0x0b0
        .quad ele3
ele3:
        .quad 0xc00
        .quad 0

main:
        irmovq ele1,%rdi
        xorq %rax,%rax                  # rax = 0
        irmovq $8,%rdx                  # 在main中设置偏移量,避免递归调用中重复设置
        call rsum_list
        ret

rsum_list:
        pushq %rbx                      # 被调用者保存,此时,此函数为被调用函数
        andq %rdi,%rdi                  # Set CC
        je end                          # 为零,跳转
        mrmovq  (%rdi),%rbx             # 解地址,ls->val,将val保存到被调用者保存寄存器rbx中
        addq %rdx,%rdi                   # ls->next
        mrmovq (%rdi),%rdi              # ls=ls->next
        call rsum_list                  
        addq %rbx,%rax
end:
        popq %rbx                      # 恢复
        ret

# Stack starts here and grows to lower addresses
        .pos 0x200
stack: 

测试结果如下,验证正确。

在这里插入图片描述

copy.ys

第三题,是数值拷贝,跟第一题差不多。

# Execution begins at address 0
        .pos 0
        irmovq stack,%rsp        # Set up stack pointer
        call main                   # Execute main program
        halt                        # Terminate program

# Sample linked list
.align 8
# Source block
src:
        .quad 0x00a
        .quad 0x0b0
        .quad 0xc00
# Destination block
dest:
        .quad 0x111
        .quad 0x222
        .quad 0x333

main:
        irmovq src,%rdi
        irmovq dest,%rsi
        irmovq $3,%rdx
        call copy_block
        ret

copy_block:
        xorq %rax,%rax
        irmovq $8,%rcx
        irmovq $1,%r8
loop:
        andq %rdx,%rdx                  # Set CC
        je end                          # 为零,跳转
        mrmovq  (%rdi),%rbx             # 解地址    val=*src
        addq %rcx,%rdi                  # src++
        rmmovq %rbx,(%rsi)              # *dest=val
        addq %rcx,%rsi                  # dest++
        xorq %rbx,%rax                  # result^=val
        subq %r8,%rdx                   # len--
        jmp loop
end:
        ret

# Stack starts here and grows to lower addresses
        .pos 0x200
stack: 

测试结果如下,验证正确。

在这里插入图片描述

Part B

在这部分中,在目录 sim/seq 中工作。任务是扩展 SEQ 处理器以支持 iaddq,如作业问题中所述4.51 和 4.52。 要添加此指令,我们将修改文件 seq-full.hcl,该文件实现了CS:APP3e 教科书中描述的 SEQ 版本。

先参考书上图4.18内容,完成iaddq的实现阶段。如下

#取值
    icode:ifun <- M1[PC]
    rA:rB <- M1[PC+1]
    valC <- M8[PC+2]
    valP <- PC+10
#译码
	valB <- R[rB]
#执行
	valE <- valB + valC
#访存

#写回
	R[rB] <- valE
#更新PC
	PC <- valP

随后根据上面的内容,完成对 seq-full.hcl 的修改,其实就是将IIADDQ添加都几个地方。这部分可以参考书上4.3.4节SEQ阶段的实现。

# !!增加一个IIADDQ常量值表示 iaddq指令
bool instr_valid = icode in 
	{ INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ,
	       IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ ,IIADDQ};

# Does fetched instruction require a regid byte?
# !! iaddq需要寄存器,加上
bool need_regids =
	icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ, 
		     IIRMOVQ, IRMMOVQ, IMRMOVQ ,IIADDQ};

# Does fetched instruction require a constant word?
# !! 同样也需要常数字
bool need_valC =
	icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL,IIADDQ };

## What register should be used as the B source?
# !! B source源 需要寄存器
word srcB = [
	icode in { IOPQ, IRMMOVQ, IMRMOVQ ,IIADDQ } : rB;
	icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
	1 : RNONE;  # Don't need register
];

## What register should be used as the E destination?
# !! E destination 目的需要寄存器
word dstE = [
	icode in { IRRMOVQ } && Cnd : rB;
	icode in { IIRMOVQ, IOPQ,IIADDQ} : rB;
	icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
	1 : RNONE;  # Don't write any register
];

## Select input A to ALU
# !!valC + valB
word aluA = [
	icode in { IRRMOVQ, IOPQ } : valA;
	icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ ,IIADDQ} : valC;
	icode in { ICALL, IPUSHQ } : -8;
	icode in { IRET, IPOPQ } : 8;
	# Other instructions don't need ALU
];

## Select input B to ALU
# !! valC + valB
word aluB = [
	icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, 
		      IPUSHQ, IRET, IPOPQ ,IIADDQ} : valB;
	icode in { IRRMOVQ, IIRMOVQ } : 0;
	# Other instructions don't need ALU
];

## Should the condition codes be updated?
# !! 条件码应该被更新
bool set_cc = icode in { IOPQ ,IIADDQ};

通过一下命令测试,测试指令来自官方文档。当执行make VERSION=full出现undefined reference to 'matherr'错误时,可以注释掉ssim.c里的两条相关代码。

make VERSION=full					#根据seq-full.hcl文件构建新的仿真器
./ssim -t ../y86-code/asumi.yo		#在小的Y86-64程序中测试你的方法
cd ../y86-code; make testssim		#在小的Y86-64程序中测试你的方法
cd ../ptest; make SIM=../seq/ssim	#测试除了iaddq以外的所有指令
cd ../ptest; make SIM=../seq/ssim TFLAGS=-i		#测试我们实现的iaddq指令

测试结果如下

在这里插入图片描述

在这里插入图片描述

Part C

在这部分中,在目录 sim/seq 中工作。任务是通过修改 ncopy.ys 和 pipe-full.hcl,目的是使 ncopy.ys尽可能快地跑。

我们先测试一下什么也不做能跑多快。参考官方文档进行测试。

make VERSION=full				# 构建模拟器
./psim -t sdriver.yo			# 模拟运行,4长度数组 
./psim -t ldriver.yo			# 模拟运行,63长度数组
./correctness.pl				# 测试ncopy.ys代码的正确性(数组长度0~65)
./benchmark.pl				# 测CPE

原始版本测得CPI: 897 cycles/765 instructions = 1.17 (ldriver.yo)Average CPE 15.18,不同机器可能略有差异。

1.首先可以想到得第一个点是使用Part B部分实现的iaddq,替换后如下。

# Version 1.0 !! iaddq
# You can modify this portion
	# Loop header
	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:
	iaddq $1, %rax		# count++
Npos:	
	iaddq $8, %rdi		# src++
	iaddq $8, %rsi		# dst++
	iaddq $-1,%rdx		# len--,同时设置状态码
	jg Loop			# if so, goto Loop:

测试得CPI: 677 cycles/545 instructions = 1.24(ldriver.yo)Average CPE 11.70

2.进一步我们考虑循环展开,我们考虑8*1展开

# Version 2.0 !! iaddq+循环展开
# You can modify this portion
	# Loop header
	xorq %rax,%rax		# count = 0;
	rrmovq %rdx,%rcx	# limit=len
	iaddq $-7,%rcx		# limit-=7	!!! 注意这里要步长减1,否则某些情况会少展开一次,甚至速度变慢(主要跟判断流程有关)
	jle Rest		# if so, goto Rest:

Loop:
# read val from src
	mrmovq (%rdi),%r8
	mrmovq 8(%rdi), %r9
	mrmovq 16(%rdi), %r10
	mrmovq 24(%rdi), %r11
	mrmovq 32(%rdi), %r12
	mrmovq 40(%rdi), %r13
	mrmovq 48(%rdi), %r14
	mrmovq 56(%rdi), %rbx
# store val to dst
	rmmovq %r8, (%rsi)
	rmmovq %r9, 8(%rsi)
	rmmovq %r10, 16(%rsi)
	rmmovq %r11, 24(%rsi)
	rmmovq %r12, 32(%rsi)
	rmmovq %r13, 40(%rsi)
	rmmovq %r14, 48(%rsi)
	rmmovq %rbx, 56(%rsi)

ele1:
	andq %r8,%r8		# val<=0?
	jle ele2			# if so,goto ele2
	iaddq $1,%rax		# count++ 
ele2:   
	andq %r9, %r9          
	jle ele3
	iaddq $1, %rax
ele3:   
	andq %r10, %r10
	jle ele4
	iaddq $1, %rax
ele4:   
	andq %r11, %r11
	jle ele5
	iaddq $1, %rax
ele5:   
	andq %r12, %r12
	jle ele6
	iaddq $1, %rax
ele6:   
	andq %r13, %r13
	jle ele7
	iaddq $1, %rax
ele7:   
	andq %r14, %r14
	jle ele8
	iaddq $1, %rax
ele8:   
	andq %rbx, %rbx
	jle End1
	iaddq $1, %rax
End1:
	iaddq $64,%rdi	# src+=8
	iaddq $64,%rsi	# dst+=8
	iaddq $-8,%rdx # len-=8
	iaddq $-8,%rcx	# limit-=8
	jg Loop

Rest:	# 不够8个展开,剩余的进行循环
	andq %rdx,%rdx		# len<=0
	jle Done
LoopRest:
	mrmovq (%rdi),%rbx	
	rmmovq %rbx,(%rsi)
	andq %rbx,%rbx
	jle End2
	iaddq $1,%rax
End2:
	iaddq $8,%rdi
	iaddq $8,%rsi
	iaddq $-1,%rdx
	jg LoopRest:

测试得CPI: 439 cycles/359 instructions = 1.22(ldriver.yo)Average CPE 8.61

3.尽管上面的CPE达到了8.61,但得分却只有Score 37.9/60.0。不妨进一步将剩余不够K个展开的,不再使用循环,展开成一个一个的。而且我们用到的寄存器也只有13个,还有一个rbp没用到(Y86-64共15个寄存器,除开rsp)。

# Version 2.1 !! iaddq + 9*1展开 
# You can modify this portion
	# Loop header
	xorq %rax,%rax		# count = 0;
	rrmovq %rdx,%rcx	# limit=len
	iaddq $-8,%rcx		# limit-=8
	jle Pos1		# if so, goto ele1:

Loop:
# read val from src
	mrmovq (%rdi),%r8
	mrmovq 8(%rdi), %r9
	mrmovq 16(%rdi), %r10
	mrmovq 24(%rdi), %r11
	mrmovq 32(%rdi), %r12
	mrmovq 40(%rdi), %r13
	mrmovq 48(%rdi), %r14
	mrmovq 56(%rdi), %rbx
	mrmovq 64(%rdi), %rbp

# store val to dst
	rmmovq %r8, (%rsi)
	rmmovq %r9, 8(%rsi)
	rmmovq %r10, 16(%rsi)
	rmmovq %r11, 24(%rsi)
	rmmovq %r12, 32(%rsi)
	rmmovq %r13, 40(%rsi)
	rmmovq %r14, 48(%rsi)
	rmmovq %rbx, 56(%rsi)
	rmmovq %rbp, 64(%rsi)
ele1:
	andq %r8,%r8		# val<=0?
	jle ele2			# if so,goto ele2
	iaddq $1,%rax		# count++ 
ele2:   
	andq %r9, %r9          
	jle ele3
	iaddq $1, %rax
ele3:   
	andq %r10, %r10
	jle ele4
	iaddq $1, %rax
ele4:   
	andq %r11, %r11
	jle ele5
	iaddq $1, %rax
ele5:   
	andq %r12, %r12
	jle ele6
	iaddq $1, %rax
ele6:   
	andq %r13, %r13
	jle ele7
	iaddq $1, %rax
ele7:   
	andq %r14, %r14
	jle ele8
	iaddq $1, %rax
ele8:   
	andq %rbx, %rbx
	jle ele9
	iaddq $1, %rax
ele9:
	andq %rbp,%rbp
	jle End1
	iaddq $1,%rax
End1:
	iaddq $72,%rdi	# src+=9
	iaddq $72,%rsi	# dst+=9
	iaddq $-9,%rdx # len-=9
	iaddq $-9,%rcx	# limit-=9
	jg Loop

# 不够9个展开,剩余的进行循环,剩下的最多只有8个
Pos1:
	iaddq $-1,%rdx
	jl Done
	mrmovq (%rdi),%r8
	rmmovq %r8,(%rsi)
	andq %r8,%r8
	jle Pos2
	iaddq 1,%rax
Pos2:
	iaddq $-1,%rdx
	jl Done
	mrmovq 8(%rdi),%r8
	rmmovq %r8,8(%rsi)
	andq %r8,%r8
	jle Pos3
	iaddq 1,%rax
Pos3:
	iaddq $-1,%rdx
	jl Done
	mrmovq 16(%rdi),%r8
	rmmovq %r8,16(%rsi)
	andq %r8,%r8
	jle Pos4
	iaddq 1,%rax
Pos4:
	iaddq $-1,%rdx
	jl Done
	mrmovq 24(%rdi),%r8
	rmmovq %r8,24(%rsi)
	andq %r8,%r8
	jle Pos5
	iaddq 1,%rax
Pos5:
	iaddq $-1,%rdx
	jl Done
	mrmovq 32(%rdi),%r8
	rmmovq %r8,32(%rsi)
	andq %r8,%r8
	jle Pos6
	iaddq 1,%rax
Pos6:
	iaddq $-1,%rdx
	jl Done
	mrmovq 40(%rdi),%r8
	rmmovq %r8,40(%rsi)
	andq %r8,%r8
	jle Pos7
	iaddq 1,%rax
Pos7:
	iaddq $-1,%rdx
	jl Done
	mrmovq 48(%rdi),%r8
	rmmovq %r8,48(%rsi)
	andq %r8,%r8
	jle Pos8
	iaddq 1,%rax
Pos8:
	iaddq $-1,%rdx
	jl Done
	mrmovq 56(%rdi),%r8
	rmmovq %r8,56(%rsi)
	andq %r8,%r8
	jle Done
	iaddq 1,%rax

测试得CPI: 400 cycles/331 instructions = 1.21(ldriver.yo)Average CPE 8.36 ,Score 42.8/60.0

4.回想本题要求,通过修改 ncopy.ys 和 pipe-full.hcl,目的是使 ncopy.ys尽可能快地跑。目前为止,处去PartB部分我们还未曾修改pipe-full.hcl,那么接下来就要对pipe-full.hcl动手了。

我们观察上方的汇编代码,可以发现形同mrmovq (%rdi),%r8 rmmovq %r8,(%rsi)这两组紧挨的指令可能会发生数据冒险,事实上也确实会发生,这会导致产生一个暂停。(因为只要一条指令执行了 load操作,从内存中读一个值到寄存器,并且下一条指令要用这个寄存器作为源操作数,就会产生一个暂停。如果要在执行阶段中使用这个源操作数,暂停是避免冒险的唯一方法。对于第二条指令将源操作数存储到内存的情况,例如 rmmovg或 push 指令,是不需要这样的暂停的。摘自课后家庭作业4.57。)所以我们根据作业4.57加以实现加载转发。关于此作业的相关解答,参考自这里。参考4.57作业添加加载转发如下。

## Generate valA in execute stage
word e_valA = [
	E_icode in { IRMMOVQ, IPUSHQ } && E_srcA == M_dstM : m_valM;
	1:E_valA;    # Pass valA through stage
];
################ Pipeline Register Control #########################

# Should I stall or inject a bubble into Pipeline Register F?
# At most one of these can be true.
bool F_bubble = 0;
bool F_stall =
	# Conditions for a load/use hazard
	E_icode in { IMRMOVQ, IPOPQ } &&
	( 
		E_dstM==d_srcB || 	#!! 只有E_dstM==d_srcA才可能使用加载转发,否则只有暂停
		(
			E_dstM==d_srcA && !(D_icode in {IRMMOVQ,IPUSHQ})	# 进一步只有rmmovq和pushq对valA的使用不会在phase E
		) 
	) ||
	# Stalling at fetch while ret passes through pipeline
	IRET in { D_icode, E_icode, M_icode };

# Should I stall or inject a bubble into Pipeline Register D?
# At most one of these can be true.
bool D_stall = 
	# Conditions for a load/use hazard
	E_icode in { IMRMOVQ, IPOPQ } && 
	(
		E_dstM==d_srcB || 	#!! 只有E_dstM==d_srcA才可能使用加载转发,否则只有暂停
		(
			E_dstM==d_srcA && !(D_icode in {IRMMOVQ,IPUSHQ})	# 进一步只有rmmovq和pushq对valA的使用不会在phase E
		) 
	);

bool D_bubble =
	# Mispredicted branch
	(E_icode == IJXX && !e_Cnd) ||
	# Stalling at fetch while ret passes through pipeline
	# but not condition for a load/use hazard
	!(				# !! 这里也涉及到了装载/使用冒险,所以也需要做修改
		E_icode in { IMRMOVQ, IPOPQ } && 					
		(
			E_dstM==d_srcB || 	#!! 只有E_dstM==d_srcA才可能使用加载转发,否则只有暂停
			(
				E_dstM==d_srcA && !(D_icode in {IRMMOVQ,IPUSHQ})	# 进一步只有rmmovq和pushq对valA的使用不会在phase E
			)
		) 
	) &&
	  IRET in { D_icode, E_icode, M_icode };

# Should I stall or inject a bubble into Pipeline Register E?
# At most one of these can be true.
bool E_stall = 0;
bool E_bubble =
	# Mispredicted branch
	(E_icode == IJXX && !e_Cnd) ||
	# Conditions for a load/use hazard
	(						# !! 这里也涉及到了装载/使用冒险,所以也需要做修改
		E_icode in { IMRMOVQ, IPOPQ } && 					
		(
			E_dstM==d_srcB || 	#!! 只有E_dstM==d_srcA才可能使用加载转发,否则只有暂停
			(
				E_dstM==d_srcA && !(D_icode in {IRMMOVQ,IPUSHQ})	# 进一步只有rmmovq和pushq对valA的使用不会在phase E
			)
		) 
	);

# Should I stall or inject a bubble into Pipeline Register M?
# At most one of these can be true.
bool M_stall = 0;
# Start injecting bubbles as soon as exception passes through memory stage
bool M_bubble = m_stat in { SADR, SINS, SHLT } || W_stat in { SADR, SINS, SHLT };

# Should I stall or inject a bubble into Pipeline Register W?
bool W_stall = W_stat in { SADR, SINS, SHLT };
bool W_bubble = 0;

同时对ncopy.ys算法流程进行优化

  • len先减(step-1)循环结束之后再加上(step-1)即可避免在循环内对limit变量的修改
  • 以及初始时不再对rax进行置零,默认是0。
  • 由于对于跳转指令的默认预测策略是选择分支进行跳转,所以我们利用这点,使得程序不要直接默认跳转到Done,因为大多数情况下不会跳到Done
  • 对不足K个一组的入口处进入优化

进行以上优化即可达到Average CPE 7.62 得分Score 57.7/60.0

# Version 2.1 !! iaddq + 9*1展开 + 算法流程优化		Score	57.7/60.0
# You can modify this portion
	# Loop header
	#xorq %rax,%rax		# count = 0;
	iaddq $-8,%rdx		# limit-=8
	jle Pos1		# if so, goto ele1:

Loop:
# read val from src
	mrmovq (%rdi),%r8
	mrmovq 8(%rdi), %r9
	mrmovq 16(%rdi), %r10
	mrmovq 24(%rdi), %r11
	mrmovq 32(%rdi), %r12
	mrmovq 40(%rdi), %r13
	mrmovq 48(%rdi), %r14
	mrmovq 56(%rdi), %rbx
	mrmovq 64(%rdi), %rbp

# store val to dst
	rmmovq %r8, (%rsi)
	rmmovq %r9, 8(%rsi)
	rmmovq %r10, 16(%rsi)
	rmmovq %r11, 24(%rsi)
	rmmovq %r12, 32(%rsi)
	rmmovq %r13, 40(%rsi)
	rmmovq %r14, 48(%rsi)
	rmmovq %rbx, 56(%rsi)
	rmmovq %rbp, 64(%rsi)
ele1:
	andq %r8,%r8		# val<=0?
	jle ele2			# if so,goto ele2
	iaddq $1,%rax		# count++ 
ele2:   
	andq %r9, %r9          
	jle ele3
	iaddq $1, %rax
ele3:   
	andq %r10, %r10
	jle ele4
	iaddq $1, %rax
ele4:   
	andq %r11, %r11
	jle ele5
	iaddq $1, %rax
ele5:   
	andq %r12, %r12
	jle ele6
	iaddq $1, %rax
ele6:   
	andq %r13, %r13
	jle ele7
	iaddq $1, %rax
ele7:   
	andq %r14, %r14
	jle ele8
	iaddq $1, %rax
ele8:   
	andq %rbx, %rbx
	jle ele9
	iaddq $1, %rax
ele9:
	andq %rbp,%rbp
	jle End1
	iaddq $1,%rax
End1:
	iaddq $72,%rdi	# src+=9
	iaddq $72,%rsi	# dst+=9
	iaddq $-9,%rdx # len-=9
	jg Loop

# 不够9个展开,剩余的进行循环,剩下的最多只有8个
Pos1:
	#iaddq $8,%rdx
	#iaddq $-1,%rdx
    iaddq $7,%rdx
	jge P1
	jmp Done
P1:
	mrmovq (%rdi),%r8
	rmmovq %r8,(%rsi)
	andq %r8,%r8
	jle Pos2
	iaddq 1,%rax
Pos2:
	iaddq $-1,%rdx
	jge P2
	jmp Done
P2:
	mrmovq 8(%rdi),%r8
	rmmovq %r8,8(%rsi)
	andq %r8,%r8
	jle Pos3
	iaddq 1,%rax
Pos3:
	iaddq $-1,%rdx
	jge P3
	jmp Done
P3:
	mrmovq 16(%rdi),%r8
	rmmovq %r8,16(%rsi)
	andq %r8,%r8
	jle Pos4
	iaddq 1,%rax
Pos4:
	iaddq $-1,%rdx
	jge P4
	jmp Done
P4:
	mrmovq 24(%rdi),%r8
	rmmovq %r8,24(%rsi)
	andq %r8,%r8
	jle Pos5
	iaddq 1,%rax
Pos5:
	iaddq $-1,%rdx
	jge P5
	jmp Done
P5:
	mrmovq 32(%rdi),%r8
	rmmovq %r8,32(%rsi)
	andq %r8,%r8
	jle Pos6
	iaddq 1,%rax
Pos6:
	iaddq $-1,%rdx
	jge P6
	jmp Done
P6:
	mrmovq 40(%rdi),%r8
	rmmovq %r8,40(%rsi)
	andq %r8,%r8
	jle Pos7
	iaddq 1,%rax
Pos7:
	iaddq $-1,%rdx
	jge P7
	jmp Done
P7:
	mrmovq 48(%rdi),%r8
	rmmovq %r8,48(%rsi)
	andq %r8,%r8
	jle Pos8
	iaddq 1,%rax
Pos8:
	iaddq $-1,%rdx
	jge P8
	jmp Done
P8:
	mrmovq 56(%rdi),%r8
	rmmovq %r8,56(%rsi)
	andq %r8,%r8
	jle Done
	iaddq 1,%rax
  • 最终版,经过一顿搜索,经过参考改进,有了如下满分版本。Average CPE 5.14Score 60.0/60.0。下面最主要的改变是直接修改pipe-full.hcl使其预测策略为不跳转,以及改为4*1展开,主要是为了减小较大的元素个数的CPE,依此来拉低整体的平均CPE。

额外对pipe-full.hcl做如下修改

# Predict next value of PC
word f_predPC = [
	
	f_icode in { ICALL } : f_valC;	# 删除IJXX,使其默认策略是不跳转
	1 : f_valP;
];
# Version 3 !! iaddq + 4*1展开 + 算法流程优化		Score	60.0/60.0 
# You can modify this portion
	# Loop header
	#xorq %rax,%rax		# count = 0;
	iaddq $-3,%rdx		# limit-=3
	jle Pos1		# if so, goto ele1:

Loop:
# read val from src
	mrmovq (%rdi),%r8
	mrmovq 8(%rdi), %r9
	mrmovq 16(%rdi), %r10
	mrmovq 24(%rdi), %r11


# store val to dst
	rmmovq %r8, (%rsi)
	rmmovq %r9, 8(%rsi)
	rmmovq %r10, 16(%rsi)
	rmmovq %r11, 24(%rsi)
	
ele1:
	andq %r8,%r8		# val<=0?
	jle ele2			# if so,goto ele2
	iaddq $1,%rax		# count++ 
ele2:   
	andq %r9, %r9          
	jle ele3
	iaddq $1, %rax
ele3:   
	andq %r10, %r10
	jle ele4
	iaddq $1, %rax
ele4:   
	andq %r11, %r11
	jle End1
	iaddq $1, %rax
End1:
	iaddq $32,%rdi	# src+=9
	iaddq $32,%rsi	# dst+=9
	iaddq $-4,%rdx # len-=9
	jg Loop

# 不够4个展开,剩余的进行循环,剩下的最多只有3个
Pos1:
	iaddq $3,%rdx

	jle Done
	mrmovq (%rdi),%r8
	rmmovq %r8,(%rsi)
	andq %r8,%r8
	jle Pos2
	iaddq 1,%rax
Pos2:
	iaddq $-1,%rdx
	jle Done
	mrmovq 8(%rdi),%r8
	rmmovq %r8,8(%rsi)
	andq %r8,%r8
	jle Pos3
	iaddq 1,%rax
Pos3:
	iaddq $-1,%rdx
	jle Done
	mrmovq 16(%rdi),%r8
	rmmovq %r8,16(%rsi)
	andq %r8,%r8
	jle Done
	iaddq 1,%rax

参考链接

课后习题参考链接

总结

Part A 和 Part B部分都非常容易,但Part C想要拿满分就比较困难,可能需要各种各样的优化。原本对于本章的内容自我感觉学的也是一知半解,但经过此实验,解题过程遇到不解的需要经常翻书,以及对家庭作业部分进行学习,加深了对本章的理解。明白了流水线化如何让不同的阶段并行操作,如何尽可能提高系统的吞吐量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/102472.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Zookeeper生产常用命令大全(最新3.8.0版本)

文章目录官方文档一、服务端二、客户端1、连接客户端2、help3、create1> 创建持久节点2> 创建临时节点3> 创建持久有序节点4> 创建临时有序节点5> 创建ttl节点6> 创建容器节点4、get5、set6、ls7、stat8、删除节点1> delete2> deleteall8、其他命令二、…

PID算法控制

文章目录一、PID算法二、控制方法对比PID的手动整定PID衰减曲线整定PID调节器各校正环节的作用是&#xff1a;计算注意事项PID算法的一般表达式是&#xff1a;一、PID算法 PID控制是最早发展的自动控制策略之一&#xff0c;是微机化控制系统的一个重要组成部分&#xff0c;整个…

大数据必学Java基础(一百一十五):Session域监听器

文章目录 Session域监听器 一、Session域共有四个监听器接口 二、监听器代码 Session域监听器

学好Python的未来7大就业方向,月薪不低于2w!

最近很多人都有一个问题&#xff1a;“ 我想学 Python&#xff0c;但是学完 Python 后都能干啥 &#xff1f;” “ 现在学 Python&#xff0c;哪个方向最简单&#xff1f;哪个方向最吃香 &#xff1f;” “ …… ” 相信不少 Python 的初学者&#xff0c;都会遇到上面的这些…

[ 数据结构 -- 手撕排序算法第六篇 ] 快速排序(非递归版本)

文章目录前言一、常见的排序算法二、快速排序的非递归版本三、具体步骤四、非递归的代码实现五、时间复杂度4.1最好情况4.2最坏情况六、总结前言 手撕排序算法第六篇&#xff1a;快速排序&#xff01; 从本篇文章开始&#xff0c;我会介绍并分析常见的几种排序&#xff0c;例如…

143744-88-1,标记肽Dansyl-GCVLS

法尼基二磷酸法尼基转移酶(FTase)的荧光底物。该五肽基于H-Ras的c端区域&#xff0c;其n端有一个丹酚基团。由于半胱氨酸巯基的法尼化作用&#xff0c;丹酚基从极性分子环境变为非极性分子环境&#xff0c;伴随而来的是荧光增强和丹酚基向较低波长发射最大值的转移。该产物的完…

【设计模式】代理模式 (六)

文章目录5.1 代理模式5.1.1 概述5.1.2 结构5.1.3 静态代理5.1.4 JDK动态代理5.1.5 CGLIB动态代理5.1.6 三种代理的对比5.1.7 优缺点5.1.8 使用场景5.1 代理模式 5.1.1 概述 由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时&#xff0c;访问对象不适合或者不…

程序员现状分析,什么时候是进入IT行业的黄金时期?千万注意了

近些来&#xff0c;程序员成为了很多人就业的首选。但是大多数人对于程序员的了解仅仅停留在了薪资待遇上&#xff0c;但是他们对于程序员的工作却所知甚少。甚至有不少人只听说了程序员的工资很高&#xff0c;并不知道程序员的工资会高到什么地步。 其实近些年来&#xff0c;随…

Mysql窗口函数 (知识点梳理+题目解析+面试实战)(四万字长文,一文读懂,建议收藏后食用)

前言&#xff1a; 本文章是原创50篇时开启的纪念作&#xff0c;之前的文章基本5000字&#xff0c;而本篇约4.5万字&#xff0c;真一篇顶九篇。 窗口函数作为Mysql 8的新特性以及面试笔试的重点&#xff0c;掌握并且进行来练习是非常有必要的。本文章详细介绍窗口函数的方…

uni-app app分享到朋友和朋友圈

app分享到朋友和朋友圈 uni.share(OBJECT) uni-app的App引擎封装了微信、QQ、微博的分享SDK&#xff0c;开发者可以直接调用相关功能 可以分享到微信、QQ、微博&#xff0c;每个社交平台被称为分享服务提供商&#xff0c;即provider。 可以分享文字、图片、图文、音乐、视频…

热门技术中的应用:微服务相关协议-第32讲-RPC协议综述:远在天边,近在眼前

前面我们讲了容器网络如何实现跨主机互通,以及微服务之间的相互调用。 网络是打通了,那服务之间的互相调用,该怎么实现呢?你可能说,咱不是学过Socket吗。服务之间分调用方和被调用方,我们就建立一个TCP或者UDP的连接,不就可以通信了? 你仔细想一下,这事儿没这么简单。…

[附源码]计算机毕业设计Python高校商铺管理系统论文(程序+源码+LW文档)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

微服务框架 SpringCloud微服务架构 微服务面试篇 54 微服务篇 54.3 Nacos 如何支撑数十万服务注册压力?

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 微服务面试篇 文章目录微服务框架微服务面试篇54 微服务篇54.3 Nacos 如何支撑数十万服务注册压力&#xff1f;54.3.1 Nacos 服务端源码54 微…

信息提取 Information Extraction

定义 信息提取任务是从一组非结构化自然语言文本中的每个文本中识别有关实体、关系或事件的预定义类的信息&#xff0c;并通过以下任一方式以结构化形式记录此信息&#xff1a; • 注释源文本&#xff0c;使用 XML 标签 • 填写与文本分开的数据结构&#xff0c;例如模板或数据…

centos8 nginx 开启 ssl(https)

centos8 nginx 开启 ssl&#xff08;https&#xff09;首先申请证书&#xff0c;以阿里云为例centos8 nginx 安装 sslnginx 配置文件遇到问题一直没有给域名开启 https&#xff0c;在 Cesium 沙盒模拟中使用 http 资源会提示错误&#xff0c;于是想着把 https 打开&#xff0c;…

【计算机毕业设计】基于HTML+CSS+JavaScript大学生心理咨询网设计毕业论文源码

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

53.Python的f格式化字符串

53.f格式化字符串 文章目录53.f格式化字符串1.知识回顾2.f格式化字符串3.课堂练习4. 字符串格式化总结1.知识回顾 之前我们学了2种格式化方法&#xff1a; %占位符格式化 .format字符串格式化 【目标任务】 原样输出&#xff1a;大家好&#xff0c;我叫XXX&#xff0c;来自X…

2022卡塔尔世界杯 | 致我们每个人那份绿茵场上的足球梦

一个沾满草腥味的皮球&#xff0c;在经过一系列有名气的或者没名气的金脚触碰之后&#xff0c;缓慢地或者迅速地躲过后卫和门将大义凛然的身体&#xff0c;越过了那个由门柱和横梁构成的透明长方体平面…… 如果你不是球迷&#xff0c;你真的很难理解这么一个皮革制品的物理位移…

全网最详细的网络安全入门教程,一篇文章满足你

随着当下正在发生的互联网革命&#xff0c;以及乌俄局势的网络战&#xff0c;网络安全行业已经受到更多人的关注&#xff0c;而这个行业的人才缺口将继续呈指数型扩张。借用行业内某大咖的一句话“网络安全行业的人才成材率极低”&#xff0c;究其原因还是因为网络安全从业者所…

MD编辑器使用方法

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…