• 【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
    
  • 相关阅读:
    linux查看网卡个数及速度
    win7下设置 WiFi AP
    Scalaz(2)- 基础篇:随意多态-typeclass, ad-hoc polymorphism
    复用的基础是组件化与标准化
    Java & Groovy & Scala & Kotlin
    函数式编程的类型系统:typeclass--Functor的解释--构造类型
    类型构造器-Functor[F[_]]
    类型构造器--高阶类型(构造器):Kind (type theory)--类型的元
    函数的类型:函数也是类型 (*)->*
    应用内存管理三要素
  • 原文地址:https://www.cnblogs.com/kangyupl/p/13196837.html
Copyright © 2020-2023  润新知