前言

操作系统课程任务 探讨 GCC for openEuler 的特性和优势

什么是 GCC for openEuler?

GCC for openEuler 基于开源 GCC-10.3 版本(GCC 官网,2021年4月发行)开发,并进行了优化和改进,实现软硬件深度协同优化,挖掘 OpenMP、SVE 向量化、数学库等领域极致性能。它是一种 Linux 下针对鲲鹏920处理器的高性能编译器。GCC for openEuler 默认使用场景为 TaiShan 服务器、鲲鹏920 处理器、Arm 架构,操作系统为 CentOS 7.6、openEuler 22.03、openEuler 20.03 等。

实验步骤

步骤1:购买并配置华为云服务器

1.1 注册华为云账号

  1. 访问华为云官网:打开浏览器,访问 华为云官网
  2. 注册账号
    • 点击页面右上角的“注册”按钮。
    • 按照提示填写必要的信息(邮箱、密码、验证码等)完成注册。
    • 可能需要验证邮箱,请按照邮件中的指示完成验证。

1.2 登录华为云控制台

  1. 登录账号:使用刚注册的账号和密码登录华为云控制台。
  2. 实名认证
    • 如果是首次登录,可能需要进行实名认证。按照提示提交相关身份证明文件,完成实名认证。

1.3 创建虚拟私有云 (VPC)

创建云服务器需要先创建虚拟私有云。

注意:区域选择华北北京四,下一步也是。

1.4 创建弹性云服务器(ECS)

  1. 进入 ECS 控制台

    • 登录后,点击页面顶部导航栏中的“产品”。
    • 在下拉菜单中选择“弹性云服务器”。
  2. 创建服务器

    • 点击“创建弹性云服务器”按钮。
  3. 选择区域和可用区

    • 区域:选择华北-北京四。
    • 可用区:选择可用区1。

  4. 选择规格

    • 根据需求选择合适的实例规格。

    • 推荐配置

      • CPU:至少4核。
      • 内存:8GB及以上。
    • 示例配置

  5. 选择镜像

    • 推荐选择市场镜像,选择 openEuler 22.03-LTS-SP2,点击选择。





  6. 存储配置

    • 根据需求选择系统盘和数据盘的大小。

    • 默认配置通常已足够,或者设置为50G。

  7. 选择网络

    • 选择已有的网络或创建新的 VPC(虚拟私有云)。

    • 虚拟私有云是前面一步创建的。

    • 确保服务器具有公网 IP 地址,以便从 Windows 远程连接。

  8. 安全组配置

    • 安全组:选择默认安全组或创建新的安全组。

    • 选择通用的安全组即可。

    • 开放端口:确保至少开放 22 端口(SSH)。

  9. 公网访问

    如图配置

  10. 云服务器管理配置密码

    • 登录方式:选择“密码登录”。

    • 设置密码:输入并确认 root 用户的密码。请记住此密码。

    • 名称自己取

  11. 确认订单并创建

    • 检查所有配置是否正确。
    • 点击“立即购买”完成服务器创建。
  12. 获取服务器信息

    • 创建完成后,在 ECS 管理列表中找到新创建的服务器。

    • 记录下服务器的 公网 IP 地址




步骤2:连接到服务器

你可以使用 PuTTYWindows 自带的 SSH 客户端 连接到你的服务器。以下以 PuTTY 为例进行说明。

2.1 下载并安装 PuTTY

  1. 下载 PuTTY

    • 访问 PuTTY 官网
    • 下载适用于 Windows 的最新版本 PuTTY 安装包。
  2. 安装 PuTTY

    • 双击下载的安装包,按照提示完成安装。

2.2 使用 PuTTY 连接服务器

  1. 打开 PuTTY

    • 安装完成后,双击桌面上的 PuTTY 图标打开程序。
  2. 配置连接

    • Host Name (or IP address):输入你的服务器公网 IP 地址(如 123.45.67.89)。
    • Port:默认是 22
    • Connection type:选择 SSH
  3. 保存会话(可选):

    • 在“Saved Sessions”字段输入一个名称,如 euler

    • 点击“Save”按钮,方便下次快速连接。

  4. 连接

    • 点击“Open”按钮。
  5. 接受安全警告

    • 第一次连接时,PuTTY 会弹出一个安全警告窗口,点击“Yes”继续。
  6. 登录

    • Login as:输入 root,按 Enter

    • Password:输入你在创建服务器时设置的密码(输入时不会显示),按 Enter

    注意:如果密码输入错误,请重新尝试或检查服务器设置。

    可能会遇到的问题

    • 没有密码登录方式

      这是因为 sshd_config 文件中的 PasswordAuthentication 设置为 no,需要修改为 yes。在华为云控制台中找到服务器,点击右侧的管理,然后点击远程登录。

      随后可以在这个页面通过密码登录:

      然后打开 /etc/ssh/sshd_config 文件,找到 PasswordAuthentication no,修改为 yes, PermitRootLogin 也修改为 yes

      vi /etc/ssh/sshd_config



      注意:按 i 进入编辑模式,找到 PasswordAuthentication no,修改为 yes,同时 PermitRootLogin 也修改为 yes,注意这里一共修改三个位置。

      修改完之后按esc退出编辑模式,输入:wq退出并保存。

      然后重启配置文件服务:

      sudo systemctl restart sshd
      service sshd restart

      之后就可以通过密码登录了。


步骤3:获取 GCC for openEuler 软件包到服务器

3.1 从本机上传

华为云镜像下载对应镜像到本地。

为了将本地的 gcc-12.3.1-2024.09-aarch64-linux.tar.gz 上传到服务器,可以使用 WinSCP 工具。

3.1.1 下载并安装 WinSCP

  1. 下载 WinSCP

    • 访问 WinSCP 官网
    • 下载最新版本的 WinSCP 安装包。
  2. 安装 WinSCP

    • 双击下载的安装包,按照提示完成安装。

3.1.2 使用 WinSCP 上传文件

  1. 打开 WinSCP

    • 安装完成后,启动 WinSCP。
  2. 配置连接

    • File protocol:选择 SFTP
    • Hostname:输入你的服务器公网 IP 地址(如 123.45.67.89)。
    • Port number:默认是 22
    • Username:输入 root
    • Password:输入你在创建服务器时设置的密码。
  3. 连接服务器

    • 点击“Login”按钮连接服务器。
    • 如果是第一次连接,可能会提示确认服务器的主机密钥,点击“是”继续。
  4. 上传文件

    • 左侧窗口:显示你本地的文件系统。

    • 右侧窗口:显示服务器的文件系统。

    • 在左侧导航到你下载的 gcc-12.3.1-2024.09-aarch64-linux.tar.gz 文件所在的本地目录。

    • 在右侧服务器端,导航到 /opt/aarch64/compiler 目录。如果该目录不存在,可以先在服务器端创建。

      创建目录

      • 在服务器端窗口中,右键点击空白区域,选择 “New” > “Directory”。
      • 输入目录名 compiler,点击 “OK”。
      • 重复上述操作在 /opt/aarch64 下创建 compiler 目录(如果尚未存在)。
    • gcc-12.3.1-2024.09-aarch64-linux.tar.gz 文件从左侧窗口拖拽到右侧窗口的 /opt/aarch64/compiler 目录中。

  5. 确认上传

    • 上传完成后,在服务器端的 /opt/aarch64/compiler 目录下应该能看到 gcc-12.3.1-2024.09-aarch64-linux.tar.gz 文件。

3.2 使用 wget 下载

3.2.1 创建目录

mkdir -p /opt/aarch64/compiler
cd /opt/aarch64/compiler

然后使用 wget 下载,所需网址来自 华为云镜像

按日期排序,选中最新的版本,然后复制链接地址,使用 wget 下载:

# 下载 gcc-12.3.1-2024.09-aarch64-linux.tar.gz,当前目录为 /opt/aarch64/compiler
wget https://mirrors.huaweicloud.com/gcc/releases/gcc-12.3.1-2024.09/gcc-12.3.1-2024.09-aarch64-linux.tar.gz -P /opt/aarch64/compiler

3.2.2 完整性校验

下载 sha256sum 文件:

复制链接地址,使用 wget 下载:

# 下载 sha256sum 文件
wget https://mirrors.huaweicloud.com/kunpeng/archive/compiler/kunpeng_gcc/gcc-12.3.1-2024.09-aarch64-linux.tar.gz.sha256

然后进行校验:

sha256sum -c gcc-12.3.1-2024.09-aarch64-linux.tar.gz.sha256

如果文件完整,会显示如下信息:

gcc-12.3.1-2024.09-aarch64-linux.tar.gz: OK

注意:如果下载的版本与示例不同,相应的文件名也要改变,这里只是一个示例。


步骤4:安装 GCC for openEuler

4.1 创建安装目录

  1. 登录到服务器

    • 使用 PuTTY 登录到你的服务器(参考步骤2)。
  2. 创建安装目录

    如果未创建安装目录,执行以下命令:

    mkdir -p /opt/aarch64/compiler
  3. 进入安装目录

    cd /opt/aarch64/compiler

4.2 将软件包上传到服务器

注意:如果已经通过 WinSCP 上传文件到 /opt/aarch64/compiler 或通过 wget 下载到该目录,可以跳过此步骤。

4.3 解压缩软件包

  1. 查看当前目录内容

    ls
    • 确认 gcc-12.3.1-2024.09-aarch64-linux.tar.gz 文件存在。
  2. 解压缩软件包

    tar -xf gcc-12.3.1-2024.09-aarch64-linux.tar.gz
    • tar:Unix 下常用的打包工具。
    • -x:解包。
    • -f:指定文件名。
  3. 查看解压后的内容

    ls
    • 应该看到一个名为 gcc-12.3.1-2024.09-aarch64-linux 的目录。

步骤5:配置环境变量

配置环境变量可以让系统识别新安装的 GCC 编译器。推荐使用编辑 /etc/profile 文件的方法,因为它更简单。

5.1 编辑 /etc/profile 文件

  1. 打开 /etc/profile 文件

    vi /etc/profile
    • vi:一种文本编辑器。
    • /etc/profile:系统级环境变量配置文件。
  2. 进入插入模式

    • 按下 i 键,进入插入模式。
  3. 添加环境变量配置

    在文件末尾添加以下内容:

    # GCC for openEuler 环境变量配置
    export PATH=/opt/aarch64/compiler/gcc-12.3.1-2024.09-aarch64-linux/bin:$PATH
    export INCLUDE=/opt/aarch64/compiler/gcc-12.3.1-2024.09-aarch64-linux/include:$INCLUDE
    export LD_LIBRARY_PATH=/opt/aarch64/compiler/gcc-12.3.1-2024.09-aarch64-linux/lib64:$LD_LIBRARY_PATH

    解释

    • PATH:告诉系统在执行命令时在哪些目录查找可执行文件。
    • INCLUDE:指定编译器的头文件目录。
    • LD_LIBRARY_PATH:指定系统在运行程序时查找共享库的路径。
    • 如果下载的版本不同,相应的路径也要改变。
  4. 保存并退出

    • 按下 Esc 键退出插入模式。
    • 输入 :wq,然后按 Enter 键保存并退出 vi 编辑器。

5.2 使环境变量生效

  1. 执行以下命令

    # 使配置生效,这个命令无需考虑当前目录在哪
    source /etc/profile
    • source:在当前 shell 中读取并执行指定文件中的命令。
    • 这样,无需重新登录,环境变量的更改立即生效。

5.3 验证环境变量配置

  1. 查看 PATH 环境变量

    echo $PATH
    • 确认输出中包含 /opt/aarch64/compiler/gcc-12.3.1-2024.09-aarch64-linux/bin
  2. 查看 INCLUDE 环境变量

    echo $INCLUDE
    • 确认输出中包含 /opt/aarch64/compiler/gcc-12.3.1-2024.09-aarch64-linux/include
  3. 查看 LD_LIBRARY_PATH 环境变量

    echo $LD_LIBRARY_PATH
    • 确认输出中包含 /opt/aarch64/compiler/gcc-12.3.1-2024.09-aarch64-linux/lib64

5.4 注意事项

  • 安装路径不同:如果安装路径与上述不同,请相应修改环境变量中的路径。

    例如,如果安装在 /usr/local/gcc-12.3.1,则配置应为:

    export PATH=/usr/local/gcc-12.3.1/bin:$PATH
    export INCLUDE=/usr/local/gcc-12.3.1/include:$INCLUDE
    export LD_LIBRARY_PATH=/usr/local/gcc-12.3.1/lib64:$LD_LIBRARY_PATH
  • 顺序重要:确保新添加的路径在 $PATH 等变量的前面,否则系统会优先使用默认路径下的 GCC 版本。


步骤6:验证安装

6.1 检查 GCC 版本

  1. 执行以下命令

    # 当前目录还是 /opt/aarch64/compiler
    gcc -v
    • 应该看到类似如下的输出:

      Using built-in specs.
      COLLECT_GCC=gcc
      COLLECT_LTO_WRAPPER=/opt/aarch64/compiler/gcc-12.3.1-2024.09-aarch64-linux/libexec/gcc/aarch64-linux-gnu/12.3.1/lto-wrapper
      Target: aarch64-linux-gnu
      Configured with: ... (相关配置)
      Thread model: posix
      Supported LTO compression algorithms: zlib zstd
      gcc version 12.3.1 (gcc for openEuler 2.3.8)

    注意

    • gcc version 行应显示你安装的 GCC for openEuler 版本信息,如 gcc for openEuler 2.3.8
    • 如果显示的是其他版本,如系统默认的 GCC,请检查环境变量配置是否正确。

6.2 解决可能的问题

  • 命令未找到

    • 确认环境变量 PATH 是否正确配置,且包含了 GCC 的 bin 目录。
    • 确认 GCC 软件包是否正确上传并解压。
  • 版本不正确

    • 确认解压的目录是否正确,且环境变量指向正确的 GCC 安装路径。
    • 重新执行 source /etc/profile,确保环境变量已生效。

目前用到的命令简要说明

以下是安装过程中用到的一些常用命令及其说明,供参考:

  • mkdir -p /path/to/directory

    • 创建一个目录,包括必要的父目录。
    • 例如:mkdir -p /opt/aarch64/compiler
  • cd /path/to/directory

    • 切换当前工作目录到指定路径。
    • 例如:cd /opt/aarch64/compiler
  • ls

    • 列出当前目录下的文件和子目录。
    • 常用选项
      • -l:长格式显示,包含详细信息。
      • -a:显示所有文件,包括隐藏文件。
  • tar -xf file.tar.gz

    • 解压缩 .tar.gz 文件。
    • -x:解包。
    • -f:指定文件名。
  • vi /etc/profile

    • 使用 vi 编辑器打开 /etc/profile 文件。
    • 基本操作
      • i:进入插入模式。
      • Esc:退出插入模式。
      • :wq:保存并退出。
      • :q!:不保存强制退出。
  • echo $VARIABLE

    • 显示环境变量 VARIABLE 的值。
    • 例如:echo $PATH
  • source /etc/profile

    • 重新加载 /etc/profile 文件,使环境变量更改立即生效。
  • gcc -v

    • 显示 GCC 的版本信息。

步骤7:测试向量化

我们需要测试由系统默认 GCC 和 GCC for openEuler 编译的程序的性能差异。这需要分别使用两个版本的 GCC 编译相同的程序,并对比运行结果。

确认是否有默认的 GCC

# 执行下载 gcc
yum install gcc -y

发现结果如下:

然后查看默认 GCC 是什么:

which gcc

发现结果如下:

这说明系统中已经安装了一个较为标准的 GCC(版本为 10.3.1),但是目前默认 GCC 版本是 GCC for openEuler(12.3.1),并且这个编译器路径指向了 /opt/aarch64/compiler/...

目标:我们需要将两个版本的 GCC 区分开,以便在实验中分别使用它们。可以通过指定不同的路径来调用系统的 GCC 10.3.1 和 GCC for openEuler 12.3.1 进行对比实验。

区分两个版本的 GCC

目前 which gcc 返回的是 /opt/aarch64/compiler/...,这意味着系统路径被 GCC for openEuler 覆盖了。需要区分两个版本的 GCC。

直接调用 GCC 的完整路径

