计算机体系结构基础知识介绍之指令集并行的基本编译器技术(循环展开、基本管道调度)

news2024/9/23 11:25:17

一、基本管道调度和循环展开

为了保持管道满载,必须通过查找可以在管道中重叠的不相关指令序列来利用指令之间的并行性。 为了避免流水线停顿,相关指令的执行必须与源指令分开一定的时钟周期距离,该距离等于该源指令的流水线延迟。 编译器执行此调度的能力取决于程序中可用的 ILP (指令集并行)量以及管道中功能单元的延迟。

通过注意到每次迭代的主体是独立的,我们可以看到这个循环是并行的。

第一步是将前面的片段翻译为 RISC-V 汇编语言。 在下面的代码段中,x1最初是数组中地址最高的元素的地址,f2包含标量值s。Registerx2是预先计算的,因此Regs[x2]+8是要操作的最后一个元素的地址。

简单的 RISC-V 代码(未安排在管道中)如下所示:

让我们首先看看当这个循环被安排在一个简单的 RISC-V 管道上时,它的运行情况如何,延迟如下图 所示。 

 

 简单解释一下上图:FP指浮点数,指令产生的结果为浮点运算且应用于另一个浮点运算,则延迟3个周期,指令产生的结果为浮点运算且存储则延迟2个,指令产生的结果为读取浮点数且应用于另一个浮点运算则延迟一个,读取浮点数并储存无延迟。

如果没有任何调度,循环将按如下方式执行,需要九个周期:

 我们可以安排循环只获得两个停顿,并将时间减少到七个周期:

fadd.d 之后的停顿供 fsd 使用,重新定位 addi 可以防止 fld 之后的停顿。

在前面的示例中,我们完成一次循环迭代并每七个时钟周期存储回一个数组元素,但对数组元素进行操作的实际工作只需要这七个时钟周期中的三个(加载、添加和存储)。 剩余的四个时钟周期由循环开销 — addi 和 bne 以及两个停顿组成。 为了消除这四个时钟周期,我们需要相对于开销指令的数量获得更多的操作。

相对于分支和开销指令增加指令数量的一个简单方案是循环展开。 展开只是多次复制循环体,调整循环终止代码。

循环展开也可用于改进调度。 因为它消除了分支,所以它允许将来自不同迭代的指令一起调度。 在这种情况下,我们可以通过在循环体内创建额外的独立指令来消除数据使用停顿。 如果我们在展开循环时简单地复制指令,那么使用相同的寄存器可能会阻止我们有效地调度循环。 因此,我们希望每次迭代使用不同的寄存器,从而增加所需的寄存器数量。

这是合并 addi 指令并删除展开期间重复的不必要的 bne 操作后的结果。 请注意,现在必须设置 x2,以便 Regs[x2]+32 成为最后四个元素的起始地址。

 我们消除了 x1 的三个分支和三个减量。 加载和存储上的地址已得到补偿,以允许合并 x1 上的 addi 指令。 这种优化可能看起来微不足道,但事实并非如此; 它需要符号替换和简化。 符号替换和简化会重新排列表达式,从而允许常量折叠,从而允许将诸如 ((i + 1) + 1) 的表达式重写为 (i + (1 + 1)),然后简化为 (i + 2).

如果没有调度,展开循环中的每个 FP 加载或操作都会跟随一个相关操作,因此会导致停顿。 这个展开的循环将在 26 个时钟周期内运行 - 每个 fld 有 1 个停顿,每个 fadd.d 有 2 个,加上 14 个指令发出周期 - 或四个元素中每个元素的 6.5 个时钟周期,但可以对其进行调度以显着提高性能。 循环展开通常在编译过程的早期完成,以便优化器可以暴露并消除冗余计算。

在实际程序中,我们通常不知道循环的上限。 假设它是 n,并且我们想要展开循环以制作主体的 k 个副本。 我们生成一对连续的循环,而不是单个展开的循环。 第一个执行 (n mod k) 次,并且其主体是原始循环。 第二个是展开的主体,由迭代 (n/k) 次的外循环包围。 对于较大的 n 值,大部分执行时间将花费在展开的循环体中。

在这里详细解释一下为什么这样做:

这是因为循环展开可以减少循环控制的开销,提高指令级并行(ILP)的程度,从而提高程序的性能。循环展开是一种编译器优化技术,它将循环体中的指令复制多份,使得每次迭代执行更多的工作,同时减少了循环次数和循环条件判断的次数。例如,如果一个循环体有4条指令,每次迭代执行一次,那么循环展开后可以将循环体复制两份,每次迭代执行8条指令,同时将循环次数减半。这样可以节省循环控制指令(如分支、跳转、递减等)所占用的时钟周期,也可以增加流水线中不相关指令的数量,从而提高流水线的效率。

当我们不知道循环的上界时,我们可以使用一种叫做条带划分(Strip Mining)的技术,将一个大的循环分解为两个小的循环。第一个循环执行原始的循环体,次数为总迭代次数对展开因子(即复制份数)取模的结果;第二个循环执行展开后的循环体,次数为总迭代次数除以展开因子的结果。这样可以保证循环展开后的程序和原始程序的功能相同。例如,如果一个循环要执行1001次,我们想要将它展开为4份,那么我们可以生成两个循环:第一个循环执行原始的循环体1次(1001 mod 4 = 1),第二个循环执行展开后的循环体250次(1001 -1/ 4 = 250)。对于大的迭代次数,大部分的执行时间会花在第二个循环中,因为它执行了更多的工作量,同时减少了更多的控制开销。

 展开循环的执行时间已降至总共 14 个时钟周期,即每个元素 3.5 个时钟周期,而在任何展开或调度之前每个元素为 8 个周期,展开但未调度时为 6.5 个周期。

 二、循环展开和调度总结

 大多数指令集并行技术的关键是了解何时以及如何更改指令之间的顺序。  在实践中,这个过程必须通过编译器或硬件以有条不紊的方式执行。 为了获得最终展开的代码,我们要求如下:

■ 通过发现循环迭代是独立的(循环维护代码除外),确定展开循环是有用的。
■ 使用不同的寄存器以避免由于使用相同的寄存器进行不同的计算而强制产生的不必要的约束(例如,名称依赖性)。
■ 消除额外的测试和分支指令并调整循环终止和迭代代码。
■ 通过观察来自不同迭代的加载和存储是独立的来确定展开循环中的加载和存储可以互换。 这种转换需要分析内存地址并发现它们并不引用相同的地址。
■ 安排代码,保留产生与原始代码相同的结果所需的任何依赖性。

所有这些转换的关键要求是理解一条指令如何依赖于另一条指令以及在给定依赖性的情况下如何更改或重新排序指令。

三种不同的原因限制了循环展开的收益:(1) 每次展开所摊销的开销量减少,(2) 代码大小限制, (3) 编译器限制。

我们首先考虑循环开销的问题。 当我们展开循环四次时,它在指令之间产生了足够的并行性,可以在没有停顿周期的情况下调度循环。 事实上,在 14 个时钟周期中,只有 2 个周期是循环开销:维护索引值的 addi 和终止循环的 bne。 如果循环展开八次,则开销将从每个元素的 1/2 周期减少到 1/4。

展开的第二个限制是代码大小的增加。 对于较大的循环,代码大小的增长可能是一个问题,特别是如果它导致指令缓存未命中率增加。

另一个比代码大小更重要的因素是激进的展开和调度所造成的寄存器的潜在短缺。 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux内核TCP/IP源码浅析

目录 数据接收流程图硬件层网络层ip_rcvip_rcv_coreip_rcv_finish 和 ip_rcv_finish_coreip_local_deliverip_local_deliver_finish 和 ip_protocol_deliver_rcu 传输层tcp_v4_rcvtcp_v4_do_rcvtcp_rcv_state_processtcp_rcv_establishedtcp_recvmsg 数据结构socketsocksock_co…

允许Traceroute探测漏洞和ICMP timestamp请求响应漏洞解决方法(三)

目录 服务器检测出了漏洞需要修改 1.允许Traceroute探测漏洞解决方法 2、ICMP timestamp请求响应漏洞 服务器检测出了漏洞需要修改 1.允许Traceroute探测漏洞解决方法 详细描述 本插件使用Traceroute探测来获取扫描器与远程主机之间的路由信息。攻击者也可以利用这些信息来…

Chapter 3: Conditional | Python for Everybody 讲义笔记_En

文章目录 Python for Everybody课程简介Chapter 3: Conditional executionBoolean expressionsLogical operatorsConditional executionAlternative executionChained conditionalsNested conditionalsCatching exceptions using try and exceptShort-circuit evaluation of lo…

从零开始simulink自定义代码生成----自定义硬件驱动库文件(3)

文章目录 前言C mex文件mdlInitializeSizesmdlInitializeSampleTimesmdlOutputsmdlTerminatemdlRTWc文件结尾编译c文件 tlc文件Start函数Outputs函数模型及生成的代码 总结 前言 在很早的时候,做过一些Simulink自定义硬件驱动库的相关探索,但是后面没有…

惊喜!Alibaba架构师终于发布“微服务架构与实践”文档

前言: 对于微服务架构的概念,相信大家应该都不陌生,无论使用 Apache Dubbo、还是 Spring Cloud,都可以去尝试微服务,把复杂而庞大的业务系统拆分成一些更小粒度且独立部署的 Rest 服务。 但是这个过程,具…

单表查询练习

查看表的字符集编码 show create table tbname; 查看系统默认字符集 SHOW VARIABLES LIKE character_set_database; 显示所有可用的字符集 SHOW CHARACTER SET; 修改系统默认字符集 ①在 /etc/my.cnf 文件中的 [mysqld] 下添加: ②重启数据服务 systemctl re…

Linux:PXE网络装机

