archlab属于第四章的内容。这章讲了处理器体系结构,就CPU是怎样构成的.看到时候跃跃欲试,以为最后实验是真要去造个CPU,配套资料也是一如既往的豪华,合计四十多页的参考手册,一大包的源码和测试程序.意料之外是具体考你的不是"炼丹"(指沙土炼硅造芯),而是处理器级别的优化,要把处理器的性能榨干才能得满分.不愧是CMU,榨得我已经一滴脑汁也没有了,最后还只得了八成的分.

通过上次实验我知道了CMU喜欢给头铁之人留几个零头,所以剩下两成分我也不追求了.(耻辱下播)

前期准备

下载讲义和解压的部分略过,这里一讲各种依赖的设置.基本上就是make的时候缺啥补啥.

我用的系统是Unbutu,直接apt-get大法

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

Part A

这一部分是用Y86-64指令集把几个C程序实现成汇编形式来热热身.说来也巧,这次的几道汇编题里的优化小技巧都是我从几年前玩《人力资源机器》里学来的.

Y86-64指令集概括为:

  1. RF:程序寄存器
  2. rax,rbx,rcd,rdx,
  3. rsp,rbp,rsi,rdi,
  4. r8~r14
  5. CC:条件码
  6. ZF,SF,OF
  7. 指令集:
  8. halt
  9. nop
  10. rrmovq
  11. irmovq
  12. rmmovq
  13. mrmovq
  14. OPq(subq,addq,andq,xorq)
  15. jXX(jmp,jle,jl,je,jne,jge,jg)
  16. cmovXX(le,l,e,ne,ge,g)
  17. call
  18. ret
  19. pushq
  20. popq

注意这一部分需要我们内嵌测试代码,即在实现的时候需要手动在代码里添加运行栈,主函数,启动函数和测试数据,并且源文件末尾要空一行

sum_list

这一题我在用yis运行这段代码时结果错误,用ssim运行则正常,我认为时yis有问题

  1. #init function
  2. .pos 0x0
  3. irmovq stack,%rsp
  4. call main
  5. ret
  6. # Sample linked list
  7. .pos 0x200
  8. .align 8
  9. ele1:
  10. .quad 0x00a
  11. .quad ele2
  12. ele2:
  13. .quad 0x0b0
  14. .quad ele3
  15. ele3:
  16. .quad 0xc00
  17. .quad 0
  18. # main function
  19. main:
  20. irmovq ele1,%rdi
  21. call sum_list
  22. ret
  23. # sum_list function
  24. sum_list:
  25. irmovq $0,%rax
  26. jmp L1
  27. L2:
  28. mrmovq (%rdi),%rbx
  29. addq %rbx,%rax
  30. mrmovq 8(%rdi),%rdi
  31. L1:
  32. andq %rdi,%rdi
  33. jne L2
  34. ret
  35. #alloc stack space
  36. .pos 0x1000
  37. stack:

rsum_list

  1. #init function
  2. .pos 0x0
  3. irmovq stack,%rsp
  4. call main
  5. ret
  6. # Sample linked list
  7. .pos 0x200
  8. .align 8
  9. ele1:
  10. .quad 0x00a
  11. .quad ele2
  12. ele2:
  13. .quad 0x0b0
  14. .quad ele3
  15. ele3:
  16. .quad 0xc00
  17. .quad 0
  18. # main function
  19. main:
  20. irmovq ele1,%rdi
  21. call rsum_list
  22. ret
  23. # rsum_list function
  24. rsum_list:
  25. andq %rdi,%rdi
  26. je L1
  27. mrmovq (%rdi),%rbx
  28. mrmovq 8(%rdi),%rdi
  29. pushq %rbx
  30. call rsum_list
  31. popq %rbx
  32. addq %rbx,%rax
  33. ret
  34. L1:
  35. irmovq $0,%rax
  36. ret
  37. #alloc stack space
  38. .pos 0x1000
  39. stack:

copy_block

  1. #init function
  2. .pos 0x0
  3. irmovq stack,%rsp
  4. call main
  5. ret
  6. .align 8
  7. # Source block
  8. src:
  9. .quad 0x00a
  10. .quad 0x0b0
  11. .quad 0xc00
  12. # Destination block
  13. dest:
  14. .quad 0x111
  15. .quad 0x222
  16. .quad 0x333
  17. # main function
  18. main:
  19. irmovq src,%rdi
  20. irmovq dest,%rsi
  21. irmovq $3,%rdx
  22. call copy_block
  23. ret
  24. # copy_block function
  25. copy_block:
  26. irmovq $8,%r8
  27. irmovq $1,%r9
  28. irmovq $0,%rax
  29. addq %r9,%rdx
  30. jmp L2
  31. L1:
  32. mrmovq (%rdi),%rbx
  33. addq %r8,%rdi
  34. rmmovq %rbx,(%rsi)
  35. addq %r8,%rsi
  36. xorq %rbx,%rax
  37. L2:
  38. subq %r9,%rdx
  39. jne L1
  40. ret
  41. #alloc stack space
  42. .pos 0x1000
  43. stack:

Part B

这部分要求为处理器增加一个iaddq指令,参考课本的图4.18,很容易写出iaddq对应的各个阶段

  1. fetch:
  2. icode:ifun <- M1[PC]
  3. rA:rB <- M1[PC+1]
  4. valC <- M8[PC+2]
  5. valP <- PC+10
  6. decode:
  7. valB <- R[rB]
  8. execute:
  9. valE <- valB OP valC
  10. set CC
  11. memory:
  12. NONE
  13. write back:
  14. R[rB] <- valE
  15. PC update:
  16. PC <- valP

然后在seq-full.hcl文件里搜索IRRMOVQ和IOPQ相关项并修改之即可

  1. --- ~/Desktop/seq-full.hcl
  2. +++ ~/Desktop/sim/seq/seq-full.hcl
  3. @@ -106,16 +106,16 @@
  4. bool instr_valid = icode in
  5. { INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ,
  6. - IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ };
  7. + IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ, IIADDQ };
  8. # Does fetched instruction require a regid byte?
  9. bool need_regids =
  10. icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ,
  11. - IIRMOVQ, IRMMOVQ, IMRMOVQ };
  12. + IIRMOVQ, IRMMOVQ, IMRMOVQ, IIADDQ };
  13. # Does fetched instruction require a constant word?
  14. bool need_valC =
  15. - icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL };
  16. + icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL, IIADDQ };
  17. ################ Decode Stage ###################################
  18. @@ -128,7 +128,7 @@
  19. ## What register should be used as the B source?
  20. word srcB = [
  21. - icode in { IOPQ, IRMMOVQ, IMRMOVQ } : rB;
  22. + icode in { IOPQ, IRMMOVQ, IMRMOVQ, IIADDQ } : rB;
  23. icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
  24. 1 : RNONE; # Don't need register
  25. ];
  26. @@ -136,7 +136,7 @@
  27. ## What register should be used as the E destination?
  28. word dstE = [
  29. icode in { IRRMOVQ } && Cnd : rB;
  30. - icode in { IIRMOVQ, IOPQ} : rB;
  31. + icode in { IIRMOVQ, IOPQ, IIADDQ} : rB;
  32. icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
  33. 1 : RNONE; # Don't write any register
  34. ];
  35. @@ -152,7 +152,7 @@
  36. ## Select input A to ALU
  37. word aluA = [
  38. icode in { IRRMOVQ, IOPQ } : valA;
  39. - icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ } : valC;
  40. + icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IIADDQ } : valC;
  41. icode in { ICALL, IPUSHQ } : -8;
  42. icode in { IRET, IPOPQ } : 8;
  43. # Other instructions don't need ALU
  44. @@ -161,7 +161,7 @@
  45. ## Select input B to ALU
  46. word aluB = [
  47. icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL,
  48. - IPUSHQ, IRET, IPOPQ } : valB;
  49. + IPUSHQ, IRET, IPOPQ, IIADDQ } : valB;
  50. icode in { IRRMOVQ, IIRMOVQ } : 0;
  51. # Other instructions don't need ALU
  52. ];
  53. @@ -173,7 +173,7 @@
  54. ];
  55. ## Should the condition codes be updated?
  56. -bool set_cc = icode in { IOPQ };
  57. +bool set_cc = icode in { IOPQ, IIADDQ };
  58. ################ Memory Stage ###################################

一个小插曲是因为各种库更新换代,部分源代码失效导致我编译ssim的时候报错了

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

我的解决方法就是把所有带matherr的相关代码注释掉,主要就下面这两行.这两行没有在别处调用,且注释后在我整个实验过程中没有遇到任何副作用,大可放心.

  1. extern int matherr();
  2. int *tclDummyMathPtr = (int *) matherr;

Part C