直接使用 GCC 编译器的完整路径来调用不同的版本,确保使用的是期望的 GCC 编译器。

  1. 系统标准 GCC(版本 10.3.1):

    • 系统 GCC 通常安装在 /usr/bin/gcc,可以通过直接调用完整路径来使用:

      /usr/bin/gcc --version

      这将显示系统标准 GCC 的版本。

  2. GCC for openEuler(版本 12.3.1):

    • 已知 GCC for openEuler 的路径是 /opt/aarch64/compiler/gcc-12.3.1-2024.09-aarch64-linux/bin/gcc

    • 通过调用完整路径来使用这个版本:

      /opt/aarch64/compiler/gcc-12.3.1-2024.09-aarch64-linux/bin/gcc --version

具体实验步骤

分别使用 系统标准 GCCGCC for openEuler 来编译和运行程序,并进行对比。

1. 创建并编辑矩阵乘法程序

  1. 创建或进入工作目录

    • 路径/home/gcc_experiment

      mkdir -p /home/gcc_experiment
      cd /home/gcc_experiment
  2. 创建 C 文件

    • 文件路径/home/gcc_experiment/matrix_mul.c

      vi matrix_mul.c
    • 在编辑器中粘贴以下代码:

      #include <stdio.h>
      #include <stdlib.h>
      #include <time.h>
      #include <omp.h> void multiply(int n, double **A, double **B, double **C) {
      int i, j, k;
      #pragma omp parallel for private(i, j, k) shared(A, B, C)
      for (i = 0; i < n; i++) {
      for (j = 0; j < n; j++) {
      C[i][j] = 0.0;
      for (k = 0; k < n; k++) {
      C[i][j] += A[i][k] * B[k][j];
      }
      }
      }
      } int main() {
      int i, j, n;
      double **A, **B, **C;
      clock_t start, end; for (n = 100; n <= 1500; n += 100) {
      // 动态分配内存
      A = (double **)malloc(n * sizeof(double *));
      B = (double **)malloc(n * sizeof(double *));
      C = (double **)malloc(n * sizeof(double *));
      for (i = 0; i < n; i++) {
      A[i] = (double *)malloc(n * sizeof(double));
      B[i] = (double *)malloc(n * sizeof(double));
      C[i] = (double *)malloc(n * sizeof(double));
      } // 初始化矩阵
      for (i = 0; i < n; i++) {
      for (j = 0; j < n; j++) {
      A[i][j] = rand() / (double)RAND_MAX;
      B[i][j] = rand() / (double)RAND_MAX;
      }
      } // 矩阵乘法
      start = clock();
      multiply(n, A, B, C);
      end = clock(); printf("Matrix size %d: %f seconds\n", n, ((double)(end - start)) / CLOCKS_PER_SEC); // 释放内存
      for (i = 0; i < n; i++) {
      free(A[i]);
      free(B[i]);
      free(C[i]);
      }
      free(A);
      free(B);
      free(C);
      } return 0;
      }
    • 保存并退出(Esc -> :wq)。

2. 使用系统 GCC(10.3.1)进行编译和运行

  1. 编译程序

    • 路径:仍然在 /home/gcc_experiment

      /usr/bin/gcc matrix_mul.c -o matrix_mul_default -fopenmp
    • 这个命令使用系统默认的 GCC(10.3.1)进行编译,生成可执行文件 matrix_mul_default

  2. 运行程序并记录结果

    ./matrix_mul_default > default_results.txt
    • 该命令将运行结果保存到文件 default_results.txt 中。

3. 使用 GCC for openEuler(12.3.1)进行编译和运行

  1. 编译程序

    • 路径:继续在 /home/gcc_experiment

      /opt/aarch64/compiler/gcc-12.3.1-2024.09-aarch64-linux/bin/gcc matrix_mul.c -o matrix_mul_optimized -fopenmp -O3
    • 使用 GCC for openEuler(12.3.1)进行编译,并且使用 -O3 进行优化。

  2. 运行程序并记录结果

    ./matrix_mul_optimized > optimized_results.txt
    • 运行结果保存到 optimized_results.txt 中。

多次测试取平均值

# euler 开启向量化:-ftree-vectorize
/root@ecs-euler gcc_experiment# /opt/aarch64/compiler/gcc-12.3.1-2024.09-aarch64-linux/bin/gcc matrix_mul.c -o matrix_mul_euler_ftree_vectorize -fopenmp -O3 -ftree-vectorize
/root@ecs-euler gcc_experiment# ./matrix_mul_euler_ftree_vectorize > matrix_mul_euler_ftree_vectorize_1.txt
/root@ecs-euler gcc_experiment# ./matrix_mul_euler_ftree_vectorize > matrix_mul_euler_ftree_vectorize_2.txt
/root@ecs-euler gcc_experiment# ./matrix_mul_euler_ftree_vectorize > matrix_mul_euler_ftree_vectorize_3.txt # euler 不开启向量化
/root@ecs-euler gcc_experiment# /opt/aarch64/compiler/gcc-12.3.1-2024.09-aarch64-linux/bin/gcc matrix_mul.c -o matrix_mul_euler_no_ftree_vectorize -fopenmp -O3
/root@ecs-euler gcc_experiment# ./matrix_mul_euler_no_ftree_vectorize > matrix_mul_euler_no_ftree_vectorize_1.txt
/root@ecs-euler gcc_experiment# ./matrix_mul_euler_no_ftree_vectorize > matrix_mul_euler_no_ftree_vectorize_2.txt
/root@ecs-euler gcc_experiment# ./matrix_mul_euler_no_ftree_vectorize > matrix_mul_euler_no_ftree_vectorize_3.txt # 默认 gcc
/root@ecs-euler gcc_experiment# /usr/bin/gcc matrix_mul.c -o matrix_mul_default -fopenmp
/root@ecs-euler gcc_experiment# ./matrix_mul_default > matrix_mul_default_1.txt
/root@ecs-euler gcc_experiment# ./matrix_mul_default > matrix_mul_default_2.txt
/root@ecs-euler gcc_experiment# ./matrix_mul_default > matrix_mul_default_3.txt # euler 针对鲲鹏芯片调整,开启向量化
/root@ecs-euler gcc_experiment# /opt/aarch64/compiler/gcc-12.3.1-2024.09-aarch64-linux/bin/gcc matrix_mul.c -o matrix_mul_euler_kunpeng_ftree_vectorize -fopenmp -O3 -ftree-vectorize -mcpu=thunderx2t99
/root@ecs-euler gcc_experiment# ./matrix_mul_euler_kunpeng_ftree_vectorize > matrix_mul_euler_kunpeng_ftree_vectorize_1.txt
/root@ecs-euler gcc_experiment# ./matrix_mul_euler_kunpeng_ftree_vectorize > matrix_mul_euler_kunpeng_ftree_vectorize_2.txt
/root@ecs-euler gcc_experiment# ./matrix_mul_euler_kunpeng_ftree_vectorize > matrix_mul_euler_kunpeng_ftree_vectorize_3.txt

数据导出和分析

  1. 使用 WinSCP 将结果文件下载到本地

  2. 本地绘图示例

    可以看出,GCC for openEuler 在针对鲲鹏芯片的优化上,以及在向量化优化上的效果,进而为后续的性能优化提供参考。

总结

通过实验,我们可以验证 GCC for openEuler 在针对鲲鹏芯片的优化上,以及在向量化优化上的效果,进而为后续的性能优化提供参考。

接下来,我们将继续测试其他三个编译优化指令,以及评估 GCC for openEuler 在特定优化场景下的性能提升。


步骤8:测试其他三个编译优化指令

有了前面测试向量化效果的基础,这部分详细介绍如何通过另外三个不同的实验,评估 GCC for openEuler 相较于系统默认 GCC 在特定优化场景下的性能提升。每个实验针对不同的应用场景,使用相应的编译优化选项,通过编写测试程序、编译运行、记录执行时间,并最终绘制对比图,直观展示优化效果。


测试环境

在开始实验前,请确保以下环境配置正确,这些配置由前面的步骤提供:

  • 硬件:配备 Kunpeng 920 处理器的服务器或工作站。
  • 操作系统:最新版本的 openEuler
  • 编译器
    • GCC for openEuler:安装路径 /opt/aarch64/compiler/gcc-12.3.1-2024.09-aarch64-linux/bin/gcc
    • 系统默认 GCC:安装路径 /usr/bin/gcc(版本 10.3.1)
  • 工具
    • Python:用于绘图,建议安装 matplotlibpandas 库。

      pip install matplotlib pandas
    • 文件传输工具:如 WinSCP,用于下载结果文件到本地计算机。


实验概述

本部分包含三个部分,每部分针对不同的性能优化场景:

  1. 动态寻址(Dynamic Addressing)
  2. 浮点优化(Float Optimization)
  3. 内存优化(Memory Optimization)

每个实验包括以下步骤:

  • 编写测试程序
  • 使用两种编译器编译程序,应用相应的优化选项
  • 运行程序,记录执行时间
  • 收集结果并进行对比分析

1. 动态寻址

场景

适用于需要处理大规模数组或数据结构的应用,如科学计算、数据分析和机器学习等领域。

优化选项

  • -mcmodel=large:允许程序访问更大的数据段,适合处理超过默认模型限制的大数组。

目标

评估使用 -mcmodel=large 选项后,程序在处理大数组时的性能改进,具体体现在执行时间的减少上。

代码说明

该程序动态分配一个大数组,初始化数组元素后计算数组元素的和,并记录执行时间。

实验步骤

  1. 创建测试程序

    创建文件 dynamic_addressing.c

    vi dynamic_addressing.c

    粘贴以下代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h> int main(int argc, char *argv[]) {
    if(argc != 2) {
    printf("用法: %s <N>\n", argv[0]);
    return 1;
    } long N = atol(argv[1]);
    double *array = malloc(N * sizeof(double));
    if(!array) {
    perror("内存分配失败");
    return 1;
    } // 初始化数组
    for(long i = 0; i < N; i++) {
    array[i] = (double)i * 0.1;
    } clock_t start = clock();
    // 简单数组求和
    double sum = 0.0;
    for(long i = 0; i < N; i++) {
    sum += array[i];
    }
    clock_t end = clock(); double time_spent = (double)(end - start) / CLOCKS_PER_SEC;
    printf("%ld,%f\n", N, time_spent); free(array);
    return 0;
    }
  2. 创建 CSV 文件头

    # 路径:/home/gcc_experiment
    echo "N,Execution Time (seconds)" > dynamic_addressing_default.csv
    echo "N,Execution Time (seconds)" > dynamic_addressing_openEuler.csv

2. 浮点优化

场景

适用于需要进行大量浮点运算的应用,如图形处理、物理模拟和机器学习算法,尤其在速度优先而非精度优先的情况下。

优化选项

  • -ffast-math:启用激进的浮点优化,允许编译器重新排序浮点运算、忽略某些精度保证等,以提高计算速度。

目标

评估启用 -ffast-math 后,程序在浮点运算中的性能提升,通过记录执行时间变化来反映优化效果。

代码说明

该程序初始化两个浮点数组,计算它们的和并累加,记录执行时间。

