【CSAPP】Architecture Lab 實驗筆記


archlab屬於第四章的內容。這章講了處理器體系結構,就CPU是怎樣構成的.看到時候躍躍欲試,以為最后實驗是真要去造個CPU,配套資料也是一如既往的豪華,合計四十多頁的參考手冊,一大包的源碼和測試程序.意料之外是具體考你的不是"煉丹"(指沙土煉硅造芯),而是處理器級別的優化,要把處理器的性能榨干才能得滿分.不愧是CMU,榨得我已經一滴腦汁也沒有了,最后還只得了八成的分.
通過上次實驗我知道了CMU喜歡給頭鐵之人留幾個零頭,所以剩下兩成分我也不追求了.(恥辱下播)

前期准備

下載講義和解壓的部分略過,這里一講各種依賴的設置.基本上就是make的時候缺啥補啥.
我用的系統是Unbutu,直接apt-get大法

sudo apt-get install tcl8.5-dev tk8.5-dev flex

Part A

這一部分是用Y86-64指令集把幾個C程序實現成匯編形式來熱熱身.說來也巧,這次的幾道匯編題里的優化小技巧都是我從幾年前玩《人力資源機器》里學來的.
Y86-64指令集概括為:

RF:程序寄存器
rax,rbx,rcd,rdx,
rsp,rbp,rsi,rdi,
r8~r14

CC:條件碼
ZF,SF,OF

指令集:
halt
nop
rrmovq
irmovq
rmmovq
mrmovq
OPq(subq,addq,andq,xorq)
jXX(jmp,jle,jl,je,jne,jge,jg)
cmovXX(le,l,e,ne,ge,g)
call
ret
pushq
popq

注意這一部分需要我們內嵌測試代碼,即在實現的時候需要手動在代碼里添加運行棧,主函數,啟動函數和測試數據,並且源文件末尾要空一行

sum_list

這一題我在用yis運行這段代碼時結果錯誤,用ssim運行則正常,我認為時yis有問題

#init function
    .pos 0x0
    irmovq stack,%rsp
    call main
    ret

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

# main function
main:
    irmovq ele1,%rdi
    call sum_list
    ret

# sum_list function
sum_list:
    irmovq $0,%rax
    jmp L1
L2:
    mrmovq (%rdi),%rbx
    addq %rbx,%rax
    mrmovq 8(%rdi),%rdi
L1:
    andq %rdi,%rdi
    jne L2
    ret

#alloc stack space
    .pos 0x1000
stack:

rsum_list

#init function
    .pos 0x0
    irmovq stack,%rsp
    call main
    ret

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

# main function
main:
    irmovq ele1,%rdi
    call rsum_list
    ret

# rsum_list function
rsum_list:
    andq %rdi,%rdi
    je L1
    mrmovq (%rdi),%rbx
    mrmovq 8(%rdi),%rdi
    pushq %rbx
    call rsum_list
    popq %rbx
    addq %rbx,%rax
    ret
L1:
    irmovq $0,%rax
    ret

#alloc stack space
    .pos 0x1000
stack:

copy_block

#init function
    .pos 0x0
    irmovq stack,%rsp
    call main
    ret

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


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

# copy_block function
copy_block:
    irmovq $8,%r8
    irmovq $1,%r9
    irmovq $0,%rax
    addq %r9,%rdx
    jmp L2
L1:
    mrmovq (%rdi),%rbx
    addq %r8,%rdi
    rmmovq %rbx,(%rsi)
    addq %r8,%rsi
    xorq %rbx,%rax
L2:
    subq %r9,%rdx
    jne L1
    ret

#alloc stack space
    .pos 0x1000
stack:

Part B

這部分要求為處理器增加一個iaddq指令,參考課本的圖4.18,很容易寫出iaddq對應的各個階段

fetch:
icode:ifun <- M1[PC]
rA:rB <- M1[PC+1]
valC <- M8[PC+2]
valP <- PC+10

decode:
valB <- R[rB]

execute:
valE <-  valB OP valC
set CC

memory:
NONE

write back:
R[rB] <- valE

PC update:
PC <- valP

然后在seq-full.hcl文件里搜索IRRMOVQ和IOPQ相關項並修改之即可

--- ~/Desktop/seq-full.hcl
+++ ~/Desktop/sim/seq/seq-full.hcl
@@ -106,16 +106,16 @@
 
 bool instr_valid = icode in 
 	{ INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ,
-	       IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ };
+	       IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ, IIADDQ };
 
 # Does fetched instruction require a regid byte?
 bool need_regids =
 	icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ, 
-		     IIRMOVQ, IRMMOVQ, IMRMOVQ };
+		     IIRMOVQ, IRMMOVQ, IMRMOVQ, IIADDQ };
 
 # Does fetched instruction require a constant word?
 bool need_valC =
-	icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL };
+	icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL, IIADDQ };
 
 ################ Decode Stage    ###################################
 
@@ -128,7 +128,7 @@
 
 ## What register should be used as the B source?
 word srcB = [
-	icode in { IOPQ, IRMMOVQ, IMRMOVQ  } : rB;
+	icode in { IOPQ, IRMMOVQ, IMRMOVQ, IIADDQ  } : rB;
 	icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
 	1 : RNONE;  # Don't need register
 ];
@@ -136,7 +136,7 @@
 ## What register should be used as the E destination?
 word dstE = [
 	icode in { IRRMOVQ } && Cnd : rB;
-	icode in { IIRMOVQ, IOPQ} : rB;
+	icode in { IIRMOVQ, IOPQ, IIADDQ} : rB;
 	icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
 	1 : RNONE;  # Don't write any register
 ];
