HNU_CA_实验4-- 字节序和分支预测

一、实验目的

  • (1)熟悉银河操作系统下 C++程序的编辑、编译和运行,理解计算机体系结关系。
  • (2)了解 MPI 分布式内存并行和 OpenMP 共享内存并行的基本特点

二、实验说明

  • 了解 VPN,远程登陆,远程文件传输;
  • 天河新一代系统上测试字节序和分预测效果;
  • 根据知识库中的指导,运行“MPI 矩阵乘编程 DEMO” 和 “OpenMP 并行演示程序”

三、实验内容

1、配置并登录天河新一代 VPN

1)使用 Easy Connect 客户端登录超算中心 VPN

  • 使用 Web 浏览器访问 https://218.77.58.134/,点击“下载客户端”按钮,下载并安装与操作系统匹配的客户端
    在这里插入图片描述

  • 打开安装好的 Easy Connect 客户端,填写地址https://218.77.58.134/ 后按回车键继续
    在这里插入图片描述

  • 分别在用户名和密码框内填入中心提供的 VPN 账号及密码,然后输入图形验证码并点击登录
    在这里插入图片描述

  • “默认资源组”显示用户可访问的系统与对应的访问 IP,如“天河新一代”、“知识库系统”等。
    在这里插入图片描述

2)使用SSH 客户端登录超算系统

使用 SSH 客户端登录系统,如下图以 MobaXterm 为例:

  • 首先安装MobaXterm,教程:MobaXterm(终端工具)下载&安装&使用教程-【优快云】

  • 点击 Session(新建会话)
    在这里插入图片描述

  • 点击 SSH、输入相应 IP地址(“默认资源组”中有相应显示)和用户名、点击 OK

    • 上面“天河新一代”的IP为25.8.100.24-25.8.100.26:组内多人的,可输入不同的IP地址(我用的25.8.100.25,另外25.8.100.24好像是不行的)。用户名输入申请的“账号名”;
      在这里插入图片描述
  • 最后在终端输入密码(账号和密码是发到组长邮箱里的):
    在这里插入图片描述

    • 点击YES,保存登录密码,下次登录就不需要再输入登录密码了:(也可以选No) 在这里插入图片描述 - 主密码用于加密所有存储的密码 在这里插入图片描述
  • 登录成功在这里插入图片描述

2、远程登录天河新一代,上传测试代码

  • 点击“↑”箭头
    在这里插入图片描述

  • 选中“字节序文件”后,即上传到远端(后面需要编译运行,需要上传.c文件;而且这里只有gcc,没有g++,所以不能编译c++文件);分支预测的代码同理
    在这里插入图片描述

    在这里插入图片描述

  • 字节序代码(指导书给的代码)

void byteorder()
{
	union
	{
		short value;
		char union_bytes[sizeof(short)];
	}test;
	test.value = 0x0102;
 
	if (sizeof(short) == 2)
	{
		if (test.union_bytes[0] == 1 && test.union_bytes[1] == 2)
			cout << "big endian" << endl;
		else if (test.union_bytes[0] == 2 && test.union_bytes[1] == 1)
			cout << "little endian" << endl;
		else
			cout << "unknown" << endl;
	}
	else
	{
		cout << "sizeof(short) == " << sizeof(short) << endl;
	}
 
	return ;
}

  • 字节序代码(修改后的:改为main函数,c形式的输入输出)
#include <stdio.h> 


// void byteorder()
int main()
{
	union
	{
		short value;
		char union_bytes[sizeof(short)];
	}test;
	test.value = 0x0102;
 
	if (sizeof(short) == 2)
	{
		if (test.union_bytes[0] == 1 && test.union_bytes[1] == 2)
			printf("big endian\n");
		else if (test.union_bytes[0] == 2 && test.union_bytes[1] == 1)
			printf("little endian\n");
		else
			printf("unknown\n");
	}
	else
	{
		printf("sizeof(short) == %ld",sizeof(short) );
	}
 
	return 0;
}
  • 分支预测代码(指导书给的代码)
#include <algorithm>
#include <ctime>
#include <iostream>

int main()
{
   // 随机产生整数,用分区函数填充,以避免出现分桶不均
   const unsigned arraySize = 32768;
   int data[arraySize];

   for (unsigned c = 0; c < arraySize; ++c)
       data[c] = std::rand() % 256;

   // !!! 排序后下面的Loop运行将更快
   std::sort(data, data + arraySize);

   // 测试部分
   clock_t start = clock();
   long long sum = 0;

   for (unsigned i = 0; i < 100000; ++i)
   {
       // 主要计算部分,选一半元素参与计算
       for (unsigned c = 0; c < arraySize; ++c)
       {
           if (data[c] >= 128)
               sum += data[c];
       }
   }

   double elapsedTime = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;

   std::cout << elapsedTime << std::endl;
   std::cout << "sum = " << sum << std::endl;
}

  • 分支预测代码(修改后的:改为c形式的输入输出;algorithm,ctime都得改成c形式的;后面要求记录两种不同情况下的运行结果,所以排序前后的结果分别输出)
#include <stdlib.h>
#include <time.h>
#include <stdio.h> 

int comp(const void*a,const void*b)
{
	return *(int*)a-*(int*)b;
}

int main()
{
    // 随机产生整数,用分区函数填充,以避免出现分桶不均
    const unsigned arraySize = 32768;
    int data[arraySize];

	unsigned c = 0;
    for (; c < arraySize; ++c)
        data[c] = rand() % 256;
        
    printf("Before sorted:\n");

    // 测试部分
    clock_t start = clock();
    long long sum = 0;

	unsigned i = 0;
    for (; i < 100000; ++i)
    {
        // 主要计算部分,选一半元素参与计算
        c = 0;
        for (; c < arraySize; ++c)
        {
            if (data[c] >= 128)
                sum += data[c];
        }
    }

    double elapsedTime = (double)(clock() - start) / CLOCKS_PER_SEC;

    printf("%f\n",elapsedTime);
    printf( "sum = %lld\n" ,sum );
    
    printf("\nAfter sorted:\n");
    
        // !!! 排序后下面的Loop运行将更快
    qsort(data, arraySize,sizeof(int),comp);


    // 测试部分
    start = clock();
    sum = 0;

	i = 0;
    for (; i < 100000; ++i)
    {
        // 主要计算部分,选一半元素参与计算
        c = 0; 
        for (; c < arraySize; ++c)
        {
            if (data[c] >= 128)
                sum += data[c];
        }
    }

    elapsedTime = (double)(clock() - start) / CLOCKS_PER_SEC;
        
    printf("%f\n",elapsedTime);
    printf( "sum = %lld\n" ,sum );    
}

3、编译两个程序

  • 配置gcc
    在这里插入图片描述

    • 命令 module av :查看系统已安装软件、模块、函数库等;
    • 命令module load :载入相应模块;
    • 命令 module li :查看已载入模块
  • 编译

    • c语言没有algorithm的sort,而是stdlib的qsort;也没有ctime,而是time.h;
      在这里插入图片描述
    • 分支预测的代码修改后通过:在这里插入图片描述

4、字节序测试

1) 运行字节序测试程序,请截图,记录程序输出

运行结果:little endian
在这里插入图片描述

2) 根据程序输出,判定字节序类型,并说明理由

字节序类型:小端;

  • 理由:测试程序的数据为0x0102;
    • 若为小端类型:则 数据低位 存储在 地址低位;而short为2字节,所以数据的低2字节【0x02】存储在地址的低位bytes[0];
    • 若为大端类型:则 数据低位 存储在 地址高位;而short为2字节,所以数据的低2字节【0x02】存储在地址的高位bytes[1];
	union
	{
		short value;
		char union_bytes[sizeof(short)];
	}test;
	test.value = 0x0102;
 
	if (sizeof(short) == 2)
	{
		if (test.union_bytes[0] == 1 && test.union_bytes[1] == 2)
			printf("big endian\n");
		else if (test.union_bytes[0] == 2 && test.union_bytes[1] == 1)
			printf("little endian\n");
		else
			printf("unknown\n");
	}
	else
	{
		printf("sizeof(short) == %ld",sizeof(short) );
	}

5、分支预测性能测试

1)运行分支预测程序,截屏、记录两种不同情况下的运行结果

运行结果:差了大概2.48倍
在这里插入图片描述

2) 请截图,展示执行完以上操作后整个 cache 系统的状态

点击“Remote monitoring”,能看到服务器的资源(CPU、RAM、Network、disk…) 使用情况在这里插入图片描述
Remote-monitoring information for session 25.8.100.25 (hnu_yy)

  • CPU:
    Current CPU load: 9%

  • RAM:
    Total RAM: 127079 MB
    Used RAM: 18657 MB
    Available RAM: 108421 MB
    Cached RAM: 2164 MB
    Buffers: 537 MB