实验步骤

  1. 创建测试程序

    创建文件 float_optimization.c

    vi float_optimization.c

    粘贴以下代码:

    #include <stdio.h>
    #include <math.h>
    #include <stdlib.h>
    #include <time.h> int main(int argc, char *argv[]) {
    if(argc != 2) {
    printf("用法: %s <N>\n", argv[0]);
    return 1;
    } long N = atol(argv[1]);
    double *a = malloc(N * sizeof(double));
    double *b = malloc(N * sizeof(double));
    double *c = malloc(N * sizeof(double));
    if(!a || !b || !c) {
    perror("内存分配失败");
    return 1;
    } // 初始化数组
    for(long i = 0; i < N; i++) {
    a[i] = sin((double)i) * sin((double)i);
    b[i] = cos((double)i) * cos((double)i);
    } clock_t start = clock();
    // 点积运算
    double sum = 0.0;
    for(long i = 0; i < N; i++) {
    c[i] = a[i] + b[i];
    sum += c[i];
    }
    clock_t end = clock(); double time_spent = (double)(end - start) / CLOCKS_PER_SEC;
    printf("%ld,%f\n", N, time_spent); free(a);
    free(b);
    free(c);
    return 0;
    }
  2. 创建 CSV 文件头

    echo "N,Execution Time (seconds)" > float_optimization_default.csv
    echo "N,Execution Time (seconds)" > float_optimization_openEuler.csv

3. 内存优化

场景

适用于对内存使用敏感的应用,如嵌入式系统、低功耗设备或需要在有限资源上运行的程序,评估编译器在内存管理方面的优化效果。

优化选项

  • -fomit-frame-pointer:指示编译器在生成代码时省略帧指针,从而减少内存占用并可能提升执行速度。

目标

评估该优化选项对程序性能的影响,特别是在需要频繁分配和释放内存的场景中,通过记录执行时间来判断优化效果。

代码说明

该程序动态分配一个整型数组,初始化数组元素后进行简单的冒泡排序,并记录执行时间。

实验步骤

  1. 创建测试程序

    创建文件 memory_optimization.c

    vi memory_optimization.c

    粘贴以下代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h> int main(int argc, char *argv[]) {
    if(argc != 2) {
    printf("用法: %s <N>\n", argv[0]);
    return 1;
    } long N = atol(argv[1]);
    int *array = malloc(N * sizeof(int));
    if(!array) {
    perror("内存分配失败");
    return 1;
    } // 初始化数组
    for(long i = 0; i < N; i++) {
    array[i] = rand();
    } clock_t start = clock();
    // 简单排序(冒泡排序,适用于较小的 N)
    for(long i = 0; i < N; i++) {
    for(long j = 0; j < N - i - 1; j++) {
    if(array[j] > array[j + 1]) {
    int temp = array[j];
    array[j] = array[j + 1];
    array[j + 1] = temp;
    }
    }
    }
    clock_t end = clock(); double time_spent = (double)(end - start) / CLOCKS_PER_SEC;
    printf("%ld,%f\n", N, time_spent); free(array);
    return 0;
    }
  2. 创建 CSV 文件头

    echo "N,Execution Time (seconds)" > memory_optimization_default.csv
    echo "N,Execution Time (seconds)" > memory_optimization_openEuler.csv

    注意

    由于冒泡排序在大数据集上的效率极低,建议将 N 的最大值限制在 15000 以内,以确保合理的执行时间。


自动化编译与运行脚本

测试过程与测试向量化没有太大差别,为了简化实验过程,创建一个脚本 run_all_tests.sh,自动完成编译、运行以及结果记录的步骤。

创建脚本文件

  1. 创建脚本文件

    vi run_all_tests.sh
  2. 粘贴以下内容

    #!/bin/bash
    
    # 定义编译器路径
    GCC_DEFAULT="/usr/bin/gcc"
    GCC_OPTIMIZED="/opt/aarch64/compiler/gcc-12.3.1-2024.09-aarch64-linux/bin/gcc" # 定义测试文件和对应的 CSV 文件
    declare -A TESTS
    TESTS=(
    ["dynamic_addressing"]="dynamic_addressing"
    ["float_optimization"]="float_optimization"
    ["memory_optimization"]="memory_optimization"
    ) # 定义 N 值的范围和步长
    declare -A N_VALUES
    N_VALUES=(
    ["dynamic_addressing"]="1000000 3000000 5000000 7500000 10000000 12000000 15000000 200000000"
    ["float_optimization"]="1000000 1500000 2000000 3000000 5000000 7500000 10000000"
    ["memory_optimization"]="10000 15000 20000 35000 50000 75000 100000"
    ) # 清理旧结果并添加表头
    for test in "${!TESTS[@]}"; do
    default_csv="${TESTS[$test]}_default.csv"
    optimized_csv="${TESTS[$test]}_openEuler.csv"
    echo "N,Execution Time (seconds)" > "$default_csv"
    echo "N,Execution Time (seconds)" > "$optimized_csv"
    done # 运行每个测试
    for test in "${!TESTS[@]}"; do
    default_csv="${TESTS[$test]}_default.csv"
    optimized_csv="${TESTS[$test]}_openEuler.csv"
    echo "Running test: $test" for N in ${N_VALUES[$test]}; do
    echo "Testing N = $N" # 编译并运行 Default GCC
    if [ "$test" == "dynamic_addressing" ]; then
    $GCC_DEFAULT ${test}.c -o ${test}_default -O3 -mcmodel=large
    elif [ "$test" == "float_optimization" ]; then
    $GCC_DEFAULT ${test}.c -o ${test}_default -O3 -ffast-math -lm
    elif [ "$test" == "memory_optimization" ]; then
    $GCC_DEFAULT ${test}.c -o ${test}_default -O3 -fomit-frame-pointer
    fi if [ $? -ne 0 ]; then
    echo "Compilation failed for $test with Default GCC on N=$N"
    continue
    fi # 运行 Default GCC 编译的程序并记录结果
    ./$(basename ${test}_default) $N >> "$default_csv" # 编译并运行 GCC for openEuler
    if [ "$test" == "dynamic_addressing" ]; then
    $GCC_OPTIMIZED ${test}.c -o ${test}_optimized -O3 -mcmodel=large
    elif [ "$test" == "float_optimization" ]; then
    $GCC_OPTIMIZED ${test}.c -o ${test}_optimized -O3 -ffast-math -lm
    elif [ "$test" == "memory_optimization" ]; then
    $GCC_OPTIMIZED ${test}.c -o ${test}_optimized -O3 -fomit-frame-pointer
    fi if [ $? -ne 0 ]; then
    echo "Compilation failed for $test with GCC for openEuler on N=$N"
    continue
    fi # 运行 GCC for openEuler 编译的程序并记录结果
    ./$(basename ${test}_optimized) $N >> "$optimized_csv"
    done
    done echo "All tests completed."
  3. 保存并退出

    vi 编辑器中,按 Esc 键,输入 :wq,然后按 Enter 键。

  4. 赋予脚本执行权限

    chmod +x run_all_tests.sh
  5. 运行脚本

    # 路径:/home/gcc_experiment
    ./run_all_tests.sh

    脚本说明

    • 编译器路径:指定系统默认 GCC 和 GCC for openEuler 的路径。
    • 测试定义:使用关联数组定义每个测试的名称和对应的 C 文件。
    • N 值定义:为每个测试定义适当的输入规模范围。
    • CSV 初始化:为每个测试和编译器组合创建 CSV 文件,并添加表头。
    • 编译与运行
      • 根据测试类型,应用相应的编译选项。
      • 编译失败时,输出错误信息并跳过当前测试。
      • 运行编译后的程序,将输出结果(N 值和执行时间)追加到对应的 CSV 文件中。
    • 完成提示:测试全部完成后,输出提示信息。