@@ -152,7 +152,7 @@
 ## Select input A to ALU
 word aluA = [
 	icode in { IRRMOVQ, IOPQ } : valA;
-	icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ } : valC;
+	icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IIADDQ } : valC;
 	icode in { ICALL, IPUSHQ } : -8;
 	icode in { IRET, IPOPQ } : 8;
 	# Other instructions don't need ALU
@@ -161,7 +161,7 @@
 ## Select input B to ALU
 word aluB = [
 	icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, 
-		      IPUSHQ, IRET, IPOPQ } : valB;
+		      IPUSHQ, IRET, IPOPQ, IIADDQ } : valB;
 	icode in { IRRMOVQ, IIRMOVQ } : 0;
 	# Other instructions don't need ALU
 ];
@@ -173,7 +173,7 @@
 ];
 
 ## Should the condition codes be updated?
-bool set_cc = icode in { IOPQ };
+bool set_cc = icode in { IOPQ, IIADDQ };
 
 ################ Memory Stage    ###################################

一個小插曲是因為各種庫更新換代,部分源代碼失效導致我編譯ssim的時候報錯了

USER@NAME:~/sim/seq$ make ssim VERSION=std
# Building the seq-std.hcl version of SEQ
../misc/hcl2c -n seq-std.hcl <seq-std.hcl >seq-std.c
gcc -Wall -O2 -isystem /usr/include/tcl8.5 -I../misc -DHAS_GUI -o ssim \
	seq-std.c ssim.c ../misc/isa.c -L/usr/lib -ltk -ltcl -lm
/tmp/ccmcBZXH.o:(.data.rel+0x0):對‘matherr’未定義的引用
collect2: error: ld returned 1 exit status
Makefile:44: recipe for target 'ssim' failed
make: *** [ssim] Error 1

我的解決方法就是把所有帶matherr的相關代碼注釋掉,主要就下面這兩行.這兩行沒有在別處調用,且注釋后在我整個實驗過程中沒有遇到任何副作用,大可放心.

extern int matherr();
int *tclDummyMathPtr = (int *) matherr;

Part C

要求首先在pipe-full.hcl實現iaddq指令,這部分照抄上面就行.然后對ncopy.ys匯編文件和進行pipe-full.hcl效率優化.要使之效率提高一半以上才有分數,蠻有難度的,我第一次優化完看見零分直接傻了.最后一同亂搞終於拿了47/60的分
這里講一下的思路:
先通過CSAPP4.5.8節可知,我們這里可以對流水線的優化有:

  • 加載/使用冒險: 即在一條從內存讀出一個值的指令和一條使用這個值的指令間,流水線必會暫停一個周期.可以通過避免使用剛從內存里讀出的值解決.
  • 預測錯誤分支: 在分支邏輯發現不該選擇分支之前,分支目標處幾條指令已經進入流水線了.必須取消這些指令,並從跳轉指令后面的那條指令開始取指.可以通過重新架構硬件更改處理器預測邏輯,或者寫代碼時迎合處理器預測邏輯解決.
    后者難度太高,我們主要優化前者,具體方法是在rmmovq或popq指令后避免使用剛讀入的值.
    另外根據講義提示,還可以參考CSAPP5.8節,通過循環展開,減少迭代判斷語句的執行次數進行優化.舉例子:
//展開前
for(int i=0;i<N;i++) sum+=a[i];
//二路展開后
for(int i=0;i<N;i+=2) sum+=a[i]+a[i+1];

我的方法是先六路展開,再二路展開,最后處理剩下的那一個.
另外rmmovq和mrmovq間的空隙不能浪費,不如再重復一邊填充之

	xorq %rax,%rax
	jmp StartLoop6

Loop6:
	mrmovq (%rdi),%r8
	mrmovq 8(%rdi),%r9
	rmmovq %r8,(%rsi)
	rmmovq %r9,8(%rsi)
	andq %r8,%r8
	jle L61
	iaddq $1,%rax
L61:	
	andq %r9,%r9
	jle L62
	iaddq $1,%rax
L62:
	
	mrmovq 16(%rdi),%r8
	mrmovq 24(%rdi),%r9
	rmmovq %r8,16(%rsi)
	rmmovq %r9,24(%rsi)
	andq %r8,%r8
	jle L63
	iaddq $1,%rax
L63:	
	andq %r9,%r9
	jle L64
	iaddq $1,%rax
L64:

	mrmovq 32(%rdi),%r8
	mrmovq 40(%rdi),%r9
	rmmovq %r8,32(%rsi)
	rmmovq %r9,40(%rsi)
	andq %r8,%r8
	jle L65
	iaddq $1,%rax
L65:	
	andq %r9,%r9
	jle L66
	iaddq $1,%rax
L66:

	iaddq $48,%rdi
	iaddq $48,%rsi
StartLoop6:
	iaddq $-6,%rdx
	jge Loop6
	
	iaddq $6,%rdx
	jmp StartLoop2
Loop2:
	mrmovq (%rdi),%r8
	mrmovq 8(%rdi),%r9
	rmmovq %r8,(%rsi)
	rmmovq %r9,8(%rsi)
	andq %r8,%r8
	jle L21
	iaddq $1,%rax
L21:	
	andq %r9,%r9
	jle L22
	iaddq $1,%rax
L22:

	iaddq $16,%rdi
	iaddq $16,%rsi
StartLoop2:
	iaddq $-2,%rdx
	jge Loop2

	mrmovq (%rdi),%rbx
	iaddq $1,%rdx
	jne Done
	rmmovq %rbx,(%rsi)
	andq %rbx,%rbx
	jle Done
	iaddq $1,%rax


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM