Rainshaw's Blog - 计算机组织与体系结构实习
type: channel
link: https://blog.gaoruixiao.com/category/practice_for_computer_organization_and_architecture/
items: 计算机组织与体系结构实习Lab4——使用x86 SIMD指令对应用程序进行优化并分析
items: 计算机组织与体系结构实习Lab3——高速缓存的模拟、配置和优化
items: 计算机组织与体系结构实习Lab 2——RISC-V CPU模拟器设计与实现报告
items: 计算机组成与体系结构实习Lab1——处理器性能评测报告

计算机组织与体系结构实习Lab4——使用x86 SIMD指令对应用程序进行优化并分析
type: item
date: 2020-05-29T18:13:00+08:00
description: 使用x86 SIMD指令对应用程序进行优化并分析图像格式转化YUV2RGB:$$ \begin{bmatrix} R\\ G\\ B \end{bmatrix} = \begin{bmatrix} 1.164383 & 0 & 1.596027 \\ 1.164383 & -0.391762 & -0.812968 \\ 1.164383 & 2.017232 & 0 \end{bmatrix} \begin{bmatrix} Y-16\\ U-128\\ V-128 \end{bmatrix} $$Alpha 混合:$$ \begin{bmatrix} R^\prime\\G^\prime\\B^\prime \end{bmatrix} = \begin{bmatrix} A/256 & 0 & 0 \\ 0 & A/256 & 0 \\ 0 & 0 & A/256 \\ \end{bmatrix} \begin{bmatrix} R\\G\\B \end{bmatrix} $$RGB2YUV:$$ \begin{bmatrix} Y\\U\\V \end{bmatrix} = \begin{bmatrix} 0.256788 & 0.504129 & 0.097906 \\ -0.148223 & -0.290993 & 0.439216 \\ 0.439216 & -0.367788 & -0.071427 \end{bmatrix} \begin{bmatrix} R^\prime\\G^\prime\\B^\prime \end{bmatrix} + \begin{bmatrix} 16\\128\\128 \end{bmatrix} $$基础ISA直接实现上述矩阵乘法即可。源代码如下：void BasicProcessor() { auto *data = new Pixel[size]; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { int offset = i * width + j; int yi = (uint8_t) y[i][j]; int ui = (uint8_t) u[i / 2][j / 2]; int vi = (uint8_t) v[i / 2][j / 2]; int r = 1.164383 * (yi - 16) + 1.596027 * (vi - 128); int b = 1.164383 * (yi - 16) + 2.017232 * (ui - 128); int g = 1.164383 * (yi - 16) - 0.391762 * (ui - 128) - 0.812968 * (vi - 128); data[offset].r = between(r); data[offset].b = between(b); data[offset].g = between(g); } } for (int num = 0; num < frame_num; num++) { int a = num * 3 + 1; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { int offset = i * width + j; int r = data[offset].r * a / 256.0; int b = data[offset].b * a / 256.0; int g = data[offset].g * a / 256.0; int yi = 0.256788 * r + 0.504129 * g + 0.097906 * b + 16; int ui = -0.148223 * r - 0.290993 * g + 0.439216 * b + 128; int vi = 0.439216 * r - 0.367788 * g - 0.071427 * b + 128; result[num][0][offset] = yi; result[num][1][(i / 2) * (width / 2) + (j / 2)] = ui; result[num][2][(i / 2) * (width / 2) + (j / 2)] = vi; } } } delete[] data; }MMX扩展指令MMX是一个比较老的指令集，只支持整数运算，使用64位寄存器，在C++中可以通过引用mmintrin.h头文件。由于原本运算都是浮点数运算，这里我参考stackoverflow给出的算法，给出了使…

