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(终端工具)下载&安装&使用教程-【CSDN】

  • 点击 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库,这个题目不用做
  • 21
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值