要实现需要开启以下服务 dhcp --- 开机没有u盘或光盘的引导电脑会去寻找网络中的引导 tftp --- 用于引导系统 ftp&& http --- 制作yum仓库让引导的系统去ftp或者http上找rpm包 1.ftp&& http yum仓库搭建 Linux:YUM仓库服务_鲍海超-GNUBHC…

Mycat【Mycat安全设置(SQL拦截白名单、SQL拦截黑名单、Mycat-web安装 )】(九)-全面详解(学习总结---从入门到深化)

目录 Mycat安全设置_user标签权限控制 Mycat安全设置_privileges标签权限控制 Mycat安全设置_SQL拦截白名单 Mycat安全设置_SQL拦截黑名单 Mycat性能监控_Mycat-web安装 Mycat性能优化 Mycat实施指南 Mycat安全设置_user标签权限控制 目前 Mycat 对于中间件的连接控制并…

Mac矢量绘图工具 Sketch

Sketch是一款适用于 UI/UX 设计、网页设计、图标制作等领域的矢量绘图软件, 其主要特点如下: 1. 简单易用的界面设计:Sketch 的用户界面简洁明了,使得用户可以轻松上手操作,不需要复杂的学习过程。 2. 强大的矢量绘图功…

Lua快速入门笔记

文章目录 Lua快速入门笔记前言1、Lua概述2、Lua环境安装3、快速体验Lua编程4、数据类型5、变量6、循环7、流程控制8、函数9、运算符10、字符串11、数组12、迭代器13、表14、模块与包15、元表16、协同程序 Lua快速入门笔记 前言 本文是笔者参考菜鸟教程对Lua的一个快速入门学习&…

2023-07-08:RabbitMQ如何做到消息不丢失?

2023-07-08:RabbitMQ如何做到消息不丢失? 答案2023-07-08: 1.持久化 发送消息时设置delivery_mode属性为2,使消息被持久化保存到磁盘,即使RabbitMQ服务器宕机也能保证消息不丢失。同时,创建队列时设置du…

vue开发:Vue的状态管理 - Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 我个人的理解是,如果有一些公共的数据需要在多个组件中共享或者某一个状态的改变会影响多个组件&a…

基础之linux常用命令精华

目录 第一章.shell 1.1查看内部命令 1.2外部命令存放于 echo $PATH 一个一个找,找到为止,找不到就报无命令 第二章.linux常用命令 2.1.编辑linux命令行的辅助操作 2.2命令帮助help和--help,man手册使用 2.3.目录和文件的管理命令 2.…

消失的她-InsCode Stable Diffusion 美图活动一期

一、 Stable Diffusion 模型在线使用地址: https://inscode.csdn.net/inscode/Stable-Diffusion 二、模型相关版本和参数配置: Model: Cute_Animals Version: v1.2.0 Size: 512x512 Model hash: 57bd734213 Steps: 20 Sampler: Heun CFG scale: 7 三、图…

操作系统中的线程进程和同步异步和并发并行

目录 一、进程和线程1.1 进程1.2 线程1.3 实现多任务的方法1.3.1 使用多进程实现多任务1.3.2 使用多线程(单个进程包含多个线程)实现多任务1.3.3 使用多进程多进程实现多任务 1.4 进程和线程的比较1.5 Java的多线程模型的应用 二、同步和异步2.1 同步2.2 异步 三、并发与并行3.…

QTday2

点击登录&#xff0c;登陆成功&#xff0c;跳转到新的界面 主函数 #include "widget.h" #include "second.h" #include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();Second s;QObject::connect…

MySQL数据库介绍流程(最新mysql)

版本介绍 第一步&#xff1a;下载MySQL数据库 1、下载地址&#xff1a;http://dev,mysql.com/downloads/windows/installer/8.0html 2、就是直接搜索&#xff1a;mysql官方 msyql官方网站 这里就安装成功 第二步&#xff1a;这么启动和停止mysql 第三步&#xff1a;这么快捷停…

B - Get an Even String

Get an Even String - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题意&#xff1a;题目要使字符串变成偶字符串&#xff0c;对于每一个奇数i都和后面i1的位置字符相同。求给定字符串最少去掉几个字符能得到偶字符串。 解题思路&#xff1a;贪心&#xff0c;找每次第一对出现…

黑客(网安)自学

建议一&#xff1a;黑客七个等级 黑客&#xff0c;对很多人来说充满诱惑力。很多人可以发现这门领域如同任何一门领域&#xff0c;越深入越敬畏&#xff0c;知识如海洋&#xff0c;黑客也存在一些等级&#xff0c;参考知道创宇 CEO ic&#xff08;世界顶级黑客团队 0x557 成员…

5、Task_stat() always report used == size他两总是相等

1、今天想查看一下任务的堆栈使用情况&#xff0c;按官方手册加入下面调试下面代码 Task_Stat statbuf; /* declear buffer */ Task_stat(Task_self(),&statbuf); /*call func to get status */ If(statbuf.used > (statbuf.stackSize * 9 / 10)) { System_printf(“…