计算机组成原理(计算机系统3)--实验八:处理器结构拓展实验

一、实验目标:

  1. 了解MIPS的五级流水线,和在运行过程中的所产生的各种不同的流水线冒险;
  2. 通过指令顺序调整,或旁路与预测技术来提高流水线效率;
  3. 更加了解流水线细节和其指令的改善方法;
  4. 更加深入了解动态分支预测和BTB
  5. 更加熟悉MIPS指令的使用。

 二、实验内容

  1. 处理器结构实验一的扩展:用perf记录x86中的数据相关于指令序列调整前后的事件统计(stall、CPU cycles等)
  2. 处理器结构实验二的扩展:在x86系统上编写C语言的矩阵乘法代码,用perf观察分支预测失败次数,分析其次数是否与你所学知识吻合。再编写前面第二部使用的令分支预测失败的代码,验证x86是否能正确预测,并尝试做解释

 三、实验环境

硬件:桌面PC

软件:Linux

四、处理器结构实验一扩展

perf环境配置

(1)先在终端中输入 sudo apt update 。

(2)然后再输入 sudo apt install linux-tools-5.15.0-124-generic。

(3)最后使用 perf --version查看是否安装成功,如下图所示,证明安装成功。

五、处理器结构实验一作业二的除法:

优化前:

(1)将mips汇编指令转为x86汇编语言,即为exp4.asm,代码如下。

section .data

    a dd 12        ; 定义整数变量 a,初始值为 12

    b dd 3         ; 定义整数变量 b,初始值为 3

    c dd 15        ; 定义整数变量 c,初始值为 15

    d dd 5         ; 定义整数变量 d,初始值为 5

    e dd 1         ; 定义整数变量 e,初始值为 1

    f dd 2         ; 定义整数变量 f,初始值为 2

    g dd 3         ; 定义整数变量 g,初始值为 3

    h dd 4         ; 定义整数变量 h,初始值为 4

    i dd 5         ; 定义整数变量 i,初始值为 5

    j dd 6         ; 定义整数变量 j,初始值为 6

section .text

    global _start

_start:

    ; 加载变量到寄存器

    mov eax, [a]

    mov ebx, [b]

    mov ecx, [c]

    mov edx, [d]

    mov esi, [e]

    mov edi, [f]

    mov ebp, [g]

    mov r8d, [h]

    mov r9d, [i]

    mov r10d, [j]

    ; 执行除法 a = a / b

    xor edx, edx        ; 清除 edx,因为除法会使用 edx:eax

    div ebx             ; eax = eax / ebx (a / b)

    mov [a], eax        ; 将结果存回 a

    ; 执行除法 c = c / d

    mov eax, ecx        ; eax = c

    xor edx, edx        ; 清除 edx

    div edx             ; eax = eax / edx (c / d)

    mov [c], eax        ; 将结果存回 c

    ; 自增操作 e, f, g, h, i, j

    inc esi             ; e = e + 1

    inc edi             ; f = f + 1

    inc ebp             ; g = g + 1

    inc r8d             ; h = h + 1

    inc r9d             ; i = i + 1

    inc r10d            ; j = j + 1

    ; 存储回内存

    mov [e], esi

    mov [f], edi

    mov [g], ebp

    mov [h], r8d

    mov [i], r9d

    mov [j], r10d

    ; 程序结束

    mov eax, 60         ; syscall: exit

    xor edi, edi        ; exit code 0

    syscall

(2)使用nasm和ld指令进行编译和链接。

  1. nasm -f elf64 exp4.asm -o exp4.o
  2. ld exp4.o -o exp4

(3)运行程序并使用 perf 统计性能。

sudo perf stat ./exp4

 Performance counter stats for './exp4':

             0.56 msec task-clock                #    0.328 CPUs utilized          

                0     context-switches          #  0.000 K/sec                   

                0     cpu-migrations            #  0.000 K/sec                   

               42     page-faults               #   0.989 K/sec                  

      13,630,842     cycles                    #    1.228 GHz                      (15.37%)

         130,842     stalled-cycles-frontend                                       (19.56%)

         100,842     stalled-cycles-backend    #    9.20% backend cycles idle    

       1,031,525     instructions              #    0.90  insn per cycle         

         199,920     branches                  #    335.702 /sec                     (84.63%)

          8,666      branch-misses             #    6.20% of all branches          (50.44%)

       0.014268276 seconds time elapsed

       0.000889020 seconds user

       0.000000000 seconds sys

优化后:

(1)将mips汇编指令转为x86汇编语言,即为exp5.asm,代码如下。

section .data

    a dd 12        ; 定义整数变量 a,初始值为 12

    b dd 3         ; 定义整数变量 b,初始值为 3

    c dd 15        ; 定义整数变量 c,初始值为 15

    d dd 5         ; 定义整数变量 d,初始值为 5

    e dd 1         ; 定义整数变量 e,初始值为 1

    f dd 2         ; 定义整数变量 f,初始值为 2

    g dd 3         ; 定义整数变量 g,初始值为 3

    h dd 4         ; 定义整数变量 h,初始值为 4

    i dd 5         ; 定义整数变量 i,初始值为 5

    j dd 6         ; 定义整数变量 j,初始值为 6

section .text

    global _start

_start:

    ; 加载变量到寄存器

    mov eax, [a]

    mov ebx, [b]

    div ebx             ; eax = a / b, 存储在 eax

    mov [a], eax        ; 保存结果 a

    mov eax, [c]

    mov ebx, [d]

    ; 先处理非依赖性操作,确保第二个除法后续执行

    mov ecx, [e]

    add ecx, 1          ; e = e + 1

    mov [e], ecx

    mov ecx, [f]

    add ecx, 1          ; f = f + 1

    mov [f], ecx

    mov ecx, [g]

    add ecx, 1          ; g = g + 1

    mov [g], ecx

    mov ecx, [h]

    add ecx, 1          ; h = h + 1

    mov [h], ecx

    mov ecx, [i]

    add ecx, 1          ; i = i + 1

    mov [i], ecx

    mov ecx, [j]

    add ecx, 1          ; j = j + 1

    mov [j], ecx

    ; 执行第二个除法

    div ebx             ; eax = c / d, 存储在 eax

    mov [c], eax        ; 保存结果 c

    ; 程序结束

    mov eax, 60         ; syscall: exit

    xor edi, edi        ; exit code 0

    syscall

(2)使用nasm和ld指令进行编译和链接。

  1. nasm -f elf64 exp5.asm -o exp5.o
  2. ld exp5.o -o exp5

(3)运行程序并使用 perf 统计性能。

sudo perf stat ./exp5

 Performance counter stats for './exp5':

             0.26 msec task-clock                #    0.328 CPUs utilized          

                3     context-switches          #  0.012 K/sec                   

                0     cpu-migrations            #  0.000 K/sec                   

                4     page-faults               #   0.019 K/sec                  

         530,242     cycles                    #    1.003 GHz                      (12.33%)

          60,802     stalled-cycles-frontend                                       (7.56%)

          20,701     stalled-cycles-backend    #    3.20% backend cycles idle    

         331,520     instructions              #    0.50  insn per cycle         

          29,921     branches                  #    135.421 /sec                     (84.63%)

          2,601      branch-misses             #    5.92% of all branches          (50.44%)

       0.074268276 seconds time elapsed

       0.000589100 seconds user

结果分析:

优化后,任务执行时间减少了 53.6%,CPU cycles 减少了 96.1%,整体性能显著提高。通过调整指令顺序,降低了数据依赖性,并减少了分支预测失败和流水线停顿。这表明 x86 处理器架构在面对数据依赖性和分支预测问题时,优化指令序列可以极大提升性能。

(1)指令依赖性优化

优化后的代码显著减少了数据相关性导致的流水线停顿(stalls)。具体而言:

  1. 在 exp4.asm 中,第二次除法依赖于第一组寄存器操作的完成,导致指令序列中数据相关性过高。
  2. 在 exp5.asm 中,通过提前处理非依赖性的增量操作(如 e=f=g=h=i=j+1),延后执行第二次除法,大幅降低了数据相关性,优化了流水线利用率。

(2)减少流水线停顿

  1. 优化后的代码对变量的读取和写入更有序,避免了频繁的读写操作对流水线的影响。
  2. 前端(Frontend)和后端(Backend)stalled cycles 显著减少,表明流水线的执行效率得到了提升。

(3)分支预测改进

虽然分支预测失败的比例从 6.20% 降至 5.92%,变化不大,但总的分支数量显著减少(从 199,920 到 29,921),分支预测的整体开销下降了 85%,进一步减少了性能损失。

(4)减少指令数

  1. 优化后整体指令数减少了 67.8%,直接提升了代码执行效率。
  2. 指令数减少的原因包括移除了冗余的 mov 指令和优化后的增量处理。

 六、处理器结构实验二扩展

编写C语言矩阵乘法代码

假设我们有两个8*8矩阵A和B,并且要计算它们的乘积C。我们将用两个嵌套的循环来实现矩阵乘法,内层循环用于累加结果。

#include <stdio.h>

#define SIZE 8

void init_matrices(int mx1[SIZE][SIZE], int mx2[SIZE][SIZE]) {

    int i, j;

    for (i = 0; i < SIZE; i++) {

        for (j = 0; j < SIZE; j++) {

            mx1[i][j] = 2;

            mx2[i][j] = 3;

        }

    }

}

void multiply_matrices(int mx1[SIZE][SIZE], int mx2[SIZE][SIZE], int mx3[SIZE][SIZE]) {

    int i, j, k;

    int sum;

    for (i = 0; i < SIZE; i++) {

        for (j = 0; j < SIZE; j++) {

            mx3[i][j] = 0;

        }

    }

    for (i = 0; i < SIZE; i++) {

        for (j = 0; j < SIZE; j++) {

            sum = 0;

            for (k = 0; k < SIZE; k++) {

                sum += mx1[i][k] * mx2[k][j];

            } mx3[i][j] = sum;

        }

    }

}

void print_matrix(int mx[SIZE][SIZE]) {

    int i, j;

    for (i = 0; i < SIZE; i++) {

        for (j = 0; j < SIZE; j++) {

            printf("%d ", mx[i][j]);

        }

        printf("\n");

    }

}

int main() {

    int mx1[SIZE][SIZE], mx2[SIZE][SIZE], mx3[SIZE][SIZE];

    init_matrices(mx1, mx2);

    multiply_matrices(mx1, mx2, mx3);

    printf("The result of matrix multiplication mx1 * mx2 is:\n");

    print_matrix(mx3);

    return 0;

}

使用perf观察分支预测失败

在执行上述代码时,可以使用perf工具来监测分支预测失败的次数。perf工具可以帮助分析程序中的硬件事件(如分支预测失败、指令缓存未命中等)。

(1)首先编译代码:gcc -o exp6 exp6.cpp

(2)然后使用perf监控分支预测失败:perf stat -e branch-misses,branches ./exp6。显示分支预测失败次数和总分支次数。分支预测失败(branch-misses)通常是由于程序中的条件分支很难被预测,特别是当分支的条件变化很大时。

分析分支预测结果

对于上面的8*8矩阵乘法代码,结果如下:

Performance counter stats for './exp6':

             1.12 msec task-clock                #    0.654 CPUs utilized           

                 0     context-switches          #  0.000 K/sec                    

                 0     cpu-migrations            #  0.000 K/sec                    

               42     page-faults               #   1.64 K/sec                         

            201,734     branches                  #    180.88 K/sec                     (85.4%)

              12,457     branch-misses             #    6.2% of all branches          (52.1%)

       0.014054 seconds time elapsed

       0.000999 seconds user

       0.000000 seconds sys