计算机组织与体系结构实习Lab3——高速缓存的模拟、配置和优化
type: item
date: 2020-05-10T15:19:00+08:00
description: 高速缓存的模拟、配置和优化单级Cache模拟单层Cache模拟器，主程序在source-code/CacheMain.cpp中，主要负责读取trace文件，并执行Cache模拟。由于第二阶段需与CPU模拟器级联，故新编写Cache.cpp文件并修改原有文件使得可以正确运行。MemoryManager模块修改主存在本次试验中是最底层的存储器，故对于主存必然命中。主存对于读取字节的响应延迟应为总线延迟和主存命中延迟之和。主存对于写若干字节的响应延迟应为总线延迟和主存命中延迟之和。将原有get,set函数修改，均加入延迟时间计算。Cache模块设计缓存在运行过程中，会不停接收到读、写请求，根据自身情况，分别响应读命中、读不命中、写命中、写不命中。对于读命中，从存储中取出内容，经过总线延迟和命中延迟完成动作。对于读不命中，首先找到空闲块或待替换的块，若write_back为真，则将被替代块写回下一层存储，再向下一层存储发出读请求替换至准备好的块中，再进行读命中操作，需经过总线延迟和命中延迟再加上下一级的延迟来完成。对于写命中，若write_back为真，则设置dirty为真，之后进行写入操作。若write_back为真，需经总线延迟与命中延迟完成；否则，需向下一级存储发出写请求，等待下一级存储也写入完成，需总线延迟、命中延迟和下一级的延迟完成。对于写不命中，若write_alloc为真，则需先替换块，再根据wirte_back决定写入时的操作。需要总线延迟、命中延迟再加上下一级的延迟完成；否则，直接向下一级传递请求，需总线延迟、命中延迟、下一级延迟完成。Miss Rate 随 Block Size 的变化如图所示为在不同Cache Size的条件下，Miss Rate 随 Block Size的变化趋势，取associativity为8。可以看出，随着Block Size的增大，Miss Rate会逐渐减小至一稳定值。Miss Rate 随 Associativity 的变化如图所示，为固定Block Size 为64Byte，Miss Rate随Associativity的变化趋势。可以看出，绝大多数情况，随着associativity的增大，Miss Rate也会逐渐减小至一稳定值。Write Through, Write Back, Write Allocate和 No-write Allocate访问延时差异如图所示，在不同的写策略时，总访问延时有一定区别。可以看到，Write Back策略访问延时明显低于Write Through；Write Allocate对延时的减小有微弱的帮助。与CPU模拟器级联修改Simulator模块、主程序模块，将Cache加入至流水线模拟之中。这里采用Cache模块计算访存延迟并返回给流水线模拟器，再将该延迟加入到流水线模拟器的周期数中，以便计算CPILevelCapacityAssociativityBlock SizeWrite PolicyHit LatencyL132KB8 ways64Write Back1 CPU CycleL2256KB8 ways64Write Back8 CPU CycleLLC8MB8 ways64Write Back20 CPU Cycle程序运行结果$ ./Sim ../$dir/others/ackermann.riscv Ackermann(0,0) = 1 Ackermann(0,1) = 2 Ackermann(0,2) = 3 Ackermann(0,3) = 4 Ackermann(0,4) = 5 Ackermann(1,0) = 2 Ackermann(1,1) = 3 Ackermann(1,2) = 4 Ackermann(1,3) = 5 Ackermann(1,4) = 6 Ackermann(2,0) = 3 Ackermann(2,1) = 5 Ackermann(2,2) = 7 Ackermann(2,3) = 9 Ackermann(2,4) = 11 Ackermann(3,0) = 5 Ackermann(3,1) = 13 Ackermann(3,2) = 29 Ackermann(3,3) = 61 Ackermann(3,4) = 125 Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 520797 Number of Instructions: 430659 Avg Cycles per Instrcution: 1.2093 Number of JALR Instructions: 13740…