要求首先在pipe-full.hcl实现iaddq指令,这部分照抄上面就行.然后对ncopy.ys汇编文件和进行pipe-full.hcl效率优化.要使之效率提高一半以上才有分数,蛮有难度的,我第一次优化完看见零分直接傻了.最后一同乱搞终于拿了47/60的分

这里讲一下的思路:

先通过CSAPP4.5.8节可知,我们这里可以对流水线的优化有:

  • 加载/使用冒险: 即在一条从内存读出一个值的指令和一条使用这个值的指令间,流水线必会暂停一个周期.可以通过避免使用刚从内存里读出的值解决.
  • 预测错误分支: 在分支逻辑发现不该选择分支之前,分支目标处几条指令已经进入流水线了.必须取消这些指令,并从跳转指令后面的那条指令开始取指.可以通过重新架构硬件更改处理器预测逻辑,或者写代码时迎合处理器预测逻辑解决.

    后者难度太高,我们主要优化前者,具体方法是在rmmovq或popq指令后避免使用刚读入的值.

    另外根据讲义提示,还可以参考CSAPP5.8节,通过循环展开,减少迭代判断语句的执行次数进行优化.举例子:
  1. //展开前
  2. for(int i=0;i<N;i++) sum+=a[i];
  3. //二路展开后
  4. for(int i=0;i<N;i+=2) sum+=a[i]+a[i+1];

我的方法是先六路展开,再二路展开,最后处理剩下的那一个.

另外rmmovq和mrmovq间的空隙不能浪费,不如再重复一边填充之

  1. xorq %rax,%rax
  2. jmp StartLoop6
  3. Loop6:
  4. mrmovq (%rdi),%r8
  5. mrmovq 8(%rdi),%r9
  6. rmmovq %r8,(%rsi)
  7. rmmovq %r9,8(%rsi)
  8. andq %r8,%r8
  9. jle L61
  10. iaddq $1,%rax
  11. L61:
  12. andq %r9,%r9
  13. jle L62
  14. iaddq $1,%rax
  15. L62:
  16. mrmovq 16(%rdi),%r8
  17. mrmovq 24(%rdi),%r9
  18. rmmovq %r8,16(%rsi)
  19. rmmovq %r9,24(%rsi)
  20. andq %r8,%r8
  21. jle L63
  22. iaddq $1,%rax
  23. L63:
  24. andq %r9,%r9
  25. jle L64
  26. iaddq $1,%rax
  27. L64:
  28. mrmovq 32(%rdi),%r8
  29. mrmovq 40(%rdi),%r9
  30. rmmovq %r8,32(%rsi)
  31. rmmovq %r9,40(%rsi)
  32. andq %r8,%r8
  33. jle L65
  34. iaddq $1,%rax
  35. L65:
  36. andq %r9,%r9
  37. jle L66
  38. iaddq $1,%rax
  39. L66:
  40. iaddq $48,%rdi
  41. iaddq $48,%rsi
  42. StartLoop6:
  43. iaddq $-6,%rdx
  44. jge Loop6
  45. iaddq $6,%rdx
  46. jmp StartLoop2
  47. Loop2:
  48. mrmovq (%rdi),%r8
  49. mrmovq 8(%rdi),%r9
  50. rmmovq %r8,(%rsi)
  51. rmmovq %r9,8(%rsi)
  52. andq %r8,%r8
  53. jle L21
  54. iaddq $1,%rax
  55. L21:
  56. andq %r9,%r9
  57. jle L22
  58. iaddq $1,%rax
  59. L22:
  60. iaddq $16,%rdi
  61. iaddq $16,%rsi
  62. StartLoop2:
  63. iaddq $-2,%rdx
  64. jge Loop2
  65. mrmovq (%rdi),%rbx
  66. iaddq $1,%rdx
  67. jne Done
  68. rmmovq %rbx,(%rsi)
  69. andq %rbx,%rbx
  70. jle Done
  71. iaddq $1,%rax

