隐蔽通道场景演示——操作系统与虚拟化安全实验三
实验目的选择一个已经标识的隐蔽通道，通过在SecLinux环境中进行实际编程（Ｃ语言），完成发送程序、接收程序的编写，并进行收发场景的实际效果演示。实验内容构建测试环境，选择同步机制发送和接收程序的编写、编译及运行演示存在的隐蔽通道场景实验准备使用 root 进入系统，执行 user_auth 命令获得特权发现证书过期，使用 initcert 命令生成证书再次执行 user_auth 获取特权使用 root 用户创建用户并设置安全级，设置用户密码并设置用户主目录安全级$ useradd -m -h SYS_ADMIN -v SYS_ADMIN high $ passwd high $ useradd -m -h SYS_PUBLIC -v SYS_PUBLIC low $ passwd low $ setlevel SYS_ADMIN /home/high $ setlevel SYS_PUBLIC /home/low分别从两个不同终端以 high 和 low 用户登录使用两个用户设置各自的主目录自主访问权限为0777验证 MAC 策略是否正常说明不可上读说明可以下读，但不能下写实验过程、结果同步机制采用 sleep（秒）/usleep（微秒）制造同步。当一个进程 sleep 时，就将 cpu 的控制权交给其他进程，直到 sleep 时间已到，重新获得 cpu 的控制权。实验结果（采用进程标识符信道）如下面两幅图，左侧为发送端，右侧为接收端，可以看到进程标识符信道较易受到噪声的影响导致接受信息出错。实验代码（采用进程标识符信道）发送方代码：#include <stdio.h> #include <sys/types.h> #include <unistd.h> pid_t pid[128]; const int sync_time = 3; void send(char ch){ int i; printf("sending '%c'\n", ch); for(i = 0; i < ch; i++){ pid[i]=fork() if(pid[i]==0){ sleep(1); exit(0); } } printf("\tcreate %d processes!\n", ch); } int main(){ char ch; FILE* f = fopen("in.txt", "r"); ch = fgetc(f); while(1){ send(ch); sleep(sync_time); if(ch=='!') break; ch = fgetc(f); } printf("Send Done!\n"); fclose(f); }接收方代码：#include <stdio.h> #include <sys/types.h> #include <unistd.h> pid_t firstPid, lastPid; const int sync_time = 3; int main(){ int k = 0; char ch; FILE *f = fopen('out.txt', 'w'); if ((firstPid=fork())==0){ exit(0); } sleep(1); while(1){ sleep(sync_time); if((lastPid=fork())==0){ exit(0); } printf('firstPid=%d, lastPid=%d\n', firstPid, lastPid); if(k==0){ ch=lastPid-firstPid-2; }else{ ch=lastPid-firstPid-1; } printf("\tReceive the '%c'\n", ch); fputc(ch, f); firstPid = lastPid; k=1; if(ch == '!') break; } printf("Receive Done!\n"); fclose(f); }流程图（采用进程标识符信道）sequenceDiagram participant Sender participant Receiver Receiver ->> Receiver: start, firstPid, sleep 3 Sender ->> Sender: start loop send message Sender ->> Sender: read char, create int(char) processes Sender ->> Receiver: send the char(char) Receiver ->> Receiver: lastPid, calc char(char), firstPid=lastPid end Sender ->> Sender: end Receiver ->> Receiver: end实验结果（采用最近访问时间信道）如下图，左侧为发送端，右侧为接收端，多次实验发现消息传递十分稳定。实验代码（采用最近访问时间信道）发送方代码：#include <stdio.h> #include <sys/stat.h> const int sync_time = 5; void send(char ch){ int i; char file_name[50]; printf("sending '%c'\n", ch); for(i = 0; i < ch; i++){ sprintf(file_name, "/tmp/%d.txt", i); FILE *fp = fopen(file_name, "r"); fgetc(fp); fclose(fp); } printf("\tvisit %d files!\n", ch); } int main(){ char ch; FILE* f = fopen("in.txt", "r"); ch = fgetc(f); while(1){ send(ch); sleep(sync_time); if(ch=='!') break; ch = fgetc(f); } printf("Send Done!\n"); fclose(f); }接收方代码：#include <stdio.h> #include <sys/stat.h> #include <sys/types.h> const int sync_time = 5; int main(){ int i = 0; char ch, file_name[50]; FILE *f = fopen('out.txt', 'w'); printf("perpare for files in /tmp\n"); for (i = 0; i < 128; i++){ sprintf(file_name, "/tmp/%d.txt", i); FILE *fp = fopen(file_name, "w"); fclose(fp); chmod(file_name, S_IRWXU|S_IRWXG|S_IRWXO); } printf("perpare files complete\n"); while(1){ sleep(sync_time); struct stat buf; int old_time = -1, new_time; for(i = 0; i< 128; i++){ sprintf(file_name, "/tmp/%d.txt", i); stat(file_name, &buf); new_time = buf.st_atime; if(old_time != -1 && old_time - new_time > 1){ ch = (char)i; fputc(ch, f); printf("\told time: %d, new time: %d\n", old_time, new_time); printf("\tReceive the '%c'\n", ch); break; } old_time = new_time; } if(ch == '!') break; } printf("Receive Done!\n"); fclose(f); }流程图（采用最近访问时间信道）sequenceDiagram participant Sender participant Receiver Receiver ->> Receiver: start, create files Sender ->> Sender: start loop send message Sender ->> Sender: read char, visit int(char) files Sender ->> Receiver: send the char(char) Receiver ->> Receiver: count visited files end Sender ->> Sender: end Receiver ->> Receiver: end实验总结实验收获对隐蔽通信信道有了更深的了解，进一步熟悉了安胜系统的命令。实验过程中遇到的问题及解决方法虚拟机无法安装 VMware Tools ，只能用 vim 导致编程困难，没有找到好的解决办法。使用进程标识符通道时，第一个接收到的字符总是不对，后来发现是由于先运行 receiver 后运行 sender 导致第一次接收时应该用 lastPid-firstPid-2.使用最近访问实际通道时，执行发送程序总是提示段错误，原因是接收程序创建的临时文件权限未放开，使用 chmod(file_name, S_IRWXU|S_IRWXG|S_IRWXO) 函数为通信文件设置权限为 777 即可。总结实验的不足之处，以及进一步的改进措施进程标识符通信信道由于较易受到噪声的影响，消息的传递不够稳定，选用其他的隐蔽通信信道如最近访问时间信道进行试验后发现，消息的传递变得稳定。代码中采取的睡眠同步技术容易实现，适于演示。但是由于进程发送不同字符所用时间不同，而 sleep 时间一旦指定就是固定的，不能在程序运行期间更改，因此这种同步必然造成时间浪费，改进措施就是选用其他的同步机制，例如作业中提到的最近访问时间的方法。
操作系统信任链扩展——操作系统与虚拟化安全实验二
实验目的了解TPM安全芯片的组成和作用掌握计算平台信任链扩展的原理及作用掌握IMA的工作原理及作用实验内容扩展Linux系统增加IMA功能，使信任链由OS层扩展到用户应用层。运行三类程序，观察IMA的作用：用户应用程序、共享库、内核模块。实验过程、结果实验使用的是 Ubuntu16.04 操作系统在该版本内核中，已默认勾选IMA功能，并挂载了 securityfs 文件系统为系统启用 IMA 功能，编辑引导文件 /etc/default/grub，增加启用选项：即在 GRUB_CMDLINE_LINUX 一项中加入 ima_tcb 即可更新引导文件， sudo update-grub2，重启系统查看IMA记录的应用度量值运行程序观察IMA的作用用户应用程序编写任意C应用程序hello.c，编译并运行，观察观察ima securityfs的变化。程序的编译和运行参考如下命令：$ gcc hello.c -o hello $ ./hellohello.c#include <stdio.h> int main() { printf("Hello World\n"); } 运行 cat/sys/kernel/security/ima/ascii_runtime_measurements 命令后，结果如下图所示。可以看到运行应用程序之后 /home/dreamer/codes/hello 出现在 IMA 度量值中。共享库编写任意Linux共享库(*.so)，并编写任意c应用链接此共享库，编译并运行，观察 ima securityfs 的变化，参考以下命令$ gcc -o libhello.so -fPIC -shared hello.c $ cp libhello.so /lib $ gcc -o main -lhello main.c $ ./mainhello.c#include <stdio.h> void print_hello() { printf("Hello World\n"); }main.cextern void print_hello(); int main(int argc, char argv[]) { print_hello(); return 0; }运行 cat /sys/kernel/security/ima/ascii_runtime_measurements 命令后，观察到结果如下图所示，/lib/libhello.so 和 /home/dreamer/codes/2/main 出现在 IMA 度量值中。内核模块编写任意 Linux 内核模块，编译并加载，观察 ima securityfs 的变化。命令参考如下$ make $ insmod hello.ko $ dmesg $ rmmod hello.ko $ dmesghello.c#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> /*用于告诉内核该模块拥有GPL license*/ MODULE_LICENSE("GPL"); /*执行模块初始化工作*/ static int __init hello_init(void) { printk(KERN_INFO "Hello,module\n"); return 0; } /*执行模块析构工作*/ static void __exit hello_exit(void) { printk(KERN_INFO "Goodbye,module\n"); } /*该函数注册模块的构造函数*/ module_init(hello_init); /*该函数注册模块的析构函数*/ module_exit(hello_exit);Makefileobj-m := hello.o all: make -C /lib/modules/$(shell uname –r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname –r)/build M=$(PWD) clean 查看 kernel message 可以发现程序的打印信息。运行 cat /sys/kernel/security/ima/ascii_runtime_measurements 命令后，观察到结果如下图所示 /home/dreamer/codes/3 目录下的各文件出现在 IMA 度量值中。实验总结实验收获学到了IMA方面的知识，对Linux操作系统有了进一步的了解。遇到的问题和解决办法IMA 功能虽默认编译进内核，但默认情况下不会启用，执行 cat /sys/kernel/security/ima/ascii_runtime_measurements 命令得不到正确的结果，解决办法已记录在上面三、实验过程中的第3、4步骤。在使用共享库时，无法编译 main.c ，错误提示为“未声明的函数”，这其实是 gcc 的特性，需要将依赖项写在后面，源码写在前面，即编译命令应该为 gcc -o main main.c -lhello总结实验的不足，及进一步的改进措施无
Linux安装和内核编译实践——操作系统与虚拟化安全实验一
实验目的学习 Linux 内核编译和升级的过程，并从中学习 Linux 内核的组成，掌握排错能力。实验内容安装 Parallels Desktop 并初始化 Linux 虚拟机下载新版本 Linux 内核进行编译安装升级实验过程、结果安装 Parallels Desktop，并从网上下载 Ubuntu16.04 镜像（略）使用 Ubuntu16.04 镜像新建虚拟机，并做初始化工作更新系统软件并查看内核版本可以看到Ubuntu16.04默认的内核版本为4.15下载内核并进行编译安装升级到 https://www.kernel.org/ 查看最新的Linux内核版本为4.19，下载压缩包并拷贝到 /usr/src 目录下进入 /usr/src 目录下，解压 cd /usr/src && sudo tar -xvf linux-4.19.149.tar.xz进入 linux-4.19.149 目录，执行 tree -dL 1文件夹简介arch根据cpu架构区分的代码block块设备驱动程序certs内置证书crypto加密，压缩drivers设备驱动程序fs文件系统include头文件lib库函数文件mm内存管理net网络协议代码scripts配置脚本securitySElinux的模块sound音频设备的驱动程序virt内核虚拟化执行 sudo make menuconfig，遇到报错原因是缺少 libncurses-dev 使用命令 sudo apt install libncurses-dev 进行安装后再次运行 sudo make menuconfig，遇到报错可以看到出错的位置是 bison: not found 即缺少 bison ，使用命令 sudo apt install bison 解决，再次运行 sudo make menuconfig，遇到报错可以看到出错的位置是 flex: not found 即缺少 flex， 使用命令 sudo apt install flex 解决， 再次运行 sudo make menuconfig 无报错使用默认配置，选择 Exit 并保存编译内核，执行命令 sudo make -j12，遇到报错可以看出是缺少 libssl-dev 库，使用 sudo apt install libssl-dev 进行安装后再次执行命令 sudo make -j12，没有遇到报错安装内核，依次执行命令sudo make modules_install sudo make install更新 grub 引导后重启sudo update-grub2 && sudo reboot执行 uname -r 查看内核版本实验总结内核升级必要的包需要哪些？答：从排错过程来看，需要libncurses-dev, bison, flex, libssl-dev编译内核的步骤是什么？答：首先使用命令生成配置文件 .config 实验中采用的是 make menuconfig 即采用基于文本菜单的配置命令，除此之外还有make config 也可以生成 .config 文件，后者是基于文本配置界面的配置命令。然后使用命令 make -j12 开始编译。之后使用命令 make modules_install 进行模块的编译和安装。最后进行 make install 进行安装并使用命令 upgrade-grub2 更新引导。在编译过程中可能遇到的问题有哪些，解决的办法是什么？缺少依赖包、库，解决办法是安装这些缺少的依赖项。你具体遇到了什么困难，怎么解决的？没怎么遇到困难。出现的错误都很好解决。本次实验让我学习了 Linux 内核的编译与升级步骤，了解了内核的组成。
计算机组织与体系结构实习Lab4——使用x86 SIMD指令对应用程序进行优化并分析
使用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给出的算法，给出了使用整数并行运算的实现。源代码如下：void MMXProcessor() { auto *data = new __m64[size]; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { auto yi = (uint8_t) y[i][j]; auto ui = (uint8_t) u[i / 2][j / 2]; auto vi = (uint8_t) v[i / 2][j / 2]; __m64 yuv = _mm_setr_pi16(16, 128, 128, 0); yuv = _mm_sub_pi16(_mm_setr_pi16(yi, ui, vi, 0), yuv); __m64 r = _mm_madd_pi16(yuv, _mm_setr_pi16(298, 0, 409, 0)); __m64 g = _mm_madd_pi16(yuv, _mm_setr_pi16(298, -100, -208, 0)); __m64 b = _mm_madd_pi16(yuv, _mm_setr_pi16(298, 516, 0, 0)); int rr = (_mm_cvtsi64_si32(r) + int(_mm_cvtm64_si64(r) >> 32)); int gg = (_mm_cvtsi64_si32(g) + int(_mm_cvtm64_si64(g) >> 32)); int bb = (_mm_cvtsi64_si32(b) + int(_mm_cvtm64_si64(b) >> 32)); __m64 val = _mm_setr_pi32(rr, gg); val = _mm_add_pi32(val, _mm_setr_pi32(128, 128)); val = _mm_srai_pi32(val, 8); int ri = between(_mm_cvtsi64_si32(val)); int gi = between(int(_mm_cvtm64_si64(val)>>32)); int bi = between((bb + 128) >> 8); data[i * width + j] = _mm_setr_pi16(ri, gi, bi, 0); } } 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; __m64 rgb = _mm_srli_pi16(_mm_mullo_pi16(data[offset], _mm_set1_pi16(a)), 8); __m64 yi = (_mm_madd_pi16(rgb, _mm_setr_pi16(66, 129, 25, 0))); __m64 ui = (_mm_madd_pi16(rgb, _mm_setr_pi16(-38, -74, 112, 0))); __m64 vi = (_mm_madd_pi16(rgb, _mm_setr_pi16(112, -94, -18, 0))); int yy = (_mm_cvtsi64_si32(yi) + int(_mm_cvtm64_si64(yi) >> 32)); int uu = (_mm_cvtsi64_si32(ui) + int(_mm_cvtm64_si64(ui) >> 32)); int vv = (_mm_cvtsi64_si32(vi) + int(_mm_cvtm64_si64(vi) >> 32)); __m64 val = _mm_setr_pi32(yy, uu); val = _mm_add_pi32(val, _mm_setr_pi32(128, 128)); val = _mm_srai_pi32(val, 8); val = _mm_add_pi32(val, _mm_setr_pi32(16, 128)); int ri = between(_mm_cvtsi64_si32(val)); int gi = between(int(_mm_cvtm64_si64(val)>>32)); int bi = between(((vv + 128) >> 8) + 128); result[num][0][offset] = ri; result[num][1][(i / 2) * (width / 2) + j / 2] = gi; result[num][2][(i / 2) * (width / 2) + j / 2] = bi; } } } delete[] data; };SSE2扩展指令SSE和SSE2指令集使用128位寄存器，可同时处理4个单精度浮点数或2个双精度浮点数。由于图像转化过程精度要求不高，所以可以简单的将程序并行化。在C++中可以引用emmintrin.h头文件。源代码如下：void SSE2Processor() { auto *data = new __m128[size]; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { auto yi = (uint8_t) y[i][j]; auto ui = (uint8_t) u[i / 2][j / 2]; auto vi = (uint8_t) v[i / 2][j / 2]; __m128 rgb = _mm_setr_ps(yi, ui, vi, 0); float r, g, b; rgb = _mm_sub_ps(rgb, _mm_setr_ps(16, 128, 128, 0)); _mm_store_ss(&r, _mm_dp_ps(rgb, _mm_setr_ps(1.164383, 0, 1.596027, 0), 0b01110001)); _mm_store_ss(&g, _mm_dp_ps(rgb, _mm_setr_ps(1.164383, -0.391762, -0.812968, 0), 0b01110001)); _mm_store_ss(&b, _mm_dp_ps(rgb, _mm_setr_ps(1.164383, 2.017232, 0, 0), 0b01110001)); r = between(r); g = between(g); b = between(b); data[i * width + j] = _mm_setr_ps(r, g, b, 0); } } 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; __m128 rgba = _mm_mul_ps(data[offset], _mm_set1_ps(a / 256.0)); float yi, ui, vi; _mm_store_ss(&yi, _mm_dp_ps(rgba, _mm_setr_ps(0.256788, 0.504129, 0.097906, 0), 0b01110001)); _mm_store_ss(&ui, _mm_dp_ps(rgba, _mm_setr_ps(-0.148223, -0.290993, 0.439216, 0), 0b01110001)); _mm_store_ss(&vi, _mm_dp_ps(rgba, _mm_setr_ps(0.439216, -0.367788, -0.071427, 0), 0b01110001)); result[num][0][offset] = yi + 16; result[num][1][(i / 2) * (width / 2) + j / 2] = ui + 128; result[num][2][(i / 2) * (width / 2) + j / 2] = vi + 128; } } } delete[] data; };AVX扩展指令AVX扩展指令使用256位寄存器，可同时处理8个单精度浮点数，这里我每次使用3个256位寄存器，一次处理8个数据点，极大的提高了并行效率。在C++中可以引用immintrin.h头文件。源代码如下：void AVXProcessor() { auto *data = new __m256[size * 3 / 8]; int cont = 0; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { ... __m256 y0 = _mm256_setr_ps(yi0, yi1, yi2, yi3, yi4, yi5, yi6, yi7); y0 = _mm256_sub_ps(y0, _mm256_set1_ps(16)); y0 = _mm256_mul_ps(y0, _mm256_set1_ps(1.164383)); __m256 u0 = _mm256_setr_ps(ui0, ui1, ui2, ui3, ui4, ui5, ui6, ui7); u0 = _mm256_sub_ps(u0, _mm256_set1_ps(128)); __m256 v0 = _mm256_setr_ps(vi0, vi1, vi2, vi3, vi4, vi5, vi6, vi7); v0 = _mm256_sub_ps(v0, _mm256_set1_ps(128)); __m256 r = _mm256_add_ps(y0, _mm256_mul_ps(v0, _mm256_set1_ps(1.596027))); __m256 g = _mm256_add_ps(y0, _mm256_add_ps(_mm256_mul_ps(u0, _mm256_set1_ps(-0.391762)), _mm256_mul_ps(v0, _mm256_set1_ps(-0.812968)))); __m256 b = _mm256_add_ps(y0, _mm256_mul_ps(u0, _mm256_set1_ps(2.017232))); __m256 zero = _mm256_set1_ps(0); __m256 max = _mm256_set1_ps(255); r = _mm256_max_ps(r, zero); r = _mm256_min_ps(r, max); g = _mm256_max_ps(g, zero); g = _mm256_min_ps(g, max); b = _mm256_max_ps(b, zero); b = _mm256_min_ps(b, max); data[cont++] = r; data[cont++] = g; data[cont++] = b; } } for (int num = 0; num < frame_num; num++) { int a = num * 3 + 1; cont = 0; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { __m256 r = data[cont++]; __m256 g = data[cont++]; __m256 b = data[cont++]; r = _mm256_mul_ps(r, _mm256_set1_ps(a / 256.0)); g = _mm256_mul_ps(g, _mm256_set1_ps(a / 256.0)); b = _mm256_mul_ps(b, _mm256_set1_ps(a / 256.0)); __m256 yi = _mm256_add_ps(_mm256_set1_ps(16), _mm256_add_ps(_mm256_mul_ps(r, _mm256_set1_ps(0.256788)), _mm256_add_ps(_mm256_mul_ps(g, _mm256_set1_ps(0.504129)), _mm256_mul_ps(b, _mm256_set1_ps(0.097906))))); __m256 ui = _mm256_add_ps(_mm256_set1_ps(128), _mm256_add_ps(_mm256_mul_ps(r, _mm256_set1_ps(-0.148223)), _mm256_add_ps(_mm256_mul_ps(g, _mm256_set1_ps(-0.290993)), _mm256_mul_ps(b, _mm256_set1_ps(0.439216))))); __m256 vi = _mm256_add_ps(_mm256_set1_ps(128), _mm256_add_ps(_mm256_mul_ps(r, _mm256_set1_ps(0.439216)), _mm256_add_ps(_mm256_mul_ps(g, _mm256_set1_ps(-0.367788)), _mm256_mul_ps(b, _mm256_set1_ps(-0.071427))))); yi = _mm256_cvtps_epi32(yi); ui = _mm256_cvtps_epi32(ui); vi = _mm256_cvtps_epi32(vi); ... } } } delete[] data; };运行结果使用ffplay播放生成文件，仅凭肉眼看不出四个指令集生成文件的差异。对四个指令集运行时间统计如下表：源文件BASICMMXSSE2AVXdem1.yuv1230ms1243ms648ms375msdem2.yuv1274ms1281ms635ms332ms从上表中可以看出，SSE2和AVX指令对图像处理速度有很大提升，而MMX指令集相对基础指令集并没有太大改进。下面对这个结果做一个原因分析：MMX使用整数运算，相对基础指令集并没有有效减少指令数。SSE2相对BASIC实现了2倍的性能提升，这是因为SSE2中采用SIMD将点积运算并行化了。AVX相对SSE2也有2倍性能提升，这是因为AVX中利用循环展开的方法，将原本的8次循环转化为1次循环，一次处理8个数据点，极大的实现了并行计算技术。设计自定义扩展指令对SIMD应用优化并分析需求设计若干32位宽的扩展指令，支持8个宽度为256位的SIMD指令专用寄存器，支持8/16/32位pack、unpack计算，支持加/减/乘法，支持饱和计算，支持必要的数据传输指令。自设计指令的编码、助记符，以及语义一共有8个SIMD寄存器，分别标号m0~m7，则需要3位标示一个SIMD寄存器。为尽可能与RISCV其他扩展指令集兼容，OPCODE域仍取7位。为了满足SIMD的扩展性，使用5位标示一个SIMD寄存器，则可扩展至32个SIMD寄存器，个数与x、f寄存器相同。编码助记符语义0000000 mrs2 mrs1 000 mrd 0000100addri8pmrs1和mrs2按packed 8位int相加放入mrd0000000 mrs2 mrs1 001 mrd 0000100addri8psmrs1和mrs2按packed 8位int饱和相加放入mrd0000001 mrs2 mrs1 000 mrd 0000100addri16pmrs1和mrs2按packed 16位int相加放入mrd0000001 mrs2 mrs1 001 mrd 0000100addri16psmrs1和mrs2按packed 16位int饱和相加放入mrd0000010 mrs2 mrs1 000 mrd 0000100addri32pmrs1和mrs2按packed 32位int相加放入mrd0000010 mrs2 mrs1 001 mrd 0000100addri32psmrs1和mrs2按packed 32位int饱和相加放入mrd0000011 mrs2 mrs1 000 mrd 0000100addri64pmrs1和mrs2按packed 64位int相加放入mrd0000011 mrs2 mrs1 001 mrd 0000100addri64psmrs1和mrs2按packed 64位int饱和相加放入mrd0000110 mrs2 mrs1 000 mrd 0000100addrf32pmrs1和mrs2按packed 32位float相加放入mrd0000111 mrs2 mrs1 000 mrd 0000100addrf64pmrs1和mrs2按packed 64位double相加放入mrd0001000 mrs2 mrs1 000 mrd 0000100subri8pmrs1和mrs2按packed 8位int相减放入mrd0001000 mrs2 mrs1 001 mrd 0000100subri8psmrs1和mrs2按packed 8位int饱和相减放入mrd0001001 mrs2 mrs1 000 mrd 0000100subri16pmrs1和mrs2按packed 16位int相减放入mrd0001001 mrs2 mrs1 001 mrd 0000100subri16psmrs1和mrs2按packed 16位int饱和相减放入mrd0001010 mrs2 mrs1 000 mrd 0000100subri32pmrs1和mrs2按packed 32位int相减放入mrd0001010 mrs2 mrs1 001 mrd 0000100subri32psmrs1和mrs2按packed 32位int饱和相减放入mrd0001011 mrs2 mrs1 000 mrd 0000100subri64pmrs1和mrs2按packed 64位int相减放入mrd0001011 mrs2 mrs1 001 mrd 0000100subri64psmrs1和mrs2按packed 64位int饱和相减放入mrd0001110 mrs2 mrs1 000 mrd 0000100subrf32pmrs1和mrs2按packed 32位float相减放入mrd0001111 mrs2 mrs1 000 mrd 0000100subrf64pmrs1和mrs2按packed 64位double相减放入mrd0010000 mrs2 mrs1 000 mrd 0000100mulri8pmrs1和mrs2按packed 8位int相乘放入mrd0010001 mrs2 mrs1 000 mrd 0000100mulri16pmrs1和mrs2按packed 16位int相乘放入mrd0010010 mrs2 mrs1 000 mrd 0000100mulri32pmrs1和mrs2按packed 32位int相乘放入mrd0010011 mrs2 mrs1 000 mrd 0000100mulri64pmrs1和mrs2按packed 64位int相乘放入mrd0010110 mrs2 mrs1 000 mrd 0000100mulrf32pmrs1和mrs2按packed 32位float相乘放入mrd0010111 mrs2 mrs1 000 mrd 0000100mulrf64pmrs1和mrs2按packed 64位double相乘放入mrd0100000 mrs2 mrs1 000 mrd 0000100maxri8pmrs1和mrs2按packed 8位int比较，大的放入mrd0100001 mrs2 mrs1 000 mrd 0000100maxri16pmrs1和mrs2按packed 16位int比较，大的放入mrd0100010 mrs2 mrs1 000 mrd 0000100maxri32pmrs1和mrs2按packed 32位int比较，大的放入mrd0100011 mrs2 mrs1 000 mrd 0000100maxri64pmrs1和mrs2按packed 64位int比较，大的放入mrd0100110 mrs2 mrs1 000 mrd 0000100maxrf32pmrs1和mrs2按packed 32位float比较，大的放入mrd0100111 mrs2 mrs1 000 mrd 0000100maxrf64pmrs1和mrs2按packed 64位double比较，大的放入mrd1000000 mrs2 mrs1 000 mrd 0000100minri8pmrs1和mrs2按packed 8位int比较，小的放入mrd1000001 mrs2 mrs1 000 mrd 0000100minri16pmrs1和mrs2按packed 16位int比较，小的放入mrd1000010 mrs2 mrs1 000 mrd 0000100minri32pmrs1和mrs2按packed 32位int比较，小的放入mrd1000011 mrs2 mrs1 000 mrd 0000100minri64pmrs1和mrs2按packed 64位int比较，小的放入mrd1000110 mrs2 mrs1 000 mrd 0000100minrf32pmrs1和mrs2按packed 32位float比较，小的放入mrd1000111 mrs2 mrs1 000 mrd 0000100minrf64pmrs1和mrs2按packed 64位double比较，小的放入mrdimm[11:0] xrs1 000 mrd 0001000addii8pxrs1和imm按packed 8位int相加复制多次放入mrdimm[11:0] xrs1 001 mrd 0001000addii16psxrs1和imm按packed 16位int相加复制多次放入mrdimm[11:0] xrs1 010 mrd 0001000addii32pxrs1和imm按packed 32位int相加复制多次放入mrdimm[11:0] xrs1 011 mrd 0001000addii64pxrs1和imm按packed 64位int相加复制多次放入mrdimm[11:0] frs1 110 mrd 0001000addif32pfrs1和imm按packed 32位float相加复制多次放入mrdimm[11:0] frs1 111 mrd 0001000addif64pfrs1和imm按packed 64位double相加复制多次放入mrd0000000 xrs2 mrs1 000 mrd 0100000sumi8按8位整型计算mrs1中的和，结果存入mrd的第xrs2处(mrd视为64位分组)0000001 xrs2 mrs1 000 mrd 0100000sumi16按16位整型计算mrs1中的和，结果存入mrd的第xrs2处(mrd视为64位分组)0000010 xrs2 mrs1 000 mrd 0100000sumi32按32位整型计算mrs1中的和，结果存入mrd的第xrs2处(mrd视为64位分组)0000011 xrs2 mrs1 000 mrd 0100000sumi64按64位整型计算mrs1中的和，结果存入mrd的第xrs2处(mrd视为64位分组)0000110 xrs2 mrs1 000 mrd 0100000sumf32按32位浮点计算mrs1中的和，结果存入mrd的第xrs2处(mrd视为64位分组)0000111 xrs2 mrs1 000 mrd 0100000sumf64按64位浮点计算mrs1中的和，结果存入mrd的第xrs2处(mrd视为64位分组)imm[11:0] xrs1 000 mrd 0010000lvxrs1和imm相加作为内存地址，从该地址读取32字节数据存入mrdimm[11:5] mrs2 xrs1 001 imm[4:0] 0010000sdxrs1和imm相加作为内存地址，从mrs2中向该地址写入32字节数据使用mySIMD重写核心程序基本上是改写AVX指令集的程序，m0~m8为8个SIMD寄存器void AVsXProcessor() { auto **data = new double *[size]; for (int i = 0; i < size; i++) { data[i] = new double[4]; } for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { auto yi0 = (uint8_t) y[i][j]; auto ui0 = (uint8_t) u[i / 2][j / 2]; auto vi0 = (uint8_t) v[i / 2][j / 2]; double tmp[4] = {yi0, ui0, vi0, 0}; m0 = lv tmp, 0 tmp = {16.0, 128.0, 128.0, 0.0}; m1 = lv tmp, 0 m0 = subrf64p m0, m1 tmp = {1.164383, 0.0, 1.596027, 0.0}; m1 = lv tmp, 0 m2 = mulrf64p m0, m1 tmp = {1.164383, -0.391762, -0.812968, 0.0}; m1 = lv tmp, 0 m3 = mulrf64p m0, m1 tmp = {1.164384, 2.017232, 0.0, 0.0}; m1 = lv tmp, 0 m4 = mulrf64p m0, m1 m5 = sumf64 m2, 0 m5 = sumf64 m3, 1 m5 = sumf64 m4, 2 tmp = {0, 0, 0, 0}; m1 = lv tmp, 0 m5 = maxrf64p m5, m1 tmp = {255, 255, 255, 255}; m1 = lv tmp, 0 m5 = minrf64p m5, m1 sv m5, data[i * width + j] } } 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++) { double tmp[4] = {a / 256.0, a / 256.0, a / 256.0, a / 256.0}; m0 = lv data[i * width + j], 0 m1 = lv tmp, 0 m0 = mulrf64p m0, m1 tmp = {0.256788, 0.504129, 0.097906, 0.0}; m1 = lv tmp, 0 m2 = mulrf64p m0, m1 tmp = {-0.148223, 0.290993, 0.439126, 0.0}; m1 = lv tmp, 0 m3 = mulrf64p m0, m1 tmp = {0.439126, -0.367788, -0.071427, 0.0}; m1 = lv tmp, 0 m4 = mulrf64p m0, m1 m5 = sumf64 m2, 0 m5 = sumf64 m3, 1 m5 = sumf64 m4, 2 tmp = {16, 128, 128, 0}; m1 = lv tmp, 0 m5 = addrf64p m5, m1 sv m5, tmp result[num][0][i * width + j] = tmp[0]; result[num][1][(i / 2) * (width / 2) + j / 2] = tmp[1]; result[num][2][(i / 2) * (width / 2) + j / 2] = tmp[2]; } } } for (int i = 0; i < size; i++) { delete[] data[i]; } delete[] data; };性能提升使用mySIMD指令集后，主程序共包含32条SIMD指令，由于每条指令处理4个数据，但有一个数据是无效的，所以相对于一个像素点，其减少的指令数为32*2=64条。如果进行优化，则每次循环只需要4个SIMD寄存器，那么由于图像处理出现数据冒险是小概率的，所以可以同时进行两个循环。综上所述，减少的指令数大约为64*1920*1080=1.3x10^8条，执行速度会是原来的三倍左右。
计算机组织与体系结构实习Lab3——高速缓存的模拟、配置和优化
高速缓存的模拟、配置和优化单级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 Number of JAL Instructions: 20734 Number of LOAD Instructions: 95400 Number of STORE Instructions: 68308 Number of BRANCH Instructions: 27451 Number of Successful Prediction: 13847 Number of Unsuccessful Prediction: 13604 Branch Prediction Accuracy: 0.5044 (Using Strategy: Always Not Taken) Number of Control Hazards: 61860 Number of Data Hazards: 307250 Number of Structural Hazards: 27192 -----------------------------------从中可以看出，运行结果正确。CPI对比下表给出带Cache和不带Cache的程序运行CPI文件名CPI of IdealMemoryCPI with CacheCPI withoutCachehelloworld1.21687.552427.0909quicksort1.34271.275349.2958matrixmulti1.22541.232039.0974ackermann1.20601.229539.2194在没有Cache的模拟器中，所有访存周期都为1个周期，当引入Cache后，CPI均有所增加，这是因为引入Cache后，我们访存操作都需要不止一个周期。从表中可以看出，不同程序其CPI差异很大，局部性好的程序CPI增加幅度小，如矩阵乘法，其数据大多在一个block内，引入Cache带来的性能损失非常小。当我们去掉Cache并设置访存周期为100时，我们发现引入Cache基本上都能将CPI下降很多，这说明减小访存延迟，合理设计Cache规模对计算机性能的影响是巨大的。高速缓存管理策略优化采用两级Cache，默认配置如下LevelCapacityAssociativityLine size(Bytes)Write PolicyBus LatencyHit LatencyL132KB8 ways64Write Back0 CPU Cycle?L2256KB8 ways64Write Back6 CPU Cycle?Mem$\infty$ 0100 CPU Cycle其中Bus Latency为总线延迟，即本层接到上层发来请求和本层返回数据给上层时间延迟之和，Hit Latency为本层查找数据所需时间，只要没使用bypass该延时始终存在。假设CPU和主存频率为2GHz，主存无限大。计算Hit Latency安装cacti依赖库sudo apt install gcc-multilib g++-multilib修改cacti配置文件-size (bytes) ??? -block size (bytes) ? -associativity ? -technology (u) 0.032 -cache type "cache"得到运行结果从图中可以看出，默认配置下，32nm工艺节点下，L1 Cache的 Hit Latency 为（1.47944）ns，约等于（3）cycle - 默认配置下，32nm工艺节点下，L2 Cache的 Hit Latency 为（1.9206）ns，约等于（4）cycle故，默认配置下，Cache的全部参数如下表LevelCapacityAssociativityLine size(Bytes)Write PolicyBus LatencyHit LatencyL132KB8 ways64Write Back0 CPU Cycle3 CPU CycleL2256KB8 ways64Write Back6 CPU Cycle4 CPU CycleMem$\infty$ 0100 CPU Cycle默认配置运行trace2017使用LRU替换算法，无任何优化算法，使用命令./CacheYouhuaSim ../cache-trace/???.trace 100运行提供的两个trace文件共一百次。TraceLevelLatencyMiss RateAMAT01-mcf-gem5-xcg.traceL130.20045511.548831 L2100.396080 Mem100x 02-stream-gem5-xaa.traceL130.11340411.501351 L2100.755171 Mem100x 其中AMAT计算公式为$$ AMAT1 = (1-0.200455)*3+0.200455*[(1-0.396080)*10+0.396080*100]\\ AMAT2 = (1-0.113404)*3+0.113404*[(1-0.755171)*10+0.755171*100] $$优化策略替换策略默认策略是LRU策略，考虑使用随机算法、PLRU算法替换，测试100轮。TraceLevelMiss RateLRUMiss RateRandomMiss RatePLRUAMATLRUAMATRandomAMATPLRU01-mcf-gem5-xcg.traceL10.2004550.2043370.20058811.54883111.01770911.399391 L20.3960800.3581960.387487 02-stream-gem5-xaa.traceL10.1134040.1134830.11340411.50135111.65174211.501351 L20.7551710.7693130.755171 从上表中可以看出，Random算法和PLRU算法与LRU算法的性能差不多。但由于Random算法具有随机性和不稳定性，所以Random算法不能作为好的替代算法。 虽然PLRU算法比LRU算法提高幅度较小，但由于其为稳定的算法，所以PLRU是好的替代算法。再，LRU的时间复杂度为O(associativity)，而PLRU为O(log(associativity))，可见PLRU速度也要比LRU快，虽然一般来说associativity都不大。所以，采用PLRU算法替代LRU算法是合理的。Bypass策略我们能想象到，在程序执行过程中，可能仅一次执行某些指令，且在这些指令周围的其他指令也不被执行，那么对于这类指令，如果我们需要其的时候，将其更新至Cache，这就发生了一次很长的访问，首先从主存中加载到L2 Cache，再从L2 Cache加载到L1 Cache然后返回。这样的数据装入Cache，只使用一次，使得Cache的表现变得糟糕。因此，对于这种情况，不应该加载到Cache中，应该直接从下层存储中返回该数据，即Bypass.使用MCT决定Bypass，MCT为一个记录历史载入Line的表格，其一行对应一个associativity，每行中可以有若干个Tag，遵循FIFO原则。对于上述提到的现象，我们只需考虑由于CacheSize引起的Capacity Miss即可，即在Cache Set和MCT均已满且在MCT中找不到相应的Tag时，进行Bypass防止Cache污染。采用默认配置，使用PLRU算法，分别在L1和L2上开启Bypass策略且每个MCT最多8个Tag，测试100轮。TraceLevelMRNoMRL1MRL2MRL1+L2AMATNoAMATL1AMATL2AMATL1+L2mcf.traceL10.2005880.2463380.2005880.24633811.39939112.5937099.77021913.380823 L20.3874870.3549470.3675180.390450 xaa.traceL10.1134040.2236580.1134040.22365811.50135113.33124512.27531720.152812 L20.7551710.4354680.8310030.774357 从上表中可以看到，对于局部性好的trace，当开启L2 Bypass时AMAT有较好的优化性能，而开启L1 Bypass均会降低性能。对于流水式的trace，局部性很差，这时关闭Bypass可以降低Miss Rate。这一试验结果不难理解，当局部性好时，偶尔一个远距离的内存访问，Bypass可以防止Cache被污染以获得较好的性能。所以，Bypass优化策略为仅对L2开启。然后，通过实验选择不同的MCT Size，每组测试10次。TraceMCT SizeMR L1MR L2AMATmcf.trace10.2005880.33112810.381955 20.2005880.32243910.225081 40.2005880.32421210.257096 80.2005880.36751811.038902 160.2005880.38660011.383383 320.2005880.38762911.383383 640.2005880.38748711.401952 1280.2005880.38748711.399391 2560.2005880.38748711.399391xaa.trace10.1134040.83100312.275317 20.1134040.83100312.275317 40.1134040.83100312.275317 80.1134040.83100312.275317 160.1134040.83100312.275317 320.1134040.77634711.463847 640.1134040.75517111.501351 1280.1134040.75517111.501351 2560.1134040.75517111.501351上表中 mcf.trace代表局部性良好的trace，xaa.trace代表局部性不好的trace，权衡考虑，取Size为2。由于绝大多数程序都是局部性较好的程序，此时若MCT Size为零，则会出现Cache污染，若MCT Size较大，所有出现过的都会被记录下来不再bypass，也会造成性能的降低。综上所述，L2缓存使用Bypass策略，且MCT Size（即历史Tag）为2.Prefetch策略由于程序的局部性，如果每次miss时不止从下一级存储中取出本次Miss的Cache Line，而是同时取出连续的若干个Line防止之后出现的Miss，则可以提高性能。采用 MCT Next-line Prefetch 策略，即使用Bypass的MCT判断预取的Line是否有用。并设置一参数num表示预取的Line的数量，上限设置为4。采用默认配置，使用PLRU算法，在L2上开启Bypass策略且每个MCT最多2个Tag，在L1和L2上均开启Prefetch，测试10轮。TracePrefetch NumMR L1MR L2AMATmcf.trace00.2005880.32243910.225081 10.1796600.3238749.494491 20.1735730.3232329.264405 40.1711140.3239609.186874xaa.trace00.1134040.83100312.275317 10.0567230.7132237.038136 20.0390000.6749495.642094 40.0230700.6403044.490949从上表中可以看出，使用Prefetch可以有效提高性能。所以，启用Prefetch策略，设置预取数量为4，并通过MCT判断是否加载。优化结果将之前所得的结论总结如下L1 Cache：32KB，8路，Line=64B，WA+WB写策略，PLRU替换策略，预取数量为4的预取策略，无BypassL2 Cache：256KB，8路，Line=64B，WA+WB写策略，PLRU替换策略，预取数量为4的预取策略，MCT Size为2的Bypass策略在上述配置条件下，运行trace 100遍。TraceLevel无优化Miss Rate无优化AMAT优化Miss Rate优化AMAT01-mcf-gem5-xcg.traceL10.20045511.5488310.1711149.186874 L20.396080 0.323960 Memx 02-stream-gem5-xaa.traceL10.11340411.5013510.0230704.490949 L20.755171 0.640304 Memx 从上表中可以看出，在使用上述优化策略后，程序的性能得到了很大改善。
计算机组织与体系结构实习Lab 2——RISC-V CPU模拟器设计与实现报告
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 /path/to/project mkdir build && cd "$_" cmake .. make之后会得到可执行程序Sim，是一个命令行程序，相关参数列出如下rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim Usage: ./Sim riscv-elf-file-name [-v] [-d] [-s] [-b para] Parameters: [-v]: to use verbose mode [-d]: to dump memory and reg [-s]: to enter single step mode [-b para]: claim branch perdiction strategy accpeted para: AT, NT, BTFNT, BPB AT: Always Taken NT: Always Not Taken BTFNT: Backword Taken, Forward Not Taken BPB: Branch Prediction Buffer其中riscv-elf-file-name是riscv elf可执行文件的路径，一个典型的运行流程和输出如下：rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/others/matrixmulti.riscv The content of A is: 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 The content of B is: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 The content of C=A*B is: 0 0 0 0 0 0 0 0 0 0 0 10 20 30 40 50 60 70 80 90 0 20 40 60 80 100 120 140 160 180 0 30 60 90 120 150 180 210 240 270 0 40 80 120 160 200 240 280 320 360 0 50 100 150 200 250 300 350 400 450 0 60 120 180 240 300 360 420 480 540 0 70 140 210 280 350 420 490 560 630 0 80 160 240 320 400 480 560 640 720 0 90 180 270 360 450 540 630 720 810 Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 105024 Number of Instructions: 72502 Avg Cycles per Instrcution: 1.4486 Number of JALR Instructions: 643 Number of JAL Instructions: 1106 Number of LOAD Instructions: 22984 Number of STORE Instructions: 4474 Number of BRANCH Instructions: 1722 Number of Successful Prediction: 163 Number of Unsuccessful Prediction: 1559 Branch Prediction Accuracy: 0.0947 (Using Strategy: Always Not Taken) Number of Control Hazards: 3160 Number of Data Hazards: 77031 Number of Structural Hazards: 4334 -----------------------------------在默认情况下，模拟器会先运行程序，处理程序的输入和输出，当该程序运行结束后，模拟器会打印本次运行过程中的统计数据。程序还支持单步调试模式，和啰嗦模式，分别是-s和-s参数。在单步模式中，可以输入d来保存内存快照，使用其他案件前进到下一条指令。-b参数可以指定用于分支预测的策略。本项目提供了一个自动化脚本build.sh，用以编译测试代码（需要自行修改相关脚本中的riscv64-unknown-elf-gcc程序的位置）。但无需这样做，因为提交版本中已经保留了相关编译后的riscv可执行文件，并且如果采用非--with-arch=rv64im选项生成的编译器来生成riscv可执行文件，本程序可能无法正常处理C类压缩指令。三、具体实现3.1 代码架构模拟器总体为一个类Simulator，其中包含了通用寄存器、流水线寄存器、历史记录、内存模块和分支预测模块。其中，内存模块和分支预测模块被单独拿出来构成了独立的类MemoryManager和BranchPredictor。3.2 可执行文件的装载、初始化本程序使用了开源库ELFIO，使用库函数使得加载ELF文件的操作非常简单，相关代码如下：void loadElf(ELFIO::elfio *reader, MemoryManager *memory) { ELFIO::Elf_Half seg_num = reader->segments.size(); if (verbose) { printf("-------load ELF-------\n"); } for (uint32_t i = 0; i < seg_num; i++) { const ELFIO::segment *seg_pointer = reader->segments[i]; uint64_t full_mem = seg_pointer->get_memory_size(); uint64_t full_addr = seg_pointer->get_virtual_address(); if (full_addr + full_mem > 0xFFFFFFFF) { printf("ELF address space is larger than 32bit!\n"); printf("Still could not deal with it!\n"); exit(-1); } uint32_t file_size = (uint32_t) seg_pointer->get_file_size(); uint32_t mem_size = (uint32_t) full_mem; uint32_t addr = (uint32_t) full_addr; for (uint32_t pos = addr; pos < addr + mem_size; pos++) { if (!memory->pageExist(pos)) { memory->addPage(pos); } if (pos < addr + file_size) { memory->setByte(pos, seg_pointer->get_data()[pos - addr]); } else { memory->setByte(pos, 0); } } } if (verbose) { printf("-----------------------\n"); } }并且在进入模拟时使用库函数传递程序入口给模拟器simulator.run(reader.get_entry());一坑：可执行文件内的数据长度可能小于指定的内存长度，需要用0填充，否则会导致奇怪的bug3.3 内存模块MemoryManager内存模块应该足够健壮，应该有足够的空间，并可以处理非法操作，这与操作系统中的虚拟内存很像，于是仿照x86架构设计了二级页表的机制，具体而言，将32位的内存空间在逻辑上划分为4kB的页，每一页有$2^{12}$个Byte，并且采用前10位作为一级页表的索引，第11到20位作为二级页表的索引，最后12位作为页内的偏移。一注：也就是说本程序最大只支持32位内存，一般的用户程序如果不发生内存泄漏，32位内存是完全够用的，毕竟我给虚拟机也只分配了1G内存，\_(:з」∠)\_模拟器对memory的一个访存过程如下：uint8_t MemoryManager::getByte(uint32_t addr){ if(!this->addrExist(addr)){ printf("Memory read from invalid addr 0x%x!\n", addr); exit(-1); return false; } uint16_t i = this->getFirstIndex(addr); uint16_t j = this->getSecondIndex(addr); uint16_t k = this->getPageOffset(addr); return this->memory[i][j][k]; }3.4 分支预测模块BranchPrediction本模拟器实现了如下几种策略策略名称说明ATAlways TakenNTAlways Not TakenBTFNTBack Taken Forward Not TakenBPBBranch Prediction Buffer其中，BPB模式采用内存后12位维护一个长度为$2^{12}=4096$的直接映射高速缓存，用于存储该指令的状态。本模拟器中的缓存状态有四种，他们之间的转换关系如下图：具体实现如下：bool BranchPredictor::predict(uint32_t pc, int64_t offset){ switch(this->strategy){ case AT: return true; case NT: return false; case BTFNT: return offset<0; default: //assert(this->strategy == BPB); PredictorState state = this->predbuf[pc & 0xFFFu]; if(state == STRONG_TAKEN || state == WEAK_TAKEN){ return true; } else if(state == WEAK_NOT_TAKEN || state == STRONG_NOT_TAKEN){ return false; } break; } return false; } void BranchPredictor::update(uint32_t pc, bool branch){ if(strategy == BPB){ int index = (int)(pc & 0xFFFu); PredictorState state = this->predbuf[index]; if(branch){ if (state == STRONG_TAKEN){ return; } else if(state == WEAK_TAKEN){ this->predbuf[index] = STRONG_TAKEN; return; } else if(state == WEAK_NOT_TAKEN){ this->predbuf[index] = WEAK_TAKEN; return; } else{ //assert(state == STRONG_NOT_TAKEN); this->predbuf[index] = WEAK_NOT_TAKEN; return; } } else{ if(state == STRONG_TAKEN){ this->predbuf[index] = WEAK_TAKEN; return; } else if(state == WEAK_TAKEN){ this->predbuf[index] = WEAK_NOT_TAKEN; return; } else if(state == WEAK_NOT_TAKEN){ this->predbuf[index] = STRONG_NOT_TAKEN; return; } else{ //assert(state==STRONG_NOT_TAKEN); return; } } } }并且在IF,EX阶段和run()函数主体添加相关代码，用以关联prediction代码。3.5 模拟器Simulator本模拟器中含有通用寄存器、流水线寄存器、旁路信号、历史记录等。3.5.1 run()函数模拟器中核心功能是run()函数，其接受一个参数pc，该参数为程序的入口地址，之后将该地址传递给取址寄存器，之后便进入无限循环中，每一个周期依次执行writeBack(); // WB memoryAccess(); // MEM execute(); // EX decode(); // ID fetch(); // IF五个函数，每个函数都会以上一周期的流水线寄存器为输入，并输出到下一周期的流水线寄存器中。上述已经指出，本模拟器没有实现并行执行五个阶段，所以这5个函数的执行过程应该被小心的设计，由于存在数据冒险和结构冒险，我们如果采用正常的顺序，会导致更多的时钟周期被浪费掉，于是采用倒序，这样如果在ID阶段需要读取WB阶段写回的值，即发生结构冒险无需特殊处理，当ID阶段需要EX阶段的计算值时，也无需等待到下一周期再进行数据转发，直接可以从旁路寄存器中读取相关数值。在run()函数中需要处理的特殊情况如下表：特殊情况条件处理方法JALRd_reg.opcode=OP_JALR流水线停顿，等待译码完成stall/bubble/normal/normal/normalLOAD冒险e_reg.opcode=OP_LOADe_reg.rd in {d_reg.rs1, d_reg.rs2}流水线停顿，等待访存完成stall/stall/bubble/normal/normal执行周期e_reg.stall_count != 0流水线停顿，等待计算完成stall/stall/stall/bubble/normal控制冒险出错e_reg.opcode=OP_BRANCHe_valE!=predict(e_reg.pc, e_reg.imm)取消下两条指令stall/bubble/bubble/normal/normal3.5.2 writeBack()函数写回阶段从w_reg寄存器中读取值，判断指令，若为LOAD指令，则将从内存中读出的valM写入到rd寄存器中，对于其他指令，均将valE写入到rd寄存器中。所以对于那些没有指定rd的指令应该将rd设置为0号寄存器，并在writeBack()函数的最后，判断0寄存器是否仍为0，若非零，则应该重置为0。switch (opcode) { case OP_RR: case OP_32: case OP_IMM: case OP_IMM_32: case OP_LUI: case OP_AUIPC: case OP_ECALL: case OP_JAL: case OP_JALR: case OP_STORE: //rd=0 case OP_BRANCH: //rd=0 reg[rd] = valE; break; case OP_LOAD: reg[rd] = valM; break; case OP_BUBBLE: break; default: this->raiseError("Invaild Instruction When Do Writing Back\n"); break; } if (reg[0]) { reg[0] = 0; }3.5.3 memoryAccess()函数访存阶段从m_reg寄存器中读取值，判断指令为LOAD则从地址为valE的读取数据并传递给valM，若指令为STORE则向valE的地址写入val2的值，其它指令不做处理。switch (opcode) { case OP_LOAD: { history.load_count++; switch (func) { case 0x0: valM = (int64_t) memory->getByte(valE); break; case 0x1: valM = (int64_t) memory->getShort(valE); break; case 0x2: valM = (int64_t) memory->getInt(valE); break; case 0x3: valM = (int64_t) memory->getLong(valE); break; case 0x4: valM = (uint64_t) memory->getByte(valE); break; case 0x5: valM = (uint64_t) memory->getInt(valE); break; case 0x6: valE = (uint64_t) memory->getInt(valE); break; default: this->raiseError("Invaild LOAD Instruction at %x, The Func Field %x is invaild\n", pc, func); break; } break; } case OP_STORE: history.store_count++; switch (func) { case 0x0: memory->setByte(valE, val2); break; case 0x1: memory->setShort(valE, val2); break; case 0x2: memory->setInt(valE, val2); break; case 0x3: memory->setLong(valE, val2); break; default: this->raiseError("Invaild READ Instruction at %x, The Func Field %x is invaild\n", pc, func); break; } break; case OP_RR: case OP_32: case OP_IMM: case OP_IMM_32: case OP_BRANCH: case OP_JAL: case OP_JALR: case OP_LUI: case OP_AUIPC: case OP_ECALL: case OP_BUBBLE: break; default: this->raiseError("Invaild Instruction at %x\n", pc); break; }并且，在函数结尾，将valM传递给旁路信号bypass.m_valM，提供给ID阶段。3.5.4 execute()函数执行阶段从e_reg读取数据，判断指令类型，并进行相应计算。并在函数结尾，将计算结果valE传递给旁路信号bypass.e_valE，提供给IF阶段判断分支预测结果，并且提供给ID阶段解决数据冒险。本模拟器实现了RV64IM指令集，所以引入了指令执行周期，默认设置为int const CALU_BIT = 1; int const CALU_ADD = 1; int const CALU_MUL = 4; int const CALU_MUL_32 = 3; int const CALU_DIV = 30; int const CALU_DIV_32 = 25;以上参数可在source-code/Simulator.hpp文件中进行更改。当执行阶段的指令的执行周期不为1时，将会导致流水线暂停，并将剩余执行周期减一，直至执行周期为1时，流水线继续运行。void Simulator::execute() { ... if (stall_count >= 1) { stall_count -= 1; } e_reg.out.stall_count = stall_count; if (stall_count != 0){ return; } ... } void Simulator::decode() { if (e_reg.out.stall_count != 0) return; ... } void Simulator::fetch() { if (e_reg.out.stall_count != 0) return; ... } void Simulator::run(uint64_t pc) { ... while (true) { ... if (e_reg.out.stall_count == 0) { ... } else { e_reg.stall = true; d_reg.stall = true; f_reg.stall = true; m_reg.bubble = true; } ... } }3.5.5 decode()函数译码阶段，首先从d_reg中读值，然后先从rs1,rs2寄存器读值，然后再对rs1和rs2依次判断。首先是执行阶段的rd，若rd==rs?则将对应的val?值设置为执行阶段传递给旁路信号的值bypass.valE，然后退出判断， 否则进入2若1不满足，则判断访存阶段rd与rs?是否相同，不同则进行3，若相同，若访存为LOAD指令，则将访存阶段写入的旁路信号bypass.m_valM赋值给val?，若为其他指令，则将m_reg寄存器的valE赋值给val?。若写回阶段rd与rs?相同，此时为结构冒险，但无需改动val?，因为写回阶段是在译码阶段之前完成的，相关数据已经写入到寄存器中了。例如rs1部分代码如下：val1 = reg[rs1]; if (rs1 != 0) { if (rs1 == e_reg.out.rd && e_reg.out.opcode) { // 由于非并行，故可以传递e_valE否则应stall一周期 history.data_hazard_count++; val1 = bypass.e_valE; } else if (rs1 == m_reg.out.rd && m_reg.out.opcode) { history.data_hazard_count++; if (is_read_memory(m_reg.out.inst)) { // 由于非并行，故可以传递m_valM否则应stall一周期 val1 = bypass.m_valM; } else { val1 = m_reg.out.valE; } } else if (w_reg.out.rd == rs1) { history.structural_hazard_count++; } }留意到在判断与执行阶段的过程中，我们没有对执行阶段的指令做特殊判断。这是因为若为STORE指令，虽然e_valE并不是对应的值，但由于STORE指令rd等于0，而rs?是不可能为0的，所以不可能出现这种情况。若为LOAD指令， e_valE仍不是对应的值，但在run()函数中，会做特殊判断，并将本次译码结果设置为无效。3.5.6 fetch()函数由于RV64IM指令集都为64位长，所以实现起来很简单。（若不采用--with-arch=rv64im参数则无法实现）取址阶段基本上是对RISCV指令集的直接翻译，值得一提的是，在取址时，会首先判断两类特殊情况：若执行阶段指令为JALR，则pc应该为JALR这条指令的地址再加该指令的立即数，并把最低位置0若访存阶段指令为分支指令，则需要判断当时对该指令取址时的分支预测策略是否正确，若正确则直接取址，若不正确，则需要对pc进行修正。上面的两条可以更换顺序，因为若执行为JALR且访存为分支，经过run()函数中的特殊判定后可知该分支指令的分支预测是正确的，(2)不会改变pc，只有JALR会改变pc访存为分支，且预测错误，则执行阶段为BUBBLE (1)不会改变pc访存不为分支，只需要判断JALR即可if (e_reg.out.opcode == OP_JALR) { pc = (uint64_t) (e_reg.out.val1 + e_reg.out.imm) & ~1LLu; } if (m_reg.out.opcode == OP_BRANCH) { history.branch_count++; bool b = branch_predictor->predict(m_reg.out.pc, m_reg.out.imm); branch_predictor->update(m_reg.out.pc, m_reg.out.valE); if (b ^ m_reg.out.valE) { pc = m_reg.out.pc + (b ? 4 : m_reg.out.imm); history.predicted_unsuccess++; } else { history.predicted_success++; } }一坑：取址时有可能取出全0指令，该指令非法，可以将opcode记为BUBBLE3.5.7 handleSystemCall()系统调用和库函数接口的处理前文已经提到本模拟器采用自定义的系统调用，系统调用ecall指令会使用a0和a7寄存器，其中a7为系统调用号，a0为系统调用参数，返回值保存在a0中。所有系统调用的含义见下表：系统调用名称系统调用号参数返回值程序结束0无无打印数字1数字的值无打印字符2字符的值无打印字符串3字符串起始地址无读入数字4无读入的数字读入字符5无读入的字符在为本模拟器专门编写的程序中，涉及输入输出均应调用如下函数void print_d(int num) { asm("li a7, 1;" "scall"); } void print_s(const char *str) { asm("li a7, 3;" "scall"); } void print_c(char ch) { asm("li a7, 2;" "scall"); } void exit_proc() { asm("li a7, 0;" "scall"); } char read_char() { char result; asm("li a7, 5;" "scall"); asm("addi %0, a0, 0" : "=r" (result)); return result; } long long read_num() { long long result; asm("li a7, 4;" "scall"); asm("addi %0, a0, 0" : "=r" (result)); return result; }3.5.8 计数模块本模拟器可以实现性能模拟，并实现性能计数。数据统计模块定义如下：struct History { uint32_t inst_count; uint32_t cycle_count; uint32_t branch_count; uint32_t jalr_count; uint32_t jal_count; uint32_t load_count; uint32_t store_count; uint32_t predicted_success; uint32_t predicted_unsuccess; uint32_t data_hazard_count; uint32_t control_hazard_count; uint32_t structural_hazard_count; std::vector<std::string> inst_record; std::vector<std::string> reg_record; } history;3.5.9 调试接口在模拟器运行过程中，可能出现各种非法操作，所以设计了raiseError()函数，其会打印错误原因，并输出内存快照，并退出程序。当给予-v -s参数时，会分别进入啰嗦模式和单步调试模式。四、功能测试和性能评测4.1 模拟器的功能正确性测试我自己使用的测试程序如下表（部分来源于网络）：代码文件对应的ELF文件功能描述test/helloworld.criscv-elf/helloworld.riscv最简单的Hello, Worldtest/test_arithmetic.criscv-elf/test_arithmetic.riscv测试一组算术运算test/test_syscall.criscv-elf/test_syscall.riscv测试全部的系统调用test/test_branch.criscv-elf/test_branch.riscv测试条件和循环语句test/quicksort.criscv-elf/quicksort.riscv分别对10和100个元素进行快速排序test/matrixmulti.criscv-elf/matrixmulti.riscv10*10矩阵乘法test/ackermann.criscv-elf/ackermann.riscv求解一组Ackermann函数的值利用runme.sh得到结果或手动输入运行命令如：mkdir build && cd build cmake .. make ./Sim ../riscv-elf/others/helloworld.riscv ./Sim ../riscv-elf/others/test_arithmetic.riscv ./Sim ../riscv-elf/others/test_syscall.riscv ./Sim ../riscv-elf/others/test_branch.riscv ./Sim ../riscv-elf/others/quicksort.riscv ./Sim ../riscv-elf/others/matrixmulti.riscv ./Sim ../riscv-elf/others/ackermann.riscv得到的结果如下：rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/others/helloworld.riscv Hello, World! Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 174 Number of Instructions: 143 Avg Cycles per Instrcution: 1.2168 Number of JALR Instructions: 10 Number of JAL Instructions: 8 Number of LOAD Instructions: 11 Number of STORE Instructions: 26 Number of BRANCH Instructions: 17 Number of Successful Prediction: 8 Number of Unsuccessful Prediction: 9 Branch Prediction Accuracy: 0.4706 (Using Strategy: Always Not Taken) Number of Control Hazards: 34 Number of Data Hazards: 61 Number of Structural Hazards: 9 ----------------------------------- rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/others/test_arithmetic.riscv 30 -10 370350 411 49380 771 Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 416 Number of Instructions: 335 Avg Cycles per Instrcution: 1.2418 Number of JALR Instructions: 21 Number of JAL Instructions: 26 Number of LOAD Instructions: 39 Number of STORE Instructions: 60 Number of BRANCH Instructions: 18 Number of Successful Prediction: 9 Number of Unsuccessful Prediction: 9 Branch Prediction Accuracy: 0.5000 (Using Strategy: Always Not Taken) Number of Control Hazards: 59 Number of Data Hazards: 172 Number of Structural Hazards: 15 ----------------------------------- rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/others/test_syscall.riscv This is string from print_s() 123456abc Enter a number: 159753 The number is: 159753 Enter a character: & The character is: & Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 395 Number of Instructions: 345 Avg Cycles per Instrcution: 1.1449 Number of JALR Instructions: 25 Number of JAL Instructions: 25 Number of LOAD Instructions: 30 Number of STORE Instructions: 58 Number of BRANCH Instructions: 17 Number of Successful Prediction: 8 Number of Unsuccessful Prediction: 9 Branch Prediction Accuracy: 0.4706 (Using Strategy: Always Not Taken) Number of Control Hazards: 64 Number of Data Hazards: 151 Number of Structural Hazards: 9 ----------------------------------- rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/others/test_branch.riscv Yes, f2 is true a[5] = 1 2 3 4 5 a[5] = 1 12 123 1234 12345 Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 896 Number of Instructions: 739 Avg Cycles per Instrcution: 1.2124 Number of JALR Instructions: 34 Number of JAL Instructions: 55 Number of LOAD Instructions: 111 Number of STORE Instructions: 100 Number of BRANCH Instructions: 36 Number of Successful Prediction: 12 Number of Unsuccessful Prediction: 24 Branch Prediction Accuracy: 0.3333 (Using Strategy: Always Not Taken) Number of Control Hazards: 104 Number of Data Hazards: 554 Number of Structural Hazards: 14 ----------------------------------- rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/others/quicksort.riscv Prev A: 5 3 5 6 7 1 3 5 6 1 Sorted A: 1 1 3 3 5 5 5 6 6 7 Prev B: 100 99 98 97 96 95 94 93 92 91 90 89 88 87 86 85 84 83 82 81 80 79 78 77 76 75 74 73 72 71 70 69 68 67 66 65 64 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 Sorted B: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 138899 Number of Instructions: 103451 Avg Cycles per Instrcution: 1.3427 Number of JALR Instructions: 679 Number of JAL Instructions: 3743 Number of LOAD Instructions: 41615 Number of STORE Instructions: 7993 Number of BRANCH Instructions: 11205 Number of Successful Prediction: 5518 Number of Unsuccessful Prediction: 5687 Branch Prediction Accuracy: 0.4925 (Using Strategy: Always Not Taken) Number of Control Hazards: 12835 Number of Data Hazards: 121081 Number of Structural Hazards: 6422 ----------------------------------- rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/others/matrixmulti.riscv The content of A is: 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9 9 9 The content of B is: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 The content of C=A*B is: 0 0 0 0 0 0 0 0 0 0 0 10 20 30 40 50 60 70 80 90 0 20 40 60 80 100 120 140 160 180 0 30 60 90 120 150 180 210 240 270 0 40 80 120 160 200 240 280 320 360 0 50 100 150 200 250 300 350 400 450 0 60 120 180 240 300 360 420 480 540 0 70 140 210 280 350 420 490 560 630 0 80 160 240 320 400 480 560 640 720 0 90 180 270 360 450 540 630 720 810 Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 105024 Number of Instructions: 72502 Avg Cycles per Instrcution: 1.4486 Number of JALR Instructions: 643 Number of JAL Instructions: 1106 Number of LOAD Instructions: 22984 Number of STORE Instructions: 4474 Number of BRANCH Instructions: 1722 Number of Successful Prediction: 163 Number of Unsuccessful Prediction: 1559 Branch Prediction Accuracy: 0.0947 (Using Strategy: Always Not Taken) Number of Control Hazards: 3160 Number of Data Hazards: 77031 Number of Structural Hazards: 4334 ----------------------------------- rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/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: 519383 Number of Instructions: 430659 Avg Cycles per Instrcution: 1.2060 Number of JALR Instructions: 13740 Number of JAL Instructions: 20734 Number of LOAD Instructions: 95400 Number of STORE Instructions: 68308 Number of BRANCH Instructions: 27451 Number of Successful Prediction: 13847 Number of Unsuccessful Prediction: 13604 Branch Prediction Accuracy: 0.5044 (Using Strategy: Always Not Taken) Number of Control Hazards: 61860 Number of Data Hazards: 307250 Number of Structural Hazards: 27192 -----------------------------------4.2 运行给定的5个测试程序本次测试程序共有6个，其中mul-div.c需要M扩展集，double-float.c需要D F扩展集，由于我只实现了M扩展集，所以double-float.c程序无法测试。4.2.1 运行结果给定的5个程序在test-inclass/中，类似的可以得到：rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/rv64im/inclass/add.riscv Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 595 Number of Instructions: 472 Avg Cycles per Instrcution: 1.2606 Number of JALR Instructions: 16 Number of JAL Instructions: 13 Number of LOAD Instructions: 91 Number of STORE Instructions: 64 Number of BRANCH Instructions: 44 Number of Successful Prediction: 20 Number of Unsuccessful Prediction: 24 Branch Prediction Accuracy: 0.4545 (Using Strategy: Always Not Taken) Number of Control Hazards: 72 Number of Data Hazards: 353 Number of Structural Hazards: 23 ----------------------------------- rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/rv64im/inclass/mul-div.riscv Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 612 Number of Instructions: 490 Avg Cycles per Instrcution: 1.2490 Number of JALR Instructions: 16 Number of JAL Instructions: 12 Number of LOAD Instructions: 91 Number of STORE Instructions: 64 Number of BRANCH Instructions: 43 Number of Successful Prediction: 19 Number of Unsuccessful Prediction: 24 Branch Prediction Accuracy: 0.4419 (Using Strategy: Always Not Taken) Number of Control Hazards: 69 Number of Data Hazards: 377 Number of Structural Hazards: 32 ----------------------------------- rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/rv64im/inclass/n\!.riscv Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 638 Number of Instructions: 510 Avg Cycles per Instrcution: 1.2510 Number of JALR Instructions: 28 Number of JAL Instructions: 31 Number of LOAD Instructions: 89 Number of STORE Instructions: 81 Number of BRANCH Instructions: 42 Number of Successful Prediction: 18 Number of Unsuccessful Prediction: 24 Branch Prediction Accuracy: 0.4286 (Using Strategy: Always Not Taken) Number of Control Hazards: 90 Number of Data Hazards: 307 Number of Structural Hazards: 64 ----------------------------------- rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/rv64im/inclass/qsort.riscv Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 23222 Number of Instructions: 17952 Avg Cycles per Instrcution: 1.2936 Number of JALR Instructions: 93 Number of JAL Instructions: 247 Number of LOAD Instructions: 6293 Number of STORE Instructions: 1587 Number of BRANCH Instructions: 1878 Number of Successful Prediction: 877 Number of Unsuccessful Prediction: 1001 Branch Prediction Accuracy: 0.4670 (Using Strategy: Always Not Taken) Number of Control Hazards: 2177 Number of Data Hazards: 18121 Number of Structural Hazards: 340 ----------------------------------- rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/rv64im/inclass/simple-function.riscv Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 607 Number of Instructions: 483 Avg Cycles per Instrcution: 1.2567 Number of JALR Instructions: 17 Number of JAL Instructions: 14 Number of LOAD Instructions: 93 Number of STORE Instructions: 66 Number of BRANCH Instructions: 44 Number of Successful Prediction: 20 Number of Unsuccessful Prediction: 24 Branch Prediction Accuracy: 0.4545 (Using Strategy: Always Not Taken) Number of Control Hazards: 74 Number of Data Hazards: 356 Number of Structural Hazards: 26 -----------------------------------一注：对于mul-div.c测试文件似乎由于运算太简单，导致使用RV64I也可以完成该程序。下面列出仅使用RV64I指令的测试数据：rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/rv64i/inclass/add.riscv Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 595 Number of Instructions: 472 Avg Cycles per Instrcution: 1.2606 Number of JALR Instructions: 16 Number of JAL Instructions: 13 Number of LOAD Instructions: 91 Number of STORE Instructions: 64 Number of BRANCH Instructions: 44 Number of Successful Prediction: 20 Number of Unsuccessful Prediction: 24 Branch Prediction Accuracy: 0.4545 (Using Strategy: Always Not Taken) Number of Control Hazards: 72 Number of Data Hazards: 353 Number of Structural Hazards: 23 ----------------------------------- rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/rv64i/inclass/mul-div.riscv Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 612 Number of Instructions: 490 Avg Cycles per Instrcution: 1.2490 Number of JALR Instructions: 16 Number of JAL Instructions: 12 Number of LOAD Instructions: 91 Number of STORE Instructions: 64 Number of BRANCH Instructions: 43 Number of Successful Prediction: 19 Number of Unsuccessful Prediction: 24 Branch Prediction Accuracy: 0.4419 (Using Strategy: Always Not Taken) Number of Control Hazards: 69 Number of Data Hazards: 377 Number of Structural Hazards: 32 ----------------------------------- rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/rv64i/inclass/n\!.riscv Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 901 Number of Instructions: 720 Avg Cycles per Instrcution: 1.2514 Number of JALR Instructions: 56 Number of JAL Instructions: 40 Number of LOAD Instructions: 89 Number of STORE Instructions: 81 Number of BRANCH Instructions: 98 Number of Successful Prediction: 43 Number of Unsuccessful Prediction: 55 Branch Prediction Accuracy: 0.4388 (Using Strategy: Always Not Taken) Number of Control Hazards: 164 Number of Data Hazards: 363 Number of Structural Hazards: 64 ----------------------------------- rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/rv64i/inclass/qsort.riscv Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 23222 Number of Instructions: 17952 Avg Cycles per Instrcution: 1.2936 Number of JALR Instructions: 93 Number of JAL Instructions: 247 Number of LOAD Instructions: 6293 Number of STORE Instructions: 1587 Number of BRANCH Instructions: 1878 Number of Successful Prediction: 877 Number of Unsuccessful Prediction: 1001 Branch Prediction Accuracy: 0.4670 (Using Strategy: Always Not Taken) Number of Control Hazards: 2177 Number of Data Hazards: 18121 Number of Structural Hazards: 340 ----------------------------------- rainshaw@Ubuntu-Linux:~/CLionProjects/RISCV-Simulator/build$ ./Sim ../riscv-elf/rv64i/inclass/simple-function.riscv Program exit from exit() system call -------STATISTICS--------- Number of Cycles: 607 Number of Instructions: 483 Avg Cycles per Instrcution: 1.2567 Number of JALR Instructions: 17 Number of JAL Instructions: 14 Number of LOAD Instructions: 93 Number of STORE Instructions: 66 Number of BRANCH Instructions: 44 Number of Successful Prediction: 20 Number of Unsuccessful Prediction: 24 Branch Prediction Accuracy: 0.4545 (Using Strategy: Always Not Taken) Number of Control Hazards: 74 Number of Data Hazards: 356 Number of Structural Hazards: 26 -----------------------------------4.2.2 动态执行指令数程序名执行的指令数(RV64IM)执行的指令数(RV64I)add.riscv472472mul-div.riscv490490n!.riscv510720qsort.riscv1795217952simple-function.riscv483483从表格中看出，RV64I指令缺少的乘法指令，可以由多步加法给出，这导致了n!.riscv程序的执行指令数增加。4.2.3 执行周期数和平均CPI| 程序名 | 执行周期数(RV64IM) | CPI(RV64IM) | 执行周期数(RV64I) | CPI(RV64I) | | ----------------------- | -------------------- | ------------- | --------------------------------- | | add.riscv | 595 | 1.2606 | 595 | |mul-div.riscv | 612 | 1.2490 | 612 | |n!.riscv | 638 | 1.2510 | 901 | |qsort.riscv | 23222 | 1.2936 | 23222 | |simple-function.riscv` | 607 | 1.2567 | 607 |可以发现，基本上流水线模拟器平均CPI在1.3以下，大约实现了单周期处理器的3.8倍指令吞吐量。4.2.4 不同类型的冒险统计程序名数据冒险控制冒险结构冒险指令集add.riscv3537223RV64IM RV64Imul-div.riscv3776932RV64IM RV64In!.riscv3079064RV64IMqsort.riscv181212177340RV64IM RV64Isimple-function.riscv3567426RV64IM RV64In!.riscv36316464RV64I4.2.5 分支预测模块的性能评测前述程序都是在基于默认Always Not Taken策略下进行的模拟，我们可以通过设定不同的预测策略进行评测，如下表评测程序Always TakenBTFNTPrediction Bufferadd.riscv0.54550.59090.5455mul-div.riscv0.55810.60470.5581n!.riscv0.57140.38100.5714qsort.riscv0.53300.90840.9063helloworld.riscv0.52940.52940.5294matrixmulti.riscv0.90530.90530.9053ackerman.riscv0.49560.50520.9594从表中可以看出，简单的程序如add helloworld n! mil-div由于大多数指令只执行一次，所以BPB方法退化成为AT方法，这是因为我的模拟器中，BPB方法的默认策略是WEAK_TAKEN。对于快排程序，BTFNT和BPB都获得了很高的准确率，这是因为代码中有很多循环+1操作，都满足向前跳转的性质，所以BTFNT准确度很高，而由于排序个数也比较多，循环执行次数也会很高，所以基于缓存的BPB准确性也会更好。对于矩阵乘法，三个策略都很接近，这可能是由于矩阵阶数较小，循环次数较少，限制了BTFNT和BPB的性能。对于Ackermann程序，其中基本上都是递归调用和条件判断，没有循环，此时基于缓存的BPB就能发挥巨大威力，其余两种就都比较受限了。其他需要说明的内容M扩展集具体实现了以下指令MUL, MULHU, DIV, DIVU, REM, REMU, MULW, DIVW, DIVUW, REMW, REMUW流水线流程图
计算机组成与体系结构实习Lab1——处理器性能评测报告
处理器性能评测报告我们针对系统评测的不同角度会采用不同的评测程序。在目前已有的评测程序中，为下列评测目标找到某些合适的评测程序（列出即可）测评目标测评程序测评目标测评程序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的应用有了较为深入的认识，并且认识到了Benchmark的局限性。评测环境项目详细指标和参数处理器型号及相关参数Intel(R) Corm(TM) i7-8700K CPU @3.7GHz 6 x 32 KB 8-way set associative instruction caches 6 x 32 KB 8-way set associative data caches 6 x 256 KB 4-way set associative caches 12 MB 16-way set associative shared cache内存1GB DDR4 3200MHz外存64G SSD操作系统及版本Linux Ubuntu-Linux 5.3.0-40-generic #32~18.04.1-Ubuntu SMP Mon Feb 3 14:05:59 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux编译器版本gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)测评步骤及结果1. 在linux下基于dhrystone-2.1所提供的Makefile编译dhrystone使用 tar zxvf dhrystone-2.1.tar.gz 解压,然后进入解压目录下进行 make 命令, 报错.根据报错提示,将 dhry_1.c 中的第47行的 times 函数声明修改为 extern clock_t times(struct tms *__buffer); 后, make 不再报错,生成四个新的可执行文件.2. 分别采用$10^8$、$3\times10^8$、$5\times10^8$、$7\times10^8$、$9\times10^8$为输入次数，运行编译生成的两个程序，记录、处理相关数据并做出解释。由于源程序输出精度限制,将 dhry_1.c 中第281行代码,修改为 printf ("%6.6f \n", Microseconds), 重新 make 后,用规定的循环轮数运行生成的四个可执行文件.结果如下表循环轮数类型gcc_dry2gcc_dry2regcc_dry2cc_dry2reg$1\times10^8$ms/run0.04420.04380.03350.0345$1\times10^8$Dhry/s2.26x$10^7$2.28x$10^7$2.99x$10^7$2.90x$10^7$$3\times10^8$ms/run0.04360.04480.03400.0337$3\times10^8$Dhry/s2.30x$10^7$2.23x$10^7$2.94x$10^7$2.97x$10^7$$5\times10^8$ms/run0.04360.04430.03400.0340$5\times10^8$Dhry/s2.29x$10^7$2.26x$10^7$2.94x$10^7$2.94x$10^7$$7\times10^8$ms/run0.04360.04370.03410.0339$7\times10^8$Dhry/s2.30x$10^7$2.29x$10^7$2.93x$10^7$2.95x$10^7$$9\times10^8$ms/run0.04480.04380.03440.0337$9\times10^8$Dhry/s2.23x$10^7$2.28x$10^7$2.90x$10^7$2.97x$10^7$从gcc编译的选项对于dhrystone的处理能力比cc稍差一些,对于不同的输入次数, dhrystone per second 会随着此数的增多而略微上升.3. 对dhrystone代码做少量修改，使其运行结果不变但“性能”提升。由于 dhry_2.c 的 Proc_8 函数中第101行存在一个只进行两次的循环,所以将其改写,去掉循环以减少循环的跳转预测错误引起的时间损失.结果如下表结果如下表循环轮数类型gcc_dry2_oldgcc_dry2_newcc_dry2_oldcc_dry2_new$1\times10^8$ms/run0.04420.04250.03350.0336$1\times10^8$Dhry/s2.26x$10^7$2.35x$10^7$2.99x$10^7$2.97x$10^7$$3\times10^8$ms/run0.04360.04280.03400.0341$3\times10^8$Dhry/s2.30x$10^7$2.34x$10^7$2.94x$10^7$2.93x$10^7$$5\times10^8$ms/run0.04360.04360.03400.0341$5\times10^8$Dhry/s2.29x$10^7$2.30x$10^7$2.94x$10^7$2.94x$10^7$$7\times10^8$ms/run0.04360.04230.03410.0344$7\times10^8$Dhry/s2.30x$10^7$2.36x$10^7$2.93x$10^7$2.91x$10^7$$9\times10^8$ms/run0.04480.04230.03440.0342$9\times10^8$Dhry/s2.23x$10^7$2.36x$10^7$2.90x$10^7$2.92x$10^7$从上表中可以看出,针对 Proc_8 循环语句的修改, 小幅度的提升了性能,降低了 CPU Time, 该修改是有效的.4. 采用dhrystone进行评测有哪些可改进的地方？对其做出修改、评测和说明。使用编译优化选项对跳转指令进行预测优化, 分别修改 Makefile 中的 GCCOPTIM 选项为 -O0,-O1,-O2,-O3 使用 gcc_dry2 进行实验,结果如下表.循环轮数类型-O0-O1-O2-O3$1\times10^8$ms/run0.08680.04350.03520.0348$1\times10^8$Dhry/s1.15x$10^7$2.30x$10^7$2.84x$10^7$2.87x$10^7$$3\times10^8$ms/run0.08690.04270.03490.0351$3\times10^8$Dhry/s1.15x$10^7$2.34x$10^7$2.87x$10^7$2.85x$10^7$$5\times10^8$ms/run0.08720.04710.03510.0346$5\times10^8$Dhry/s1.15x$10^7$2.12x$10^7$2.85x$10^7$2.89x$10^7$$7\times10^8$ms/run0.08740.04230.03520.0341$7\times10^8$Dhry/s1.14x$10^7$2.37x$10^7$2.84x$10^7$2.94x$10^7$$9\times10^8$ms/run0.08680.04340.03540.0343$9\times10^8$Dhry/s1.15x$10^7$2.30x$10^7$2.82x$10^7$2.92x$10^7$从表中可以看出 -O1 相对于 -O0 来说, CPU Time 有明显的下降, 并且 -O1,-O2,-O3 来说没有较大提升.5. 在linux下使用编译器分别采用-O0、-O2、-O3选项对whetstone程序进行编译并执行，记录评测结果。使用命令 gcc whetstone.c -lm -o whetstone -O0 依次生成 -O0, -O2, -O3 程序, 得到whet0, whet1, whet2 三个可执行文件(数字编号对应优化).6. 分别采用$10^6$、$10^7$、$10^8$、$10^9$为输入次数，运行编译生成的可执行程序，记录、处理相关数据并做出解释。得到实验结果如下表循环轮数类型whet0whet2whet3$1\times10^6$Dura311615$1\times10^6$MIPS3225.86250.06666.7$1\times10^7$Dura299112104$1\times10^7$MIPS3344.58928.69615.4$1\times10^8$Dura301110971007$1\times10^8$MIPS3321.29115.89930.5$1\times10^9$Dura28825105699763$1\times10^9$MIPS3469.29461.610242.8根据上表,发现循环轮数足够大时, whetstone 检测趋于稳定. 此外 -O0 到 -O3 各个优化选项带来的影响基本符合预期, -O2 和 -O3 性能提升很小,因为 -O3 与 -O2 最主要差别为对于 inline 的优化,而 whetstone 中没有这一类函数.7. 进一步改进whetstone程序性能（例如新的编译选项），用实验结果回答。由于 whetstone 是针对浮点运算的测评,利用 -ffast-math 选项对 whetstone 性能进行优化, -ffast-math 对 math 库进行优化,大幅提高运行速度,但也可能造成一些运算错误.实验结果如下表循环轮数类型whet0whet2whet3$1\times10^6$Dura1411$1\times10^6$MIPS7142.9100000.0100000.0$1\times10^7$Dura1391213$1\times10^7$MIPS7194.283333.376923.1$1\times10^8$Dura1377129138$1\times10^8$MIPS7262.277519.472463.8$1\times10^9$Dura1352212171312$1\times10^9$MIPS7395.482196.376219.5从上表中, 可以发现增加 -ffast-math 后运行速度有了极大的提升.8. 完成SPEC CPU2000的安装。略9. 修改自己的config文件，分别用低强度优化（例如O2）和高强度优化（例如O3）完成完整的SPEC CPU2000的评测，提交评测报告文件。这里给出我改的cfg文件，并且将 gcc、g++、gfortran 都降至4.8版本，分别用低优化和高优化进行bench。[collapse status="false" title="linux-amd64-gcc4.cfg"]############################################################################### # This is a sample config file for AMD64 linux using GCC 4.0 compiler. # This does not provide any peak level tuning, but can be used as a # starter config file. ############################################################################### # To run: runspec -c linux-amd64-gcc4.cfg -T all -n 3 int fp # The extension 'gcc4-high-opt' uses high optimization and is enabled by # default. If you want lower levels of optimization, use the extension # 'gcc4-low-opt', e.g. # runspec -c linux-amd64-gcc4.cfg -T all -n 3 -e gcc4-low-opt int fp ############################################################################### # These variables below are examples and should be modified according to the # system under test. company_name = XYZ Inc. hw_model = Opteron (TM) 150 hw_cpu = AMD Opteron (TM) 150 hw_cpu_mhz = 2400 hw_disk = IDE, WD2000 hw_fpu = Integrated hw_memory = 2 x 512 PC3200 DDR SDRAM CL2.0 (Corsair CMX512R-3200C2) hw_avail = May-2003 test_date = sw_file = Linux/ext3 sw_os = SuSE Linux 9.2 for AMD64 hw_vendor = tester_name = XYZ Inc. license_num = 0000 hw_ncpu = 1 hw_ncpuorder = 1 hw_ocache = N/A hw_other = None hw_parallel = No hw_pcache = 64KBI + 64KBD on chip hw_scache = 1024KB(I+D) on chip hw_tcache = N/A sw_state = Multi-user SuSE Run level 3 VENDOR = action = validate tune = base output_format = asc,html,config ext = gcc4-high-opt check_md5 = 1 reportable = 1 teeout=yes teerunout=yes # # These are listed as benchmark-tuning-extension-machine # default=default=default=default: CC = gcc -fpermissive -DHAS_ERRLIST -DSPEC_STDCPP CXX = g++ -fpermissive -DHAS_ERRLIST -DSPEC_STDCPP FC = gfortran F77 = gfortran ################################################################ # Portability Flags ################################################################ 255.vortex=default=default=default: notes0045= 255.vortex= CPORTABILITY=-DSPEC_CPU2000_LP64 CPORTABILITY = -DSPEC_CPU2000_LP64 186.crafty=default=default=default: notes0050= 186.crafty: CPORTABILITY=-DLINUX_i386 CPORTABILITY = -DLINUX_i386 252.eon=default=default=default: notes0051= 252.eon: CXXPORTABILITY=-DHAS_ERRLIST -DSPEC_CPU2000_LP64 CXXPORTABILITY = -DHAS_ERRLIST -DSPEC_CPU2000_LP64 253.perlbmk=default=default=default: notes0052= 253.perlbmk: CPORTABILITY=-DSPEC_CPU2000_LINUX_I386 -DSPEC_CPU2000_NEED_BOOL notes0053= -DSPEC_CPU2000_LP64 CPORTABILITY = -DSPEC_CPU2000_NEED_BOOL -DSPEC_CPU2000_LINUX_I386 -DSPEC_CPU2000_LP64 254.gap=default=default=default: notes0055= 254.gap: CPORTABILITY=-DSYS_IS_USG -DSYS_HAS_IOCTL_PROTO -DSYS_HAS_TIME_PROTO notes0056= -DSYS_HAS_CALLOC_PROTO -DSYS_HAS_MALLOC_PROTO notes0057= -DSPEC_CPU2000_LP64 CPORTABILITY = -DSYS_HAS_MALLOC_PROTO -DSYS_HAS_CALLOC_PROTO -DSYS_IS_USG -DSYS_HAS_IOCTL_PROTO -DSYS_HAS_TIME_PROTO -DSPEC_CPU2000_LP64 178.galgel=default=default=default: notes0050= 178.galgel: -ffixed-form FPORTABILITY= -ffixed-form ################################################################ # Baseline Tuning Flags ################################################################ # # int2000 # Base tuning default optimization # int=base=gcc4-low-opt=default: notes0080= Baseline C: gcc -O2 COPTIMIZE = -O2 feedback=0 252.eon=base=gcc4-low-opt=default: notes0085= Baseline C++: g++ -O2 CXXOPTIMIZE = -O2 # High Optimization: int=base=gcc4-high-opt=default: notes0080= Baseline C: gcc -O3 notes0085= Baseline C++: g++ -O3 COPTIMIZE = -O3 CXXOPTIMIZE = -O3 # # fp2000 # Base tuning default optimization # # Fortran benchmarks # fp=base=gcc4-low-opt=default: notes0085= Baseline: Fortran gfortran -O2 notes0080= Baseline: C gcc -O2 FOPTIMIZE = -O2 F77OPTIMIZE = -O2 COPTIMIZE = -O2 #High Optimization: fp=base=gcc4-high-opt=default: notes0080= Baseline: C gcc -O3 notes0085= Baseline: Fortran gfortran -O3 FOPTIMIZE = -O3 F77OPTIMIZE = -O3 COPTIMIZE = -O3 ################################################################ # Peak Tuning Flags ################################################################ # # int2000 # Peak tuning # int=peak=default=default: notes0087= All peak: basepeak=yes basepeak = yes int=default=default=default: notes0030= Portability: sw_avail= Dec-2003 sw_compiler0000= GNU gcc 4.0 compiler for AMD64 fp=default=default=default: sw_avail= Jan-2004 sw_compiler0000= GNU gcc 4.0 and gfortran 4.0 compiler for AMD64 __MD5__ 164.gzip=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:01:55 2020 optmd5=96f7305b2efe7cd6a086a3500462c7d6 exemd5=2550749a90a2523204948317004f6c16 175.vpr=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:01:55 2020 optmd5=3ba2478ba3445aab700488b14bdfc04a exemd5=9c74f8c1842951f19b3c1b2e48ab50ba 176.gcc=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:01:55 2020 optmd5=3869c2abdc72c6dede932f899746bab1 exemd5=45e9075f2fc7720fb90430798cc507d7 181.mcf=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:01:55 2020 optmd5=bcd9714775fb8b9dfc766973339bc9fa exemd5=20b03564736d487e384a1c3f90c577af 186.crafty=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:01:55 2020 optmd5=e7e175d76af05a01280aa10308a66f1f exemd5=12472c760f2654d2d264b0ef18a70726 197.parser=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:01:55 2020 optmd5=da82519e9fe7cae0c83c252057a1739e exemd5=60c73c9944ccc16786796729a4ec55ec 252.eon=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:01:55 2020 optmd5=a57f4fee33aed5ba3911b4de570ba28e exemd5=8f010418230469acb2569d0ea23f6470 253.perlbmk=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:01:55 2020 optmd5=fab2769aca012ebde3a21dc21ed71dde exemd5=5c9a8f3511b1b712bac4ecc60e21d33d 254.gap=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:01:55 2020 optmd5=3bc92b9947eb96341ef63508a8e70c28 exemd5=6ef05078ba00eac8f50b1af8fd6c8976 255.vortex=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:01:55 2020 optmd5=da9d19bbb8d7ec39dd0848959aa370f7 exemd5=ca63ea932c2d5cae4c80e9573265d21c 256.bzip2=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:01:55 2020 optmd5=14db51db373187a0f6736a9d125ec07d exemd5=862fc9b5a106bc3e4f12a56905d9e1cc 300.twolf=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:01:55 2020 optmd5=1ce94c85c8e2d9217efdf6895de6020b exemd5=535f64680cf4fdd1c9cad761459bd7f5 168.wupwise=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=5c25d15abd005cc698c92a664df5184a exemd5=3745063442df6a46ac6eb9db5097dbbd 168.wupwise=peak=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=063d1ea855415d7646c130c6e0416b61 exemd5=ea62c4b615c8a01bed3eae917a40504d 171.swim=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=a4404c279ad9ef66119bca1a18cadddb exemd5=a65db57f397667eff6f15d83f5cf9dae 171.swim=peak=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=2bd88232e6a81d10148efcdea591c5a2 exemd5=b06ecafe63b0ee604f5d9fc149e94fcb 172.mgrid=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=46bf28f6e0ccfcab7ae615e72e4697ab exemd5=380bbb12ae3afc40c770949c26ded4f1 172.mgrid=peak=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=e912cad32f119f12a0d567dcd80bed1b exemd5=f549a0c3c8951f8dbfa4b394fe5b37f0 173.applu=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=724abee4112d4b1443c1e850d6db1105 exemd5=a5cef86974cf57b8103341aed9f94509 173.applu=peak=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=69883d821c6385916c594d49d35df5d1 exemd5=277f3a4e9426f11acaf14d2be943da96 177.mesa=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=d51cec6ce8fcbcd52ea64dbf63575ceb exemd5=766dcf0c31fcbcffe6a851025f0a7e33 177.mesa=peak=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=d887ed3e6e0832b9abbb33d87c8b2598 exemd5=56f986a54dce751a9c128b8a9703022a 178.galgel=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=216232f1c342b8cf033a8ed4afa7900d exemd5=e3cb0b15ff7796aba5fdfb91472a6c1c 178.galgel=peak=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=fa2ae5d1563af1dd5b2a3b7e779accf7 exemd5=ed6ed19866e9ed9f75aa8d4226577a47 179.art=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=6babcc2f0b62506715f2d9a29e7e25a9 exemd5=320425d183802eca2b9269d66b9054f8 179.art=peak=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=c9f4bfad75e783afd051a0ecdf1aa3e3 exemd5=8178f20f56c18f57390e7240decd6ca0 183.equake=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=30dc22ffdf76386d218fbc142b265cfc exemd5=90860a44c83a9e23693b32633c67924f 183.equake=peak=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=b04acdba2329a0dc64b97e9285596632 exemd5=de870126e6dae03de05d1aee4d28508d 187.facerec=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=35a9d9f189dd3bcf03df8b1ec7450e67 exemd5=eaf558c54678420de61fd9bc02e191bb 187.facerec=peak=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=0db47db4c3ba62397682ba6a72ec9b97 exemd5=eec508203b69f4c716fd7b921105e1d2 188.ammp=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=13119a6ce16610d8c9cb57f7668eef2a exemd5=93819b1283f1c26a8963c9bf7c1d5c34 188.ammp=peak=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=28cd70cc627ff27c732fedc254b6e248 exemd5=e88fdc1bddcd3cb6073281f7e32da011 189.lucas=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=3c46180d4d2aeb9a64902de7f83e4d31 exemd5=dc4486c74a1d64531c2c4b7051701f87 189.lucas=peak=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=57818530c2124120646da96a1d67f422 exemd5=49aef15fd0edebb99dafd23cdae84db4 191.fma3d=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=b40236010d15cb4a8ddb460ba65f1803 exemd5=ee5f636eb7b3a999bb188fbf9264d9b1 191.fma3d=peak=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=48d7f92813543e40df7af626cecb1928 exemd5=d95efad10ac8cab0f3e3d30c96f21818 200.sixtrack=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=6f4ebf9dcbd9d1c3bcfab2c31dac645c exemd5=18c3ad3aa5ebe88fedc504502ca692f4 200.sixtrack=peak=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=e55964a2beba18f56ab8fc598b25da7d exemd5=69d37b6e6dd53389990b7f1f51031e35 301.apsi=base=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=1f2a54d2d4e1fcbeffab87b429217105 exemd5=04bbe69751f307d7a8390a70ddf844b8 301.apsi=peak=gcc4-high-opt=default: # Last updated Sat Feb 29 20:05:07 2020 optmd5=9c94dddc242d2e02e001a3d6830a9745 exemd5=bc18b5c0a052868587af0bd4bf092092 164.gzip=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=b410bae0d5b4cb300ecab8c9089b51cb exemd5=9e034310b3dadebcc84d6bab8f252eb3 168.wupwise=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=6d716a34f257dfaabcb9e1e6e9a8bbfb exemd5=30b6f27c6e17ccd7f31b8f71b205aea1 168.wupwise=peak=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=629ebaf8eb12ee0fbe39291808ed54df exemd5=ea62c4b615c8a01bed3eae917a40504d 171.swim=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=b8e3544181a92b94c8e8207e8bdd3593 exemd5=93e9a76c926e772821691ecc31fbff5a 171.swim=peak=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=18320100daaa43ffd8e02f2283a18300 exemd5=b06ecafe63b0ee604f5d9fc149e94fcb 172.mgrid=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=d5337d68c449d31a4407315f475a74cd exemd5=821cf74de649e049562ab8aaf4b07ebc 172.mgrid=peak=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=a18aa5298f6e5434b30009db996491e7 exemd5=f549a0c3c8951f8dbfa4b394fe5b37f0 173.applu=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=499cb2affff242cbb155985e732618e6 exemd5=232b21a6dd1ee109f43f0745c53bb95e 173.applu=peak=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=4a70e336ebe8f450fd1db9cd69b97d70 exemd5=277f3a4e9426f11acaf14d2be943da96 175.vpr=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=bc307035c095a2f898c9db4f4c09340d exemd5=1fbe49c1ef5391b9ebbf9c299b3752ba 176.gcc=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=a16a1ba36d4b4db525b519044341bd72 exemd5=ce32b4b4c6b3b563295ea02958dab2c9 177.mesa=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=6a7ea0fb4ecbbe39779eb09a6481af7f exemd5=44df1961dccdc5925244a613813174e9 177.mesa=peak=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=150fd0276a454cdadb2bd7dc07d629ed exemd5=56f986a54dce751a9c128b8a9703022a 178.galgel=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=3804f00fd90eea58fc137c8549ac97ee exemd5=a2615e7580e91e9b656adf9c9f4ed0b7 178.galgel=peak=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=29a25880c110c3c80839cca4c309f5cb exemd5=ed6ed19866e9ed9f75aa8d4226577a47 179.art=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=5900109d64ea34afa92f7abda2b86810 exemd5=5cf10a4be0a02efaf33dbe6a11eab29d 179.art=peak=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=5d4fba8242edf0c74ae9b2725eb6d824 exemd5=8178f20f56c18f57390e7240decd6ca0 181.mcf=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=740d53f67b6ea1df2fdc02e4787f3960 exemd5=edb424df15ad0ecf57b0469703bdfa8d 183.equake=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=165ff9f467b01c870ec56aa09bf89317 exemd5=47ad172d28e117b7d98ad8c10d64ebfa 183.equake=peak=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=c766db18ad6acce60fbce58d0c161a04 exemd5=de870126e6dae03de05d1aee4d28508d 186.crafty=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=bd8470e2f0717b6c1116fab19f256ff1 exemd5=08876c4d3bec36c9e28469358beeb4ec 187.facerec=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=3cfd6b2aeec4f40876ee8eafb4d53f1d exemd5=d954b378b8d66d679b469ca0be6ac9df 187.facerec=peak=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=8ff5a14b354841b6233e3129daa59a61 exemd5=eec508203b69f4c716fd7b921105e1d2 188.ammp=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=46c649dbd651d28663373649824657a4 exemd5=79169fc71da478a31befd218ef0da399 188.ammp=peak=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=27ed62374413191cf2edf09d4920cd46 exemd5=e88fdc1bddcd3cb6073281f7e32da011 189.lucas=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=605977aa3e0c622f71d30f3275329331 exemd5=0d6ac97fc9125927dd4f3fdff1b13aff 189.lucas=peak=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=65d55b5ccdece4438a4efd8d22f34868 exemd5=49aef15fd0edebb99dafd23cdae84db4 191.fma3d=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=0b6b65e98d64d39354ca483c9e06b1c1 exemd5=a821be3d371fd1fb135690796ee529da 191.fma3d=peak=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=db0bdf2723c8d203e34aa4a558c05c00 exemd5=d95efad10ac8cab0f3e3d30c96f21818 197.parser=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=2208e78ed24fd1cbf52a9f944b247607 exemd5=c0c7e8bcb8dc6800657c13c74e6b56e6 200.sixtrack=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=d91514cef4ea49a8c4586cc12aa95e9b exemd5=a04d2f8d9198f3152aad6439d4193305 200.sixtrack=peak=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=84cf5cea07e63d844f9e4af9d72afd4c exemd5=69d37b6e6dd53389990b7f1f51031e35 252.eon=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=3ddc9242465c9c3081f1a1408f9df44e exemd5=dd0fde9ef90b079b09de4bf846bbda2c 253.perlbmk=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=b85ac7c48652bd60c0dd886c665311f2 exemd5=0acfce1e19a854d2f0184dc88bc345e1 254.gap=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=a186d23f7ab59c8af67f85311c570afe exemd5=740a60c7c783d4a9660d935a44c934f3 255.vortex=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=600d5c4aa160911b1ff36551598fa4f1 exemd5=15256871b0669c91fa9aeb134779e39a 256.bzip2=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=950bff2e3d1f8f02f0aa33b40594ad1f exemd5=e34a5f93462181bf6f051a1650db1e24 300.twolf=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=bc30ac1b85dd975e82e7a87cd2c0101d exemd5=96cc977ae9940a02d5d6c7021f4843ce 301.apsi=base=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=19c21bf245ce3b60c6190f3e0d064669 exemd5=cc1ab14d0493adae7f860f22f314687a 301.apsi=peak=gcc4-low-opt=default: # Last updated Sat Feb 29 22:28:54 2020 optmd5=93ac2de4f8e6bab66d3bc06dbacf676f exemd5=bc18b5c0a052868587af0bd4bf092092[/collapse]CFP2000.016.html CINT2000.016.html为高强度优化的测评报告文件.CFP2000.017.html CINT2000.017.html为低强度优化的测评报告文件.小结本次实验,利用 Whetstone Benchmark 和 Dhrystone Benchmark 以及 SPEC CPU2000, 对测评电脑进行了多方面的测试. 从中可以看出,不同的测评程序对于同一电脑的结果均有所差别, 这说明 Benchmark 具有一定的局限性, 较容易被针对优化.
TCP协议实验——计算机网络概论实习作业四
TCP协议实验[TOC]实验目的本实验的主要目的是学习和了解 TCP 协议的原理和设计实现的机制。TCP 协议还要向应用层提供编程接口，即网络编程中所普遍使用的 Socket 函数。通过实现这些接口函数，可以深入了解网络编程的原理，提高网络程序的设计和调试能力。实验要求1) 了解 TCP 协议的主要内容，并针对客户端角色的、“停-等”模式的 TCP 协议，完成对接收和发送流程的设计。2) 实现 TCP 报文的接收流程，重点是报文接收的有限状态机。3) 实现 TCP 报文的发送流程，完成 TCP 报文的封装处理。4) 实现客户端 Socket 函数接口。实验内容实验内容主要包括:1) 设计保存 TCP 连接相关信息的数据结构(一般称为 TCB，Transmission Control Block)。2) TCP 协议的接收处理。3) TCP 协议的封装发送。4) TCP 协议提供的 Socket 函数接口问题记录gSrcPort应该为2007，而非指导书上的2005在stud_tcp_output()中新建的TCB，在stud_tcp_input()中调用要将接收端和发送端互换实验源码代码基于上次作业修改完成，故有较多注释/* * THIS FILE IS FOR TCP TEST */ /* struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; */ #include "sysInclude.h" extern void tcp_DiscardPkt(char *pBuffer, int type); extern void tcp_sendReport(int type); extern void tcp_sendIpPkt(unsigned char *pData, UINT16 len, unsigned int srcAddr, unsigned int dstAddr, UINT8 ttl); extern int waitIpPacket(char *pBuffer, int timeout); extern unsigned int getIpv4Address(); extern unsigned int getServerIpv4Address(); #define BUFFER_SIZE 1024 #define TIMEOUT 10 #define FIN 0x01 #define SYN 0x02 #define ACK 0x10 #define FIN_ACK 0x11 #define SYN_ACK 0x12 #define CLOSED 0 #define LISTEN 1 #define SYN_RCVD 2 #define SYN_SENT 3 #define ESTABLISHED 4 #define FIN_WAIT_1 5 #define FIN_WAIT_2 6 #define TIME_WAIT 7 #define GetSrcPort(p) (ntohs(*(UINT16*)p)) #define GetDstPort(p) (ntohs(*(UINT16*)(p+2))) #define GetSeq(p) (ntohl(*(UINT32*)(p+4))) #define GetACK(p) (ntohl(*(UINT32*)(p+8))) #define GetHeaderLength(p) ((p[12]>>2)&0x3C) #define GetFlags(p) (p[13]&0x13) #define SetSrcPort(p,x) *(UINT16*)p=htons(x) #define SetDstPort(p,x) *(UINT16*)(p+2)=htons(x) #define SetSeq(p,x) *(UINT32*)(p+4)=htonl(x) #define SetACK(p,x) *(UINT32*)(p+8)=htonl(x) #define SetHeaderLength(p,x) p[12]=(x<<2) #define SetFlags(p,x) p[13]=x #define SetWindow(p,x) *(UINT16*)(p+14)=htons(x) #define SetCheckSum(p,x) *(UINT16*)(p+16)=x int gSrcPort = 2007; int gDstPort = 2006; int gSeqNum=1; int gAckNum=1; struct TCB{ TCB* next_tcb; int socketfd; UINT32 srcAddr; UINT32 dstAddr; UINT16 srcPort; UINT16 dstPort; UINT32 seq; UINT32 ack; UINT16 window; UINT8 state; TCB(int sockfd){ seq=gSeqNum; ack=gAckNum; window=1; state=CLOSED; next_tcb=NULL; socketfd=sockfd; } TCB(){ seq=gSeqNum; ack=gAckNum; window=1; state=CLOSED; next_tcb=NULL; socketfd=0; srcAddr=getIpv4Address(); dstAddr=getServerIpv4Address(); srcPort=gSrcPort; dstPort=gDstPort; } }; TCB * tcb_linklist_head = NULL; static int socketfd=1; UINT16 calculate_checksum(char *pBuffer, int len, UINT32 srcAddr, UINT32 dstAddr){ int tcp_len=len+12; UINT32 sum=0; if(tcp_len & 0x1 ==1) tcp_len+=1; char * buffer=new char[tcp_len]; memset(buffer, 0, tcp_len); memcpy(buffer+12, pBuffer, len); *(UINT32*)buffer=htonl(srcAddr); *(UINT32*)(buffer+4)=htonl(dstAddr); buffer[9]=6; *(UINT16*)(buffer+10)=htons(len); for(int i=0; i<tcp_len; i+=2) sum += *(UINT16*)(buffer+i); sum = (sum & 0xffff)+(sum>>16); sum=~sum; delete[] buffer; printf("checksum:%x\n",sum); return sum; } TCB* searchTCB_byaddr(UINT32 srcAddr, UINT16 srcPort, UINT32 dstAddr, UINT16 dstPort){ TCB * tcb=tcb_linklist_head; while(tcb!=NULL && !(tcb->srcAddr==srcAddr&&tcb->srcPort==srcPort&&tcb->dstAddr==dstAddr&& tcb->dstPort==dstPort)){ tcb=tcb->next_tcb; } return tcb; } TCB* searchTCB_byid(int socketfd){ TCB* tcb = tcb_linklist_head; while(tcb!=NULL && tcb->socketfd!=socketfd) tcb=tcb->next_tcb; return tcb; } int stud_tcp_input(char *pBuffer, unsigned short len, unsigned int srcAddr, unsigned int dstAddr) { printf("\nTCP Input!\n"); if(calculate_checksum(pBuffer,len,ntohl(srcAddr),ntohl(dstAddr))!=0){ return -1; } UINT16 srcPort=GetSrcPort(pBuffer); UINT16 dstPort=GetDstPort(pBuffer); UINT32 seq=GetSeq(pBuffer); UINT32 ack=GetACK(pBuffer); UINT8 flags=GetFlags(pBuffer); TCB *tcb = searchTCB_byaddr(ntohl(dstAddr),dstPort,ntohl(srcAddr),srcPort); if(tcb==NULL){ return -1; } if(ack!=tcb->seq+1){ tcp_DiscardPkt(pBuffer,STUD_TCP_TEST_SEQNO_ERROR); return -1; } if(tcb->state==SYN_SENT && flags==SYN_ACK){ tcb->seq=ack; tcb->ack=seq+1; stud_tcp_output(NULL,0,PACKET_TYPE_ACK,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr); }else if(tcb->state==FIN_WAIT_1 && flags==ACK){ tcb->state=FIN_WAIT_2; }else if(tcb->state==FIN_WAIT_2 && flags==FIN_ACK){ tcb->seq=ack; tcb->ack=seq+1; tcb->state=TIME_WAIT; stud_tcp_output(NULL,0,PACKET_TYPE_ACK,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr); tcb->state=CLOSED; } return 0; } void stud_tcp_output(char *pData, unsigned short len, unsigned char flag, unsigned short srcPort, unsigned short dstPort, unsigned int srcAddr, unsigned int dstAddr) { printf("\nTCP output\n"); TCB * tcb=searchTCB_byaddr(srcAddr, srcPort, dstAddr, dstPort); if(tcb_linklist_head==NULL){ printf("\n NEW TCB!\n"); tcb= new TCB(); tcb_linklist_head=tcb; } if(tcb==NULL){ return; } if(tcb->window==0){ return; } unsigned char * packet = new unsigned char[len + 20]; memset(packet,0, len+20); memcpy(packet+20,pData,len); SetSrcPort(packet,tcb->srcPort); SetDstPort(packet,tcb->dstPort); SetSeq(packet,tcb->seq); SetACK(packet,tcb->ack); SetHeaderLength(packet,20); switch(flag){ case PACKET_TYPE_SYN: SetFlags(packet,SYN); tcb->state=SYN_SENT; break; case PACKET_TYPE_ACK: SetFlags(packet,ACK); break; case PACKET_TYPE_SYN_ACK: SetFlags(packet,SYN_ACK); break; case PACKET_TYPE_FIN: SetFlags(packet, FIN); break; case PACKET_TYPE_FIN_ACK: SetFlags(packet,FIN_ACK); tcb->state=FIN_WAIT_1; break; case PACKET_TYPE_DATA: break; } SetWindow(packet,tcb->window); SetCheckSum(packet,calculate_checksum((char*)packet,len+20,srcAddr,dstAddr)); tcp_sendIpPkt(packet,len+20,tcb->srcAddr,tcb->dstAddr,255); delete[] packet; return; } int stud_tcp_socket(int domain, int type, int protocol) { printf("\nTCP SOCKET\n"); TCB * tcb= new TCB(socketfd++); tcb->next_tcb=tcb_linklist_head; tcb_linklist_head=tcb; return tcb->socketfd; } int stud_tcp_connect(int sockfd, struct sockaddr_in *addr, int addrlen) { printf("\n TCP CONNECT\n"); char buffer[BUFFER_SIZE]; TCB * tcb=searchTCB_byid(sockfd); if(tcb==NULL) return -1; tcb->srcAddr= getIpv4Address(); tcb->srcPort=gSrcPort; tcb->dstAddr=ntohl(addr->sin_addr.s_addr); tcb->dstPort=ntohs(addr->sin_port); stud_tcp_output(NULL,0,PACKET_TYPE_SYN,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr); if(waitIpPacket(buffer,TIMEOUT)==-1) return -1; if(GetFlags(buffer)!=SYN_ACK) return -1; tcb->seq=GetACK(buffer); tcb->ack=GetSeq(buffer)+1; stud_tcp_output(NULL,0,PACKET_TYPE_ACK,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr); tcb->state=ESTABLISHED; return 0; } int stud_tcp_send(int sockfd, const unsigned char *pData, unsigned short datalen, int flags) { printf("\nTCP SEND\n"); char buffer[BUFFER_SIZE]; TCB* tcb=searchTCB_byid(sockfd); if(tcb==NULL) return -1; if(tcb->state!=ESTABLISHED) return -1; stud_tcp_output((char*)pData,datalen,PACKET_TYPE_DATA,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr); if(waitIpPacket(buffer,TIMEOUT)==-1) return -1; if(GetFlags(buffer)!=ACK) return -1; tcb->seq=GetACK(buffer); tcb->ack=GetSeq(buffer)+1; return 0; } int stud_tcp_recv(int sockfd, unsigned char *pData, unsigned short datalen, int flags) { printf("\nTCP RECEIVE\n"); char buffer[BUFFER_SIZE]; int len=0; TCB *tcb=searchTCB_byid(sockfd); if(tcb==NULL) return -1; if(tcb->state!=ESTABLISHED) return -1; len=waitIpPacket(buffer,TIMEOUT); if(len==-1) return -1; int header_length=GetHeaderLength(buffer); memcpy(pData,buffer+header_length,len-header_length); tcb->seq=GetACK(buffer); tcb->ack=GetSeq(buffer)+(len-header_length); stud_tcp_output(NULL,0,PACKET_TYPE_ACK,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr); return 0; } int stud_tcp_close(int sockfd) { printf("\nTCP CLOS\n"); char buffer[BUFFER_SIZE]; TCB *pre = NULL; TCB * tcb =tcb_linklist_head; while(tcb!=NULL && tcb->socketfd!=sockfd){ pre=tcb; tcb=tcb->next_tcb; } if(tcb==NULL) return -1; if(tcb->state!=ESTABLISHED){ if(pre!=NULL){ pre->next_tcb=tcb->next_tcb; }else{ tcb_linklist_head=tcb->next_tcb; } delete tcb; return -1; } stud_tcp_output(NULL,0,PACKET_TYPE_FIN_ACK,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr); if(waitIpPacket(buffer,TIMEOUT)==-1) return -1; if(GetFlags(buffer)==ACK){ tcb->state=FIN_WAIT_2; tcb->seq=GetACK(buffer); tcb->ack=GetSeq(buffer)+1; if(waitIpPacket(buffer,TIMEOUT)==-1) return -1; if(GetFlags(buffer)==FIN_ACK){ tcb->state=TIME_WAIT; tcb->seq=GetACK(buffer); tcb->ack=GetSeq(buffer)+1; stud_tcp_output(NULL,0,PACKET_TYPE_ACK,tcb->srcPort,tcb->dstPort,tcb->srcAddr,tcb->dstAddr); }else{ return -1; } }else{ return -1; } if(pre!=NULL){ pre->next_tcb=tcb->next_tcb; }else{ tcb_linklist_head=tcb->next_tcb; } delete tcb; return 0; }
IPv4 转发实验——计算机网络概论实习作业三
IPv4转发实验实验目的本实验设计模拟实现路由器中的 IPv4 协议，可以在原有 IPv4 分组收发实验的基础上，增加 IPv4 分组的转发功能。对网络的观察视角由主机转移到路由器中，了解路由器是如何为分组选择路由，并逐跳地将分组发送到目的主机。本实验中也会初步接触路由表这一重要的数据结构，认识路由器是如何根据路由表对分组进行转发的。实验要求在前面 IPv4 分组收发实验的基础上，增加分组转发功能。具体来说，对于每一个到达本机的 IPv4 分组，根据其目的 IPv4 地址决定分组的处理行为，对该分组进行如下的几类操作:1) 向上层协议上交目的地址为本机地址的分组;2) 根据路由查找结果，丢弃查不到路由的分组;3) 根据路由查找结果，向相应接口转发不是本机接收的分组。实验内容实验内容主要包括:1) 设计路由表数据结构。2) IPv4 分组的接收和发送。3) IPv4 分组的转发。实验过程路由表维护需要完成下列分组接收处理步骤:1) stud_Route_Init()函数中，对路由表进行初始化。2) stud_route_add()函数中，完成路由的增加。转发处理流程在stud_fwd_deal()函数中，需要完成下列分组接收处理步骤:1) 查找路由表。根据相应路由表项的类型来确定下一步操作，错误分组调用函数 fwd_DiscardPkt()进行丢弃，上交分组调用接口函数 fwd_LocalRcv()提交给上层协议继续处理，转发分组进行转发处理。注意，转发分组还要从路由表项中获取下一跳的 IPv4 地址。2) 转发处理流程。对 IPv4 头部中的 TTL 字段减 1，重新计算校验和，然后调用下层接口 fwd_SendtoLower()进行发送处理。实验源码代码基于上次作业修改完成，故有较多注释#include "sysInclude.h" #include<vector> #include <iostream> // system support extern void fwd_LocalRcv(char *pBuffer, int length); extern void fwd_SendtoLower(char *pBuffer, int length, unsigned int nexthop); extern void fwd_DiscardPkt(char *pBuffer, int type); extern unsigned int getIpv4Address(); using namespace std; typedef struct { unsigned int nexthop; unsigned dest; } routeItem; vector<routeItem> myroute; int stud_fwd_deal(char * pBuffer, int length){ printf("Check IP Version:\n"); unsigned int version=(pBuffer[0]>>4); printf("IP Version is %d\n", version); // if(version!=4){ // ip_DiscardPkt(pBuffer, STUD_IP_TEST_VERSION_ERROR); // return 1; // } printf("Check Header Length:\n"); unsigned int len=(pBuffer[0]&0xF); printf("Header Length is %d\n", len); // if(len<5){ // ip_DiscardPkt(pBuffer, STUD_IP_TEST_HEADLEN_ERROR); // return 1; // } printf("Check CheckSum:\n"); unsigned int sum=0; for(int i=0;i<len*2;i++){ sum+=((unsigned short*)pBuffer)[i]; sum=(sum>>16)+(sum&0xffff); } cout<<sum<<endl<<~sum<<endl; //cout<<bitset<sizeof(short)*8>((unsigned short)sum)<<endl; //ut<<bitset<sizeof(short)*8>(~(unsigned short)sum)<<endl; printf("Check Sum is %d\n", ~sum); printf("CheckSUM is %d\n", ((unsigned short*)pBuffer)[5]); // if(((unsigned short)sum)!=0xffff){ // ip_DiscardPkt(pBuffer, STUD_IP_TEST_CHECKSUM_ERROR); // return 1; // } printf("Check Destination:\n"); unsigned int destination=ntohl(*(unsigned int*)(pBuffer+16)); unsigned int ip=getIpv4Address(); printf("destination is %d, host ip is %d\n", destination, ip); if(ip==destination){ fwd_LocalRcv(pBuffer,length); return 0; } printf("Check Time To Live:\n"); unsigned int ttl=pBuffer[8]; printf("Time To Live is %d\n", ttl); if(ttl<=0){ ip_DiscardPkt(pBuffer, STUD_FORWARD_TEST_TTLERROR); return 1; } vector<routeItem>::iterator ii; for(ii=myroute.begin();ii!=myroute.end();ii++) { if(ii->dest==destination) { char *buffer=new char[length]; memcpy(buffer,pBuffer,length); buffer[8]--; int sum=0,i=0; unsigned short Local_Checksum=0; for(;i<2*len;i++) { if(i!=5) { sum+=(buffer[2*i]<<8)+(buffer[2*i+1]); sum%=65535; } } Local_Checksum=htons(0xffff-(unsigned short)sum); memcpy(buffer+10,&Local_Checksum,2); fwd_SendtoLower(buffer,length,ii->nexthop); return 0; } } fwd_DiscardPkt(pBuffer,STUD_FORWARD_TEST_NOROUTE); return -1; // printf("Send To UP\n"); // ip_SendtoUp(pBuffer,length); // return 0; } void stud_route_add(stud_route_msg *proute){ routeItem t; t.nexthop=proute->nexthop; t.dest=ntohl(proute->dest) & (0xffffffff<<(32-ntohl(proute->masklen))); myroute.push_back(t); } void stud_Route_Init(){ myroute.clear(); }
数据库SQL上机练习1.求供应了某两种不同目录（自己指定）下的产品的供应商姓名。选择目录1和目录2select distinct companyname from Production.Suppliers where supplierid in ( select A.supplierid from Production.Products A, Production.Products B where A.supplierid = B.supplierid and A.categoryid = 1 and B.categoryid = 2 );查询结果：共4行companySupplier CIYNMSupplier GQRCVSupplier SVIYASupplier SWRXU2.求没有供应某两种不同目录（自己指定）下的产品的供应商姓名。选择目录1和目录2select distinct companyname from Production.Suppliers where supplierid not in ( select distinct A.supplierid from Production.Products A where A.categoryid = 1 or A.categoryid = 2 );查询结果：共17行，列举前5行companySupplier BWGYESupplier EQPNCSupplier ERVYZSupplier FNUXMSupplier JDNUG···3.求只向与自己位于不同城市的顾客供应产品的供应商姓名。select distinct companyname from Production.Suppliers where supplierid not in ( select distinct Suppliers.supplierid from Sales.Orders, Sales.OrderDetails, Production.Products, Production.Suppliers, Sales.Customers where Orders.orderid = OrderDetails.orderid and OrderDetails.productid = Products.productid and Products.supplierid = Suppliers.supplierid and Orders.custid = Customers.custid and Customers.city = Suppliers.city )查询结果：共26行，列举前5行companySupplier BWGYESupplier CIYNMSupplier ELCRNSupplier EQPNCSupplier ERVYZ···4.求每个城市中供应产品数量最多的供应商姓名。select distinct shipcity, companyname, sum(qty) from Production.Suppliers, Production.Products, Sales.OrderDetails, Sales.Orders A where Products.supplierid = Suppliers.supplierid and OrderDetails.productid = Products.productid and A.orderid = OrderDetails.orderid group by shipcity, Products.supplierid, companyname having sum(qty) >= all ( select sum(qty) from Production.Suppliers, Production.Products, Sales.OrderDetails, Sales.Orders where Products.supplierid = Suppliers.supplierid and OrderDetails.productid = Products.productid and OrderDetails.orderid = Orders.orderid and A.shipcity = Orders.shipcity group by Products.supplierid )查询结果：共75行，列举前5行shipcitycompanynamesum(qty)AachenSupplier OAVQT30AlbuquerqueSupplier OGLRK185AnchorageSupplier GQRCV165ÅrhusSupplier SVIYA144BarcelonaSupplier SVIYA12·········5.列出每个目录下被购买次数最多的产品。select A.categoryid, productname from Production.Categories A, Production.Products, Sales.OrderDetails where Products.categoryid = A.categoryid and OrderDetails.productid = Products.productid group by A.categoryid, Products.productid, productname having count(orderid) >= all ( select count(orderid) from Production.Categories, Production.Products, Sales.OrderDetails where Products.categoryid = Categories.categoryid and OrderDetails.productid = Products.productid and A.categoryid = Products.categoryid group by Categories.categoryid, Products.productid ) ORDER BY A.categoryid查询结果：共8行，列举前8行categoryproductname1Product QOGNU2Product LUNZZ3Product WUXYK4Product UKXRI5Product VKCMF6Product BLCAX7Product APITJ8Product TTEEX6.是否存在这样的<目录、供应商>对，该供应商供应了该目录下的所有产品？select categoryname, companyname from Production.Categories, Production.Suppliers, Production.Products A where A.supplierid = Suppliers.supplierid and A.categoryid = Categories.categoryid group by A.categoryid, A.supplierid, categoryname, companyname having count(productid) = ( select count(productid) from Production.Categories, Production.Products where Products.categoryid = Categories.categoryid and A.categoryid = Categories.categoryid )查询结果：共0行，所以并没有这样的<目录、供应商>对7.定义每个员工参与的所有订单的产品数量*产品单价*折扣率之和作为员工的业绩，列出每个城市的业绩排在前三的员工。with YJ(empid, yeji) as ( select Employees.empid, sum(unitprice * qty * (1.0 - discount)) yeji from HR.Employees, Sales.Orders, Sales.OrderDetails where Employees.empid = Orders.empid and Orders.orderid = OrderDetails.orderid group by Employees.empid ) select shipcity, Employees.empid, lastname, firstname, yeji from HR.Employees, Sales.Orders A, Sales.OrderDetails, YJ where Employees.empid = A.empid and A.orderid = OrderDetails.orderid and YJ.empid = Employees.empid group by shipcity, Employees.empid, lastname, firstname ,yeji having yeji >= any ( select yeji from ( select top 100 percent yeji, ROW_NUMBER() over (ORDER BY yeji desc) as row from (select yeji from YJ, Sales.Orders where YJ.empid = Orders.empid and A.shipcity = Orders.shipcity group by YJ.empid,yeji ) s order by yeji desc ) m where row <= 3 ) order by shipcity;查询结果：共207行，列举前6行shipcityempidlastnamefirstnameyejiAachen3LewJudy202812.8430000Aachen4PeledYael232890.8460000Aachen1DavisSara192107.6045000Albuquerque1DavisSara192107.6045000Albuquerque3LewJudy202812.8430000Albuquerque4PeledYael232890.8460000···············8.员工业绩定义同上，找出这样的员工，他的业绩好于所有雇佣日期在他之后的其他员工的业绩。with T1(empid, yeji) as ( select Employees.empid, sum(unitprice * qty * (1.0 - discount)) yeji from HR.Employees, Sales.Orders, Sales.OrderDetails where Employees.empid = Orders.empid and Orders.orderid = OrderDetails.orderid group by Employees.empid ) select distinct A.empid, lastname,firstname from T1 A,HR.Employees AA where A.empid=AA.empid and not exists( select * from T1 B, HR.Employees BB where B.yeji>A.yeji and BB.hiredate > AA.hiredate and B.empid=BB.empid ) order by empid查询结果：共3行empidlastnamefirstname4PeledYael8CameronMaria9DolgopyatovaZoya9.按时间顺序列出每位顾客购买的所有产品，要求将所有产品放在一个字段中输出。数据量小时可用declare mycursor insensitive cursor for select Orders.custid, productname from Sales.Customers, Sales.Orders, Sales.OrderDetails, Production.Products where Orders.custid = Customers.custid and Orders.orderid = OrderDetails.orderid and OrderDetails.productid = Products.productid order by Customers.custid, orderdate; open mycursor; declare @data table ( custid INT NOT NULL, productnamelist text NOT NULL ) declare @lastcustid INT, @thiscustid INT, @productname nvarchar(40), @productnamelist varchar(8000); set @lastcustid = -1; print (@@fetch_status) fetch next from mycursor into @thiscustid,@productname; print (@@fetch_status) while @@fetch_status = 0 begin if (@thiscustid = @lastcustid) begin set @productnamelist = @productnamelist + ',' + @productname; end else begin if (@lastcustid = -1) begin set @lastcustid = @thiscustid; set @productnamelist = @productname end else begin insert into @data values (@lastcustid, @productnamelist); set @lastcustid = @thiscustid; set @productnamelist = @productname; end end fetch next from mycursor into @thiscustid,@productname; if (@@fetch_status = -1) begin insert into @data values (@lastcustid, @productnamelist); end end; select * from @data; close mycursor; deallocate mycursor;declare mycursor insensitive cursor for select Orders.custid, productname from Sales.Customers, Sales.Orders, Sales.OrderDetails, Production.Products where Orders.custid = Customers.custid and Orders.orderid = OrderDetails.orderid and OrderDetails.productid = Products.productid order by Customers.custid, orderdate; open mycursor; declare @data table ( custid INT NOT NULL, productnamelist varchar(max) NOT NULL ) declare @lastcustid INT, @thiscustid INT, @productname nvarchar(40); set @lastcustid = -1; print (@@fetch_status) fetch next from mycursor into @thiscustid,@productname; print (@@fetch_status) while @@fetch_status = 0 begin if (@thiscustid = @lastcustid) begin update @data set productnamelist = productnamelist + ',' + @productname where custid = @thiscustid; end else begin insert into @data values (@thiscustid, @productname); set @lastcustid = @thiscustid; end fetch next from mycursor into @thiscustid,@productname; end; select * from @data; close mycursor; deallocate mycursor;查询结果：共89行，列举前两行和最后一行custidproductnamelist1Product CBRRL,Product LSOFL,Product OFBNT,Product ICKNK,Product IMEHJ,Product JYGFE,Product LUNZZ,Product UKXRI,Product VAIIV,Product OFBNT,Product MYMOI,Product ACRVI2Product COAXA,Product TOONT,Product RJVNM,Product WHBYK,Product PWCJB,Product NUNAW,Product POXFU,Product GEEOO,Product XKXDO,Product QMVUN······91Product XWOXC,Product ACRVI,Product HHYDP,Product WHBYK,Product RECZE,Product RECZE,Product QAQRL,Product TBTBL,Product APITJ,Product NEVTJ,Product XYZPE,Product XYZPE,Product QOGNU,Product BKAZJ,Product BWRLG,Product WUXYK