计算机组织与体系结构实习Lab 2——RISC-V CPU模拟器设计与实现报告
type: item
date: 2020-04-11T00:38:00+08:00
description: RISC-V CPU模拟器设计与实现报告一、开发环境1.1 RISCV交叉编译工具的安装和使用为测试模拟器的正确性，必须将源程序编译为基于RISCV指令的可执行程序，而由于个人计算机一般是x86架构的，所以需要交叉编译工具在x86电脑上编译生成RISCV架构可执行程序。为配置环境，执行了以下操作从Github上下载了riscv-tools，从issues中得知，交叉编译工具已移动到riscv-gnu-toolchain项目中，于是执行了以下命令cd ~ && mkdir source-code && cd "$_" git clone --recursive https://github.com/riscv/riscv-gnu-toolchain cd riscv-gnu-toolchain注意到在编译riscv-gnu-toolchain时，默认配置为支持RV64IMAFDC指令集，即RV64GC指令集，如下图，此时使用该工具编译时即使设置参数-Wa, -march=rv64i仍会链接使用扩展指令集的标准函数，其中C指令很多，并且导致我们的模拟器无法从程序入口执行，必须寻找main函数的位置。所以，为了得到只使用RV64I的ELF程序，必须在编译riscv-gnu-toolchain时便指定指令集，于是执行了以下命令# cd riscv-gnu-toolchain mkdir build-rv64i && cd "$_" ../configure --with-arch=rv64i --prefix=$HOME/riscv/riscv64i make -j$(nproc)此时，使用riscv64-unknown-elf-gcc对测试代码编译时，便只会使用RV64I标准集的指令，为了谨慎起见，我仍然在编译命令中加入了相关参数。编译指令示例如下$HOME/riscv/riscv64i/bin/riscv64-unknown-elf-gcc -Wa,-march=rv64i test/matrixmulti.c test/lib.c -o riscv-elf/others/matrixmulti.riscv $HOME/riscv/riscv64i/bin/riscv64-unknown-elf-objdump -D riscv-elf/others/matrixmulti.riscv > riscv-elf/others/matrixmulti.s除RV64I基本指令集外，我还额外实现了M扩展集的部分指令，所以上述命令修改为mkdir build-rv64im && cd "$_" ../configure --with-arch=rv64im --prefix=$HOME/riscv/riscv64i make -j$(nproc) $HOME/riscv/riscv64im/bin/riscv64-unknown-elf-gcc -Wa,-march=rv64im test/matrixmulti.c test/lib.c -o riscv-elf/others/matrixmulti.riscv $HOME/riscv/riscv64im/bin/riscv64-unknown-elf-objdump -D riscv-elf/others/matrixmulti.riscv > riscv-elf/others/matrixmulti.s在我编写的编译测试代码的脚本文件中，均含有以下三句dir=~/riscv/riscv64im/bin rvgcc=$dir/riscv64-unknown-elf-gcc flags='-Wa,-march=rv64im'通过修改dir flags参数可以改变上述命令中的编译器位置和编译选项。脚本中默认为使用--with-arch=rv64im参数编译生成的编译器，并且编译测试代码时的编译选项为-Wa,-march=rv64im。二、设计概述2.1 设计目标首要目标是实现一个流水线的模拟器，能够处理基本指令集，完成程序设计目标，并且程序的鲁棒性必须好，应该能够实现拒绝不正常的内存访问等。其次，由于程序没有实现并行操作，实际上仍然是依次顺序执行5个阶段，所以部分冒险情况可以被简化。此外，本模拟器没有选择兼容Linux的系统调用，而是选择使用自定义的系统调用，因此只能运行专门为此编写的程序（相关源码参见test/文件夹），同时，由于课程作业中提供的测试文件均没有包含特殊的系统调用，因此本模拟器也可以运行它们（相关源码参见test-inclass/文件夹）。2.2 编译与运行本项目提供了一个自动化脚本runme.sh，用于编译本模拟器，并模拟运行测试程序。或者可以采用手动方式，命令如下# cd…