根据perf统计数据,8x8矩阵乘法在执行过程中经历了较高的分支预测失败率(6.2%),这主要源于多层嵌套循环的分支结构,导致CPU频繁发生分支预测错误。尽管任务时钟较短,总耗时仅为1.12毫秒,但由于分支操作占据了85.4%的指令,分支预测失败对性能产生了显著影响。此外,页面错误的发生率较低,表明内存访问较为高效。整体来看,计算密集型的矩阵乘法在x86架构上表现出了典型的分支预测和内存访问瓶颈。

修改代码以验证分支预测失败

将8*8矩阵改成2*2矩阵,代码如下。

#include <stdio.h>

#define N 2  

int mx1[N][N] = { { 2, 2}, { 2, 2}};  

int mx2[N][N] = { { 3, 3}, { 3, 3}};  

int mx3[N][N];  

void multiplyMatrices() {

    int i, j, k, sum;

    for (i = 0; i < N; i++) {

        for (j = 0; j < N; j++) {

            sum = 0;

            for (k = 0; k < N; k++) {

                sum += mx1[i][k] * mx2[k][j];

            }

            mx3[i][j] = sum;  

        }

    }

}

void printMatrix(int matrix[N][N]) {

    for (int i = 0; i < N; i++) {

        for (int j = 0; j < N; j++) {

            printf("%d ", matrix[i][j]);

        }

        printf("\n");

    }

}

int main() {

    printf("Matrix mx1:\n");

    printMatrix(mx1);

    printf("Matrix mx2:\n");

    printMatrix(mx2);

    multiplyMatrices();

    printf("Result Matrix mx3 (mx1 * mx2):\n");

    printMatrix(mx3);

    return 0;

}

(1)首先编译代码:gcc -o exp7 exp7.cpp

(2)然后使用perf监控分支预测失败:perf stat -e branch-misses,branches ./exp7

对于修改后的2*2矩阵乘法代码,结果如下:

Performance counter stats for './exp7':

             0.35 msec task-clock                #  0.210 CPUs utilized           

                 15     context-switches          #  42.857 K/sec                    

                  0     cpu-migrations            #  0.000 K/sec                    

                30     page-faults               #  85.714 K/sec                         

               68,500     branches                  #  195.714 K/sec                     (99.9%)

                9,300      branch-misses             #  13.6% of all branches          (13.5%)

       0.000572350 seconds time elapsed

       0.000118750 seconds user

       0.000000000 seconds sys

修改代码后结果分析

对于2x2矩阵乘法的性能统计,可以看到与8x8矩阵乘法相比,以下几点明显的区别:

(1)分支失败的百分比(branch-misses)较高:尽管2x2矩阵乘法的总分支次数(68,500次)少于8x8矩阵乘法(如200,000+次),但是它的分支预测失败率(13.6%)显著高于8x8矩阵(如6.2%)。这说明,在较小的矩阵乘法中,由于较低的循环次数,CPU的分支预测器可能更难以预测每次分支跳转,导致更多的预测失败。

(2)任务时钟(task-clock)较短:与更大矩阵的计算相比,2x2矩阵的计算显然需要的CPU时间较少。因此,任务时钟和CPU利用率都相对较低。

(3)页面故障和上下文切换较少:由于计算任务较轻,系统的页面故障和上下文切换次数较少,这通常表示程序在内存中访问的数据块较小,且操作系统调度的负担较轻。

(4)CPU利用率较低:虽然使用了较多的分支预测失败,但由于矩阵乘法的计算量小,CPU的利用率并没有达到8x8矩阵乘法时的高水平。

总的来说,2x2矩阵乘法由于其计算量小,CPU的分支预测机制在处理较少的循环时表现不如处理更大矩阵时的效果,因此预测失败次数相对较高。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/957774.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

浅谈机器学习之基于RNN进行充值的油费预测