6、 根据知识库指导,运行 MPI 并行和 OpenMP 并行程序:

  • 在 VPN 登录情况下,在浏览器中打开知识库网页; 知识库 | 国家超级计算长沙中心,进入主页。
    在这里插入图片描述

  • 点击浏览,出现主菜单:
    在这里插入图片描述

  • 点击左侧目录 HPC_demo,出现两个子目录“MPI 矩阵乘编程 DEMO”和“OpenMP 并行演示程序
    在这里插入图片描述

7、分别进入两目录,分析并行效果

分别进入“MPI 矩阵乘编程 DEMO”和“OpenMP 并行演示程序”目录,
按指南对源程序进行编辑、编译;修改脚本、提交任务;记录结果,分析并行效果。

1)MPI 矩阵乘编程 DEMO

  • 1.配置环境

    • “知识库”中的环境准备,需要gcc和openmpi:
      在这里插入图片描述
    • 使用命令module配置
      在这里插入图片描述
  • 2.上传、解压文件:上传跟上面类似,使用“↑”箭头;解压见下:tar -xvzf multi_matrix_demo.tar.gz
    在这里插入图片描述

  • 3.编译:先切换到multi_matrix_demo文件夹下,再gcc编译

    • gcc -o matrix_s matrix_s.cmpicc -o matrix_p matrix_p.c
      在这里插入图片描述
  • 4.编辑脚本

    • (1)直接运行给定的脚本会出错
      • 其实英语好的话,这里就知道因为“批处理作业提交失败:不允许用户组使用此分区”,也就不用下面的试错了。但一开始没看出来,就隐约记住了not permitted
        在这里插入图片描述
    • (2)“指南”中也说需要编辑:
      在这里插入图片描述
    • (3)去《手册》找为什么:按给的步骤需要先查看资源,再编写脚本,再用yhbatch提交任务;
      在这里插入图片描述
    • (4) yhi查看资源:
      在这里插入图片描述
      • 找不同:分区、节点数不一样;之前隐约记得有个“不许可”,所以就怀疑是分区的问题
        在这里插入图片描述
    • (5)修改脚本:把分区改成自己的分区即可(双击.sh文件,打开直接修改就可以)
      在这里插入图片描述
  • 5.运行程序
    yhbatch ./matrix_p.sh 在这里插入图片描述

  • 6.查看运行结果

    • 运行后会在当前目录输出slurm-xxxxx.out的文件,xxxx一般为jobid,可以用cat命令查看输出内容
    • 这里xxxxx就是1895361:cat slurm-1895361.out在这里插入图片描述
    • 串行运行结果:计算两个1024*1024的A,B矩阵(两个矩阵的大小需要输入运行指令后,手动输入1024 1024)乘积所费的时间为:47.205643秒;./matrix_s
      在这里插入图片描述
    • 对比
      • 串行结果47.205643,而通过mpi并行规模为12核的并行程序计算只需要8.907275秒;
      • 可以看出并行加速比为约5.30,并行计算效率为5.30/12=44%

2)OpenMP 并行演示程序

分别创建文件parallelMatrix.c,paraPi.c,fft.c,并把代码复制进去:(代码在“OpenMP 并行演示程序”目录里)
在这里插入图片描述

  • 1.parallelMatrix.c
    • 编译运行
      • gcc -g -O0 -fopenmp -std=c99 parallelMatrix.c -o gccpara.out
      • ./gccpara.out
    • 运行结果:
      在这里插入图片描述
    • 作图
      在这里插入图片描述
    • 作图程序
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
matplotlib.rc("font", family='Microsoft YaHei')


xpoints = np.array([1, 2, 4, 8,32,48,60])
# times = np.array([49.437591,25.199742,12.801155,6.695927,2.662523,2.528642,2.86362])
times = np.array([120.072972,62.014002,32.736311,16.249100,4.903782,3.590005,2.937059])

speedup = times[0] / times
print(speedup)

parallel_efficiency = speedup / xpoints
print(parallel_efficiency)