计算机组成与体系结构实习Lab1——处理器性能评测报告
type: item
date: 2020-04-07T10:19:00+08:00
description: 处理器性能评测报告我们针对系统评测的不同角度会采用不同的评测程序。在目前已有的评测程序中，为下列评测目标找到某些合适的评测程序（列出即可）测评目标测评程序测评目标测评程序CPU整点性能SPECint2017CPU浮点性能SPECfp2017计算机事务处理能力TPC-C嵌入式系统计算能力CoreMark2D处理能力3DMark3D处理能力3DMark并行计算性能LINPACK系统响应速度Loadrunner编译优化能力SPECint2017操作系统性能Sysbench多媒体处理能力PassMark SoundCheck™IO处理能力Iozone浏览器性能wrk网络传输速率NetperfJava运行环境性能Specjbb2005邮件服务性能servermark文件服务器性能servermarkWeb服务器性能servermark服务器功耗和性能SPECpower 阅读文献（Reinhold P.Weicker, An Overview of Common Benchmarks, IEEE Computer, December 1990.）并回答下面的问题简述用于性能评测的MIPS指标之含义，以及它是如何被计算的MIPS 有多种含义，使用时必须特别指出Native MIPS, 即每秒执行的百万指令数。由于 RISC 指令系统的精简设计， RISC 在相同情况下是 CISC 的 MIPS 的数倍。Peak MIPS, 指处理器的MIPS峰值，在数值上等于处理器频率。EDN MIPS, Dhrystone MIPS指运行特定程序得到的 Native MIPS 或者 VAX MIPSVAX MIPS, 指定 VAX 11/780 为 1MIPS，规定程序功能、语言、编译器，将待测机器的运行时间与 VAX 11/780 作比，比值就是待测机器的 MIPS 。使用linux下的剖视工具（例如gprof）对dhrystone和whetstone进行剖视，参考论文Table 1形式给出数据，你的结果和该论文是否一致，为什么?使用 -pg -static 进行编译.procedure profile for dhrystone(cc_dry2)从图片中可以看出,我的结果与论文基本相同.猜测原因是整型运算和库函数运行速度目前基本只与处理器频率相关,因此其占比没有很大变化.procedure profile for whetstone3从图片中可以看出, 我的结果与论文中不同, 我认为是计算机能力造成的.我的计算机和论文作者的计算机的浮点运算能力之间存在较大差异,导致了结果的不同.文中讨论了处理器之外可能对性能造成影响的因素，请分别使用两种不同的语言（例如C和Java）使用同一算法实现快速排序、矩阵乘法、求Ackermann函数，验证文中的观点。（请保留你的程序，我们在后面可能还会用到它）我的程序采用了 C 和 Python. 论文中认为除CPU外,编程语言、编译器、动态库、高速缓存以及内存大小都会影响到程序到性能编程语言: 在 C 和 Python 两种语言中, 众所周知 C 语言一般比 Python 语言要快.编译器: 不同到编译选项也会直接影响程序性能,优化选项 -O0 到 -O3 性能一般会增加, -register 选项会要求运算只使用寄存器, -ffast-math 会针对浮点运算进行某些优化.动态库: 通过剖视可以看出, 程序执行过程中总有一部分时间需要调用和执行库函数,不同库有不同实现方式,会对性能有所影响.高速缓冲: 一般缓存越大,性能越快. 这一点同样适用于内存.快排中,对一个长度为$10^5$的整数数列进行快排,该数列事先生成,两种语言各循环100次, C语言耗时15s, Python耗时39s矩阵乘法算法中,采用朴素的$O(n^3)$算法, 对两个 1000*1000 的整数矩阵进行矩阵乘法,各循环100次, C 耗时 54s, Python 不使用 numpy 库函数耗时103s, 利用 numpy 库函数仅耗时 13s求 Ackermann 函数, 对A(40,20)循环$10^6$次, C 耗时2s, python 耗时 1167s (一度以为卡死了...但做了下面的benchmark我又觉得这个时间还行).从以上结果可以看出, C 语言基本上都比 Python 的要快上一个数量级.性能评测 基于某个给定的计算机系统平台，使用dhrystone、whetstone、SPEC CPU2000开展评测、分析、研究并给出报告。工作背景和评测目标本次实验通过使用dhrystone,whetstone,SPEC CPU2000对个人PC进行测评，目标是掌握Benchmark的使用和测评估计办法。经过此次实验，我对Benchmark的…