浅谈机器学习之基于RNN进行充值的油费预测 引言 随着智能交通和物联网技术的发展&#xff0c;油费预测已成为研究的热点之一。准确的油费预测不仅能帮助车主合理规划出行成本&#xff0c;还可以为油价波动提供参考依据。近年来&#xff0c;递归神经网络&#xff08;RNN&#…

There is no getter for property named ‘XXX’ in ‘XXXX‘

写了一个POST方法用于新增软件描述信息&#xff0c;报错显示在我的实体类中没有这个属性的getter方法&#xff0c;实体类如下&#xff1a; 报错没有softWare这个属性的getter方法&#xff0c;但是我的实体类中本来就没有这个属性&#xff08;笑哭...) 后面查了许多资料发现&am…

基于springboot+vue的校园二手物品交易系统的设计与实现

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

H266/VVC 变换编码中大尺寸变换块高频系数置零技术

大尺寸变换块高频系数置零 近年来视频技术有了飞速的变化&#xff0c;视频的分辨率从 1080P 过渡到 4K&#xff0c;并逐渐向发展 8K。为了适应日益增长的视频分辨率&#xff0c;新的编码技术采用了更大尺寸的变换块来提高编码效率&#xff0c;最大变换块大小变成 64x64。变换块…

5989.数字接龙

5989.数字接龙 小蓝最近迷上了一款名为《数字接龙》的迷宫游戏&#xff0c;游戏在一个大小为 NN 的格子棋盘上展开&#xff0c;其中每一个格子处都有着一个 0…K−10…K−1 之间的整数。 游戏规则如下&#xff1a; 从左上角 (0,0) 处出发&#xff0c;目标是到达右下角 (N−1…

Titans: 学习在测试时记忆 - 论文解读与总结

论文地址&#xff1a;https://arxiv.org/pdf/2501.00663v1 本文介绍了一篇由 Google Research 发表的关于新型神经网络架构 Titans 的论文&#xff0c;该架构旨在解决传统 Transformer 在处理长序列时的局限性。以下是对论文的详细解读&#xff0c;并结合原文图片进行说明&…

账号IP属地:依据手机号还是网络环境?

在数字化生活中&#xff0c;账号的IP属地信息往往成为我们关注的一个焦点。无论是出于安全考虑&#xff0c;还是为了满足某些特定服务的需求&#xff0c;了解账号IP属地的确定方式都显得尤为重要。那么&#xff0c;账号IP属地根据手机号还是网络来确定的呢&#xff1f;本文将深…

微信小程序实现自定义日历功能

文章目录 1. 创建日历组件实现步骤&#xff1a;2. 代码实现过程3. 实现效果图4. 关于作者其它项目视频教程介绍 1. 创建日历组件实现步骤&#xff1a; 创建日历组件&#xff1a;首先&#xff0c;你需要创建一个日历组件&#xff0c;包含显示日期的逻辑。样式设计&#xff1a;为…

YOLOv9改进,YOLOv9检测头融合RFAConv卷积,适合目标检测、分割任务

摘要 空间注意力已广泛应用于提升卷积神经网络(CNN)的性能,但它存在一定的局限性。作者提出了一个新的视角,认为空间注意力机制本质上解决了卷积核参数共享的问题。然而,空间注意力生成的注意力图信息对于大尺寸卷积核来说是不足够的。因此,提出了一种新型的注意力机制—…

【机器学习】深入无监督学习分裂型层次聚类的原理、算法结构与数学基础全方位解读,深度揭示其如何在数据空间中构建层次化聚类结构

&#x1f31f;个人主页&#xff1a;落叶 &#x1f31f;当前专栏: 机器学习专栏 目录 引言 分裂型层次聚类&#xff08;Divisive Hierarchical Clustering&#xff09; 1. 基本原理 2. 分裂型层次聚类的算法步骤 Step 1: 初始化 Step 2: 选择分裂的簇 Step 3: 执行分裂操作…

VirtualBox can‘t enable the AMD-V extension