结果收集与绘图

完成实验后,将生成六个 CSV 文件,分别对应三个测试在系统默认 GCC 和 GCC for openEuler 下的执行时间。接下来,通过 Python 脚本将结果可视化。

使用 WinSCP 或其他文件传输工具,将以下 CSV 文件下载到本地计算机:

  • dynamic_addressing_default.csv
  • dynamic_addressing_openEuler.csv
  • float_optimization_default.csv
  • float_optimization_openEuler.csv
  • memory_optimization_default.csv
  • memory_optimization_openEuler.csv

示例图表

  1. 动态寻址性能对比

  2. 浮点优化性能对比

  3. 内存优化性能对比


本部分结论

通过上述三个实验,可以评估 GCC for openEuler 在三个优化场景下相较于系统默认 GCC 的性能提升:

  1. 动态寻址

    • 场景:处理大规模数据集,如科学计算和数据分析。
    • 优化指令-mcmodel=large
    • 结果GCC for openEuler 在处理更大规模的数据段时表现更高效,执行时间更短。
  2. 浮点优化

    • 场景:需要大量浮点运算的应用,如图形处理和物理模拟。
    • 优化指令-ffast-math
    • 结果:启用浮点优化后,GCC for openEuler 显著提高浮点运算速度,执行时间明显减少。
  3. 内存优化

    • 场景:内存使用敏感的应用,如嵌入式系统和低功耗设备。
    • 优化指令-fomit-frame-pointer
    • 结果GCC for openEuler 通过省略帧指针减少内存占用,提高程序整体执行速度。

综合结论

GCC for openEuler 在多种优化选项下展现出显著的性能优势,特别是在大规模数据处理、浮点运算优化和内存管理方面。这使其成为在特定硬件和操作系统环境下,追求高性能应用的理想编译器选择。


步骤8 故障排除

在实验过程中,可能会遇到以下常见问题及其解决方法:

1. 编译错误:undefined reference to 'sincos'

问题描述

编译浮点优化实验程序时,出现链接错误,提示 sincos 函数未定义。

解决方法

确保在编译时链接数学库,使用 -lm 选项。例如:

gcc float_optimization.c -o float_optimization_default -O3 -ffast-math -lm

2. 编译错误:gcc: error: unrecognized argument ‘-mcmodel=large’

问题描述

编译动态寻址实验程序时,出现错误提示 -mcmodel=large 选项未被识别。

解决方法

  • 确认编译器版本:确保使用的是支持 -mcmodel=large 选项的 GCC 版本。
  • 检查编译器路径:使用正确路径下的 GCC for openEuler
  • 查看 GCC 文档:参考当前 GCC 版本的文档,确认是否支持该选项。

3. 执行错误:./dynamic_addressing_openEuler: No such file or directory

问题描述

运行编译后的程序时,提示找不到可执行文件。

解决方法

  • 确认编译成功:检查编译过程是否有错误,确保生成了可执行文件。

  • 检查文件权限:确保可执行文件具有执行权限。

    chmod +x dynamic_addressing_openEuler
  • 确认文件路径:在正确的目录下运行程序。

4. 程序运行时间异常长

问题描述

内存优化实验中的冒泡排序在大数据集上运行时间极长。

解决方法

  • 限制 N 值:将 N 的最大值设置在合理范围内(如15000)。
  • 优化算法:考虑使用更高效的排序算法(如快速排序),以减少执行时间。

整个实验的总结与不足

通过实验,测试了 GCC for openEuler 的特性和优势,但是有一点不足是在测试向量化时,默认的 GCC -O3 优化会出奇地快,有待进一步研究。


参考资料