plt.title('运行统计')
plt.plot(xpoints, times,label="running time(s)",color='b')
plt.plot(xpoints, speedup,label='speedup',color='r')
plt.plot(xpoints, parallel_efficiency,label='parallel_efficiency',color='k')
plt.legend()
plt.show()

  • 2.paraPi.c

    • 编译运行
      • “知识库”中给的编译命令gcc -g -O0 -lm -fopenmp paraPi.c -o gccpara.out,但用这个会报错:
        在这里插入图片描述
      • 原因:gcc 编译错误:"undefined reference to ‘sqrt’
        • gcc默认指定的有几个库文件,比如libstd。但是不包括math的库,所需要的math库不是gcc默认指定的,所以需要手动连接math库,需要在编译的时候加上一个-lm选项。
        • -l是指定XXX库,m就指math库。即:gcc hello.c -lm -lm就是链接到math库的问题。
      • 然后试错过程中,发现把“-lm”放到最后就可以编译成功了:
        gcc -g -O0 -fopenmp paraPi.c -o gccpara.out -lm
        在这里插入图片描述

    原因说明:

    • 编译指令说明:
      • gcc: 调用 GCC 编译器。
      • -g: 生成调试信息。
      • -O0: 禁用优化(保持代码尽可能的原始形式,便于调试)。
      • -lm: 链接数学库 libm。
      • -fopenmp: 启用 OpenMP 支持,用于并行编程。
      • paraPi.c: 源文件。
      • -o gccpara.out: 指定输出的可执行文件名。
    • 错误原因:gcc -g -O0 -lm -fopenmp paraPi.c -o gccpara.out
      • 命令行参数的顺序会影响 GCC 如何处理这些参数,特别是在链接阶段。
      • -lm 出现在源文件之前时,链接器可能会找不到需要的符号,导致编译失败。
      • 正确的顺序是先指定源文件,然后指定需要的库文件,这样才能确保链接阶段能够找到并使用这些库中的符号
    • ./gccpara.out

    • 运行结果:
      在这里插入图片描述

    • 作图
      在这里插入图片描述

    • 作图程序

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
matplotlib.rc("font", family='Microsoft YaHei')


xpoints = np.array([1, 2, 4, 8,32,48,60])
# times = np.array([49.437591,25.199742,12.801155,6.695927,2.662523,2.528642,2.86362])
times = np.array([17.638941,9.835184,4.922509,2.472987,0.652800,0.468996,0.484045])

speedup = times[0] / times
print(speedup)

parallel_efficiency = speedup / xpoints
print(parallel_efficiency)

plt.title('运行统计')
plt.plot(xpoints, times,label="running time(s)",color='b')
plt.plot(xpoints, speedup,label='speedup',color='r')
plt.plot(xpoints, parallel_efficiency,label='parallel_efficiency',color='k')
plt.legend()
plt.show()
  • 3.fft.c
    • 编译运行
      • “知识库”中给的编译命令gcc fft.c -o fft.out -lfftw3_threads -lfftw3 -lm -fopenmp -O0 -g -std=c99,但用这个会报错:
        在这里插入图片描述

      • 试错:把FFTW/3.3.4 load之后,上面错误消失,出现了新的错误;
        在这里插入图片描述

      • 【失败的尝试】:Linux下编译程序/usr/bin/ld: cannot find -l*错误的解决方法

        • 创建符号链接的权限不够;在这里插入图片描述
    • 解决:助教说:“第四次实验里有一部分需要安装fftw库,这个题目不用做
### HNU 2024 春季 计算机系统 实验1 引导材料与验收标准 根据已知的信息,HNU(湖南大学)在2024年春季学期的计算机系统课程中,除了第一个实验之外,其余实验均基于卡内基梅隆大学编写的教材《CSAPP》(深入理解计算机系统)的相关配套实验[^1]。然而,关于实验1的具体引导材料验收标准并未直接提及。 通常情况下,高校开设此类基础课程时,首个实验往往用于帮助学生熟悉开发环境以及基本工具链的操作流程。对于HNU而言,可以推测实验1的主要目标可能包括但不限于以下几个方面: #### 1. **实验目的** - 帮助学生掌握计算机系统的底层原理。 - 学习并熟练使用GNU工具链(如GCC、GDB等)或其他指定开发工具。 - 提供初步实践机会,让学生了解如何编写简单的C程序及其对应的汇编代码转换过程。 #### 2. **引导材料** 虽然具体文档未被公开说明,但依据惯例,实验1可能会涉及以下内容: - C语言基础知识复习:变量声明、指针操作、数组处理等内容。 - GNU工具链介绍:安装配置指南、命令行参数详解。 - 示例代码分析:提供一段简单代码作为案例,指导学生逐步完成调试任务。 ```c // 示例代码片段 int main() { int a = 5; int b = 10; int result = add(a, b); printf("Result: %d\n", result); return 0; } int add(int x, int y) { return x + y; } ``` #### 3. **验收标准** 验收标准通常是衡量学生是否达到预期学习成果的重要指标。以下是可能的标准项: - 正确运行给定测试用例,并提交相应结果文件。 - 编写一份详细的实验报告,记录每一步骤的操作细节及遇到的问题解决方法。 - 对特定函数或模块进行性能优化尝试,并对比前后差异效果。 需要注意的是,上述内容仅为合理推断,实际要求应以学校官方发布的正式通知为准。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值