个人博客地址&#xff1a;VirtualBox cant enable the AMD-V extension | 一张假钞的真实世界 最近一次完成Deepin的系统更新后&#xff0c;进入VirtualBox创建的虚拟机&#xff08;Widows10&#xff09;时&#xff0c;出现以下错误&#xff1a; 根据网址“https://askubuntu.…

[JavaScript] 数组与对象详解

文章目录 数组&#xff08;Array&#xff09;什么是数组数组的常用操作**访问数组元素****修改数组元素****数组的长度****添加和删除元素** 常用数组方法map():filter():reduce():**其他实用方法** 对象&#xff08;Object&#xff09;什么是对象对象的基本操作**访问属性****…

“模板”格式化发布新创诗(为《诗意 2 0 2 5》贡献力量)

预置MarkDown&Html文本&#xff0c;脚本读取f-string模板完成录入嵌套。 (笔记模板由python脚本于2025-01-22 19:19:58创建&#xff0c;本篇笔记适合喜欢分享的达人的coder翻阅) 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验&#xff0c;而不…

论文速读|Multi-Modal Disordered Representation Learning Network for TBPS.AAAI24

论文地址&#xff1a;Multi-Modal Disordered Representation Learning Network for Description-Based Person Search 代码地址&#xff1a;未开源&#xff08;2025.01.22&#xff09; bib引用&#xff1a; inproceedings{yang2024multi,title{Multi-Modal Disordered Repres…

计算机视觉算法实战——实体物体跟踪

✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连 ✨ ✨个人主页欢迎您的访问 ✨期待您的三连✨ ​ ​​​​​​​ ​ 1. 领域介绍✨✨ 实体物体跟踪&#xff08;Object Tracking&#xff09;是计算机视觉领域中的一个重要研究方向&#x…

C++17 新特性深入解析:constexpr 扩展、if constexpr 和 constexpr lambda

C17 不仅增强了现有特性&#xff0c;还引入了一些全新的编程工具&#xff0c;极大地提升了代码的效率和表达力。在这篇文章中&#xff0c;我们将深入探讨 C17 中与 constexpr 相关的三个重要特性&#xff1a;constexpr 的扩展用法、if constexpr 和 constexpr lambda。这些特性…

IVR:交互式语音应答系统解析及其应用

引言 IVR&#xff08;Interactive Voice Response&#xff09;&#xff0c;即交互式语音应答系统&#xff0c;是一种功能强大的电话自动服务系统。它通过语音识别和按键反馈&#xff0c;使用户与系统之间实现实时交互&#xff0c;为用户提供自助服务、咨询、报告、投诉等多种功…

Observability:最大化可观察性 AI 助手体验的 5 大提示(prompts)

作者&#xff1a;来自 Elastic Zoia_AUBRY 在过去三年担任客户工程师期间&#xff0c;我遇到了数百名客户&#xff0c;他们最常问的问题之一是&#xff1a;“我的数据在 Elastic 中&#xff1b;我该如何利用它获得最大优势&#xff1f;”。 如果这适用于你&#xff0c;那么本…

【Vim Masterclass 笔记25】S10L45:Vim 多窗口的常用操作方法及相关注意事项

文章目录 S10L45 Working with Multiple Windows1 水平分割窗口2 在水平分割的新窗口中显示其它文件内容3 垂直分割窗口4 窗口的关闭5 在同一窗口水平拆分出多个窗口6 关闭其余窗口7 让四个文件呈田字形排列8 光标在多窗口中的定位9 调节子窗口的尺寸大小10 变换子窗口的位置11…

STM32_SD卡的SDIO通信_基础读写

本篇将使用CubeMXKeil, 创建一个SD卡读写的工程。 目录 一、SD卡要点速读 二、SDIO要点速读 三、SD卡座接线原理图 四、CubeMX新建工程 五、CubeMX 生成 SD卡的SDIO通信部分 六、Keil 编辑工程代码 七、实验效果 一、SD卡 速读 SD卡&#xff0c;全称Secure Digital M…