【CSAPP】Architecture Lab 实验笔记的更多相关文章

  1. 【CSAPP】Shell Lab 实验笔记

    shlab这节是要求写个支持任务(job)功能的简易shell,主要考察了linux信号机制的相关内容.难度上如果熟读了<CSAPP>的"异常控制流"一章,应该是可以不 ...

  2. 【CSAPP】Cache Lab 实验笔记

    cachelab这节先让你实现个高速缓存模拟器,再在此基础上对矩阵转置函数进行优化,降低高速缓存不命中次数.我的感受如上一节,实在是不想研究这些犄角旮旯的优化策略了. 前期准备 我实验的时候用到了va ...

  3. 【CSAPP】Attack Lab实验笔记

    attacklab这节玩的是利用一个字符串进行缓冲区溢出漏洞攻击,就小时候想象中黑客干的事儿. 做题的时候好几次感叹这些人的脑洞,"这都可以攻击?还能这么注入?这还可能借力打力?" ...

  4. 【CSAPP】Performance Lab 实验笔记

    perflab这节的任务是利用书中知识,来对图像处理中的Rotate和Smooth操作函数进行优化.这次没对上电波,觉得学了一堆屠龙之技.于我个人理解,现在计算机配置比以前高多了,连SWAP分区都几近 ...

  5. 【CSAPP】Bomb Lab实验笔记

    bomblab这节搞的是二进制拆弹,可以通俗理解为利用反汇编知识找出程序的六个解锁密码. 早就听闻BOMBLAB的大名,再加上我一直觉得反汇编是个很艰难的工作,开工前我做好了打BOSS心理准备.实际上 ...

  6. 【CSAPP】Data Lab实验笔记

    前天讲到要刚CSAPP,这一刚就是两天半.CSAPP果然够爽,自带完整的说明文档,评判程序,辅助程序.样例直接百万组走起,管饱! datalab讲的是整数和浮点数怎么用二进制表示的,考验的是用基本只用 ...

  7. ChCore Lab3 用户进程和异常处理 实验笔记

    本文为上海交大 ipads 研究所陈海波老师等人所著的<现代操作系统:原理与实现>的课程实验(LAB)的学习笔记的第三篇:用户进程与异常处理.所有章节的笔记可在此处查看:chcore | ...

  8. CSAPP buffer lab记录——IA32版本

    CSAPP buffer lab为深入理解计算机系统(原书第二版)的配套的缓冲区溢出实验,该实验要求利用缓冲区溢出的原理解决5个难度递增的问题,分别为smoke(level 0).fizz(level ...

  9. CSAPP Bomb Lab记录

    记录关于CSAPP 二进制炸弹实验过程 (CSAPP配套教学网站Bomb Lab自学版本,实验地址:http://csapp.cs.cmu.edu/2e/labs.html) (个人体验:对x86汇编 ...

随机推荐

  1. 为什么要配置JDK环境变量?

    1. PATH环境变量.作用是指定命令搜索路径,在shell下面执行命令时,它会到PATH变量所指定的路径中查找看是否能找到相应的命令程序.我们需要把 jdk安装目录下的bin目录增加到现有的PATH ...

  2. 说说 Redis 哈希槽的概念?

    Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有 16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽, 集群的每个节点 ...

  3. 在虚拟机里面安装wordpress

    第一步: 安装Mariadb数据库 使用下面代码安装 1 yum install mariadb-server.x86_64 设置开机启动和启动 1 systemctl start mariadb 2 ...

  4. 学习SVN02

    代码发布方案: 1,安装,优化 软件环境,(nginx,lvs)  <-------运维工程师 2,程序代码(不断更新).   <--------开发工程师,(开发,运维都可以发布) 3, ...

  5. (5) 图和表(Figure and Table) 【论文写作】

  6. 浅析Node与Element

    起因 起因有二: 在看winter老师的分享:<一个前端的自我修养>时,有注意到这么一幅图,里面有写childNode和children属性. 昨天有学弟问起我,能否自己定义一个所有元素节 ...

  7. .map() vs .forEach() vs for() 如何选择?

    访问原文地址 .map() vs .forEach() vs for() 笔者说,自己基本没怎么用过for()来遍历,主要是用.forEach(). 但是总是会被很多朋友说,这些人认为for()的速度 ...

  8. A小程序与B小程序相互跳转的一点记录

    要点速览: A小程序和B小程序关联同一个公众号 B程序的用户授权 A小程序和B小程序的用户关联 诸葛 io 统计用户访问信息 需求:微信放开小程序互跳的 API 后,一些导流和拉新等活动可以在新的小程 ...

  9. ubantu系统之 在当前文件夹打开终端

    直接安装一个软件包 "nautilus-open-terminal"终端输入:sudo apt-get install nautilus-open-terminal重启系统!

  10. 【每日日报】第三十八天---java与时间相关

    1 今天看了网上的课程 学习了java的关于时间的代码 获取时间 import java.util.Date; public class DateDemo { public static void m ...