在华为云服务器上测试GCC for OpenEuler的特性的更多相关文章

  1. 基于Celery在多台云服务器上实现分布式

    起源 最近参加公司里的一个比赛,比赛内容里有一项是尽量使用分布式实现项目.因为项目最终会跑在jetsonnano,一个贼卡的开发板,性能及其垃圾.而且要求使用python? 找了很多博客,讲的真的是模 ...

  2. springboot +jsp项目打包部署到华为云服务器

    注:打包之前先保证你的项目本地运行没问题. 一.打包 打包有两种方式,打成jar包和打成war包.因为springboot有内置的服务器,所以选择打成jar包,这样云服务器就不用装tomcat了. 打 ...

  3. 华为云服务器搭建FTP后,内网访问无法连接。

    总结:1.内网访问公网需要用被动模式 2.云服务器需要放开除20.21以外的部分端口,指定范围 3.PASV IP配置为云服务器的弹性公网IP 4.局域网使用主动模式连接FTP,客户端需要关闭防火墙, ...

  4. 阿里云服务器上使用iptables设置安全策略

    转自:http://www.netingcn.com/aliyun-iptables.html 公司的产品一直运行在云服务器上,从而有幸接触过aws的ec2,盛大的云服务器,最近准备有使用阿里云的弹性 ...

  5. jdbc连接阿里云服务器上的MySQL数据库 及 数据库IP限制

    问题1:Jdbc 如何连接阿里云服务器上的MySQL数据库? 解决: 上截图: 其中IP是阿里云服务器的公网IP地址. 问题2:   刚开始接手开发的时候,使用Navicat连接阿里云服务器上的数据后 ...

  6. 使用Navicat连接阿里云服务器上的MySQL数据库--转

    手把手教你如何正确连接阿里云服务器上的数据库: 1.首先打开Navicat,文件>新建连接>MySQL连接,其他的如一图所示. 2.因为是连接服务器上的MySQL,所以我们使用SSH连接, ...

  7. 在centos 7云服务器上搭建Apache服务器并访问到你的网站

    网站是指在互联网上根据一定的规则,用HTML等语言制作的网页的集合.网站的目的是用来展示一些信息,如果是个人网站则是为了展示自己的一些想被人知道的东西,例如自己的一些作品,又或者是通过网站来达到盈利的 ...

  8. 华为云服务器为Tomcat配置SSL

    近期由于开发小程序需要在云服务器上配置https访问协议,也遇到了一点小问题,把配置过程记录一下:SSL 证书申请下来之后会有 .jks .crt .pfx .pem为后缀的文件(如何申请SSL证书这 ...

  9. 阿里云服务器上通过Docker部署redmine

    背景:在日常工作的过程中会遇到各种各样的问题,每个问题来了之后需要花时间解决.这里就面临两个问题. 1:问题责任不明确,有时候会遇到数据库或者物理服务器的问题,这时候就需要把相应问题指派给相应的人,传 ...

  10. Linux学习2-在阿里云服务器上部署禅道环境

    前言 以前出去面试总会被问到:测试环境怎么搭建?刚工作1-2年不会搭建测试环境还可以原谅自己,工作3-5年后如果还是对测试环境搭建一无所知,面试官会一脸的鄙视. 本篇以最简单的禅道环境搭建为例,学习下 ...

随机推荐

  1. 【Java】操作数据库

    工具: eclipse MySQL Navicat for MySQL MySQL 连接驱动:mysql-connector-java-5.0.4-bin.jar SQL 代码 CREATE TABL ...

  2. leetcode每日一题:转换二维数组

    题目 2610. 转换二维数组 给你一个整数数组 nums .请你创建一个满足以下条件的二维数组: 二维数组应该 只 包含数组 nums 中的元素. 二维数组中的每一行都包含 不同 的整数. 二维数组 ...

  3. 调用dll中form,太古老了,可是

    太古老了,可是用的不多.应该考虑商品化项目首选. library Prj_dll; { Important note about DLL memory management: ShareMem mus ...

  4. nodejs读写json文件

    读json文件 'use strict'; const fs = require('fs'); let rawdata = fs.readFileSync('student.json'); let s ...

  5. iOS Facebook和Google登录

    前言 最近在对接完Google和Facebook登录之后准备对这部分内容做一个小小的总结,方便以后有需要的时候查看. 具体的Google账号申请和Facebook账号的申请在这里就不做介绍了,这部分内 ...

  6. Rust+appium App自动化测试demo

    1.新建工程 打开RustCover,新建工程如下: 修改Cargo.toml文件如下: [package] name = "test_demo" version = " ...

  7. Clion搭建C++开发环境

    1.下载和安装MinGW 1)下载链接:http://www.mingw.org/ 2)选择安装目录,目录尽可能简单(如:D:\MinGW)且不要包含中文和空格 3)添加相关的包 所需的包如下:min ...

  8. 🎀chrome-网页gif截图插件

    简介 本文介绍网页中gif截图工具使用,便于日常对网页中动态效果或元素进行截图 软件介绍 Capture to a Gif 是用来录制屏幕并将其保存为 GIF 格式文件的chrome插件工具.它允许用 ...

  9. 为了掌握设计模式,开发了一款Markdown 文本编辑器软件(已开源)

    设计模式实战项目:Markdown 文本编辑器软件开发(已开源) 一.项目简介 项目名称:YtyMark-java 本项目是一款基于 Java 语言 和 JavaFX 图形界面框架 开发的 Markd ...

  10. kettle介绍-Step之REST Client

    REST Client介绍 REST 客户端转换步骤可以消费 RESTful 服务.RESTful 是一种网络应用程序的设计风格和开发方式,基于 HTTP,可以使用 XML 格式定义或 JSON 格式 ...