计算机体系结构:VLIW

原文来自知乎 计算机体系结构:VLIW

本文主要介绍计算机体系结构中的VLIW,以供读者能够理解该技术的定义、原理、应用。

🎬个人简介:一个全栈工程师的升级之路!
📋个人专栏:计算机杂记
🎀CSDN主页 发狂的小花
🌄人生秘诀:学习的本质就是极致重复!

目录

1、引言

2、VLIW设计哲学

3、干脏活累活的VLIW编译器

3.1、循环操作优化

3.2、循环之外

4、总结


1、引言

在开始之前,我们思考一下,怎么才能够让一个程序执行得更快?在计算机领域当中有一个非常经典的公式来计算一个程序的执行时间。

根据上面的式子可以看出来,有三个因素影响了程序的执行时间。

  • 该程序总共需要执行的指令个数;
  • 每条指令所需要的周期数(简称CPI)
  • 每个周期所对应的时间;

本篇文章围绕CPI展开,CPI全称Clock Per Instruction,即每个时钟周期能够执行的指令个数,它反映了处理器并行处理指令个数的能力。有时候我们也用其倒数形式,即IPC(Instruction Per Clock)。

对于非流水线的多周期处理器,我们需要多个Cycle才能执行完一条指令,对于普通的流水线处理器,我们一个周期最多也就执行一条指令,IPC最大也就只能为1了,还有没有办法让IPC进一步提高呢?

实际上在程序的指令流中,很多指令相互之间是独立的,只要处理器有足够的硬件资源,理论上它们就可以被同时执行,即实现指令级别的并行(ILP,Instruction Level Parallesim),最典型的ILP有以下几种方式:

  • 流水线(有时候提ILP不会提流水线,因为处理器设计默认采用流水线);
  • 超标量(Superscalar);
  • 超长指令字(Very Long/Large Instruction Word,VLIW);

超标量处理器设计是个非常大的话题,也是目前高性能CPU的主流技术。本篇文章不讨论超标量处理器,而是围绕VLIW展开,去思考为何VLIW曾一时风光无限,又为何在通用处理器市场败下阵来,又是怎么在DSP/NPU等领域重新大放异彩的,带着这些问题,我们正式进入VLIW的学习。

VLIW于1983年由美国计算机科学家Josh Fisher提出,并发表于体系结构顶会ISCA上,根据VLIW的名字我们就可以知道,其指令一定很长。实际也确实如此。

VLIW是将多条互相独立的指令,通过软件(编译器)的方式打包(Pack)在一起,我们将打包好的多条指令,称为instruction bundle。取指模块根据打包好的指令,送入各自独立的功能部件,并行执行,如下图所示,取指模块从指令存储器取出了Instruction Bundle,共包含四条指令,然后同时发送给后级模块,从而实现了指令级并行。

此外我们可以看出来其一共有两个浮点部件、三个整数部件、两个存储部件、一个转移部件,因此对应的指令Bundle长度为32*8=256。

根据上面的例子我们不难发现,指令一旦送出以后,各个功能部件是互相独立的,各个指令也是天然并行的。即VLIW不需要通过硬件检查指令与指令之间的依赖关系,而是由软件(编译器)去静态的调度互相独立的指令,其优点就是硬件设计将对简单(为什么?),但对于编译器开发工作者而言,任务非常艰巨(为什么?),也导致了一系列的软件兼容问题(为什么?)。

2、VLIW设计哲学

对于传统的VLIW而言,其一般具有以下的特性:

  • 一次取多个指令(是指令,而不是指令bundle,记得区分这两个东西),具备多个功能部件(MIMD架构);
  • 对于同一个bundle里面的指令而言,基于lock step的方式执行(后面解释什么是lock step);
  • 同一个bundle里面的指令静态对齐,送给功能单元;

有了以上的概念,我们看一个典型五级流水线与VLIW相结合的例子,如下图所示。Bundle包含两条指令,因此理想情况下可以实现IPC=2。此外我们还可以看出来,VLIW通常具有集中的存储器结构,以及独立的功能单元

所谓的Lock-step,即要么全都带走,要么一无所有。只要VLIW中任意一个指令stall住了,为了维持并排走的原则,所有与之并行的指令都需要停住。(有点像两人三足的感觉)

对于VLIW机器而言,是这么处理依赖关系的:

  • 由编译器处理所有依赖相关的stalls;
  • 硬件不做任何的依赖检查(无条件信任软件);
  • 如果是可变延时操作又该怎么处理呢?Memory堵住了怎么办呢?

既然VLIW是由软件即编译器处理所有和依赖相关的问题,那能够获得的信息就比较少,实际上是缺乏运行时(硬件)信息的,因为一旦发出去,就没有回头路了,由此会有一系列的问题。

假设我们取出了某条指令,比如add $t1,$s1,$s2,剩下的指令都需要用到$t1当做源操作数,如果硬件不做任何检查,那岂不是一定拿到旧的数据?整个就出错了?

又比如对于同一个程序而言,假设我们更换了不同的处理器,对于不同的硬件而言,功能部件也有可能不同,依赖关系自然也不同,如果还用之前那一套编译器,那结果岂不是有可能对不上?没错,这些都是VLIW潜在的问题。

到目前为止,即使我们对VLIW编译器的细节不清楚,但我们应该已经领略了VLIW的设计哲学了,让我们暂时总结一下VLIW的优点和缺点:

首先谈一下优点,其实优点无非就是硬件设计简单,我们具体展开讲讲:

1、无需动态调度硬件->因此硬件设计起来相对简单。之所以不用动态调度硬件,实际上是我们相信编译器,所发的指令都是没有依赖关系的,因此硬件各自执行各自的,不会有任何问题。如果是超标量处理器,是需要硬件动态的检查各个指令之间是否存在依赖关系,如果有的话,可能需要阻塞,可能需要旁路,可能需要重命名等等,这些在VLIW中都不用。(静态就是不涉及运行时,动态与之相反,可以简单的理解,硬件跑起来以后提供的信息就是动态信息)

2、同一个Bundle内的指令天然对齐,因此取指令以后直接往功能单元发即可,简化了硬件设计;

然后我们再看一下VLIW的缺点:

1、编译器需要在每个周期找到N条互相独立的指令,组成相应的bundle往功能单元发。如果找不到呢?那也只能硬着头皮发,总比不发好,但这样的话,必然某个槽或某几个槽(slot,比如上面的例子中,一个bundle中就有8个slot)需要插入NOP指令。这样就减少了并行性,并且导致代码size变大(总归是要执行完所有指令的,插入无意义的指令越多,自然总的size就越大)。

2、当执行宽度(N,slot个数)、指令延迟、功能单元发生改变的时候,都需要重新编译(而超标量处理器不需要),因此软件兼容性非常差,这也是VLIW在通用CPU领域走向失败的根本原因。

3、由于Lockstep执行的原因,导致某个指令阻塞住了,整个bundle都会阻塞住,即使它们彼此之间互相独立的。就好像两人三足中队友摔倒了,即使你是好好的,也需要等待你的队友。

VLIW就是这么一套设计哲学,将原本硬件应该干的事情,全部交给编译器去做,这也就导致了编译器开发极为困难,因为要通过有限的代码信息,去找到互相独立的指令,即充分挖掘并行性,但很多时候纯靠静态信息,并行性是很难挖掘的,而编译器为了避免出错,都是按照最保守的方式,假设最坏的情况去做,即对似是而非的独立,默认不独立,这样就会导致大量的NOPs指令。

但由于VLIW对硬件设计非常友好,因此当并行性非常容易挖掘的时候(这种情况编译器开发就没那么困难了),比如DSP、GPU、NPU等,很多指令天然就是彼此独立的,不存在依赖关系的,这种场景VLIW就具有非常大的优势。

3、干脏活累活的VLIW编译器

首先我们看一下VLIW编译器都干了啥:

  • 通过静态调度,尽可能的并行化(填满Bundle中的slot);
  • 保证同一个Bundle内的所有指令是互相独立的(必须要保证,因为硬件是不会去做检查的);
  • 通过静态调度,尽可能的避免数据冒险;(因为Lockstep的存在,一个堵住了,整个就堵住了);

3.1、循环操作优化

当我们谈并行,谈VLIW,就一定要谈循环级别的并行,因为很多时候循环是天然并行的。并且对于编译器而言是很好发现的(Loop-Level Parallelism is normally analyzed at the source level or close to it)。

我们假设编译器无法发现循环的并行性,如下图所示,可以看到编译以后的汇编代码,对于VLIW机器而言,可以挖掘的并行性很少,并且由于fadd需要用到fld以后的f1做运算,因此编译器为了避免出现数据冒险,会晚几个周期再送fadd指令到功能组件(再强调一次,依赖关系由编译器保证,这里对应图中的fld->fadd的红线),整体的性能非常的差。跑一个浮点加法需要8个周期(即使我们认为浮点加是单周期指令)。

实际上写过C/C++的朋友,应该都知道这种循环实际上是可以展开的(Loop Unrolling),就比如下面的例子,编译器就可以发现这一点,我们假设按照4的粒度进行展开,这样就可以在一次循环中执行四次迭代了。(当然如果N不是4的整数倍,需要把剩下的几次额外处理一下)

编译器完成该优化以后,我们再看展开以后对应的汇编代码,以及VLIW机器的流水线占用情况。可以看到流水线排布比之前满了一些,整体的FLOPS也有了显著的提高。

可不可以进一步提高FLOP?实际上是可以的,因为我们实际上可以发现,两次循环之间是没有依赖关系的,即上述的例子中,我们原本是0,1,2,3为一组,然后4,5,6,7。组与组之间是没有重叠的,但实际上它们就是独立的,因此可以进一步,将它们Overlap起来,这称之为Software Pipelineing,软件流水线(其实跟硬件流水线非常的像,本质上没太大区别)。如下图所示:

可能上面的这个VLIW指令排布还不够Makesense,我们再看下面这幅图。这幅图相比大家能理解单纯的Loop Unrolled和Software Pipelined的区别了。核心在于Overlapping,让独立的东西尽可能重叠起来。

3.2、循环之外

上面讲的是针对循环并行性的优化,如果没有循环呢?

Josh Fisher在提出VLIW的时候,同时也提出了一种名为Trace Scheduling的技术,该方法的关键思想是将基本块组合起来,使它们形成一个单入口多出口的较大块,可能作为直线代码执行。如下图所示,此外我们做如下的定义:

所谓的basic block其实就是一段连续的代码,其中没有分支或者跳转指令,具有单一的入口和出口;

由于控制密集型指令的存在(比如分支指令),basic block的size不可能太大;

对于单个basic block内部而言,很难挖掘出ILP(单个Basic block内部,认为是顺序执行的,指令与指令之间依赖关系较大);

Trace内部的指令都是单周期指令,通过重新安排基本块的顺序,使得Trace中的指令尽可能地利用处理器的功能单元,同时避免指令之间的冲突和依赖关系,进而提高程序的执行效率(如下图的灰色路径就是一条Trace,实际上Trace对应的是分支或跳转指令执行最频繁的一条路径)。

我们来看一个实例,左边是所有的基本块,以及某个基本块到另外一个基本块对应的概率,其实就是个决策树。右边是我们根据概率最大选出来的trace,是不是非常的清晰?

但是,直接用Trace是有问题的,跟踪调度的主要缺点之一是跟踪中间的进入和退出会导致严重的复杂性(实际上trace也没有中间进入或退出,但实际是的指令是可能有的),需要编译器生成和跟踪补偿代码,并且通常使得评估此类代码的成本变得困难。 超级块的形成过程与用于跟踪的过程类似,但它是扩展基本块的一种形式,仅限于单个入口点,但允许多个出口

我们直接采取尾部复制的方式,就可以实现单入口多出口的superblock,大家看下图,我们只是在尾部加了F的复制,我们可以看到原本C到F以及D到F的入口没了,全部转移了。这就是我们所需要的超级块。

我们看一下具体的例子,左边是原始代码。右边是经过Superblock变换以后的代码,通过该机制实现了单入口多出口。下面是Superblock变换以后的代码进一步优化的结果,通过更大的代码块,有机会找到更多的并行执行机会,编译器会相对激进的在superblock内部进行code reorder以及代码优化。

总而言之,超级块是一种复杂的编译器优化技术,需要综合考虑指令之间的依赖关系、处理器的特性和约束条件等因素。它可以在静态编译阶段对程序进行优化,以提高程序的执行效率和性能。通过合并基本块并利用指令级并行性,超级块可以减少指令之间的冲突和依赖关系,从而提高程序的吞吐量和执行效率。

这里也只是说了个superblock是什么,到底如何根据superblock进行优化,优化策略细节就是非常大的话题了,涉及到编译器的很多知识,这里就不讲了(实际上我也不会)。

4、总结

看完本篇文章,大家应该也清楚VLIW为什么会在通用处理器领域失败了,其实无非就是软硬件没有充分解耦,对编译器开发要求过高,软件兼容性太差。并且在通用CPU领域,纯靠静态信息可挖掘的并行性不够,会插入很多NOPS指令,实际的性能打不过超标量+乱序处理器。

但VLIW的硬件设计简单,对于使用场景具有大量并行性的处理器,如DSP、GPU、部分NPU等,VLIW都在大放异彩,感兴趣的可以搜相关论文看,本篇文章就讲到这里,希望对大家有所帮助,如果有错误欢迎指正。

🌈我的分享也就到此结束啦🌈
如果我的分享也能对你有帮助,那就太好了!
若有不足,还请大家多多指正,我们一起学习交流!
📢未来的富豪们:点赞👍→收藏⭐→关注🔍,如果能评论下就太惊喜了!
感谢大家的观看和支持!最后,☺祝愿大家每天有钱赚!!!欢迎关注、关注!

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

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

相关文章

腾讯云服务器99元一年购买入口链接

腾讯云服务器99元一年购买入口链接如下,现在已经降价到61元一年,官方活动链接如下: 腾讯云99元服务器一年购买页面腾讯云活动汇聚了腾讯云最新的促销打折、优惠折扣等信息,你在这里可以找到云服务器、域名、数据库、小程序等等多种…

【uniapp】小程序自定义一个通用的返回按钮组件

左边箭头,右边文字可以自定义,但是不要太长,太长可以自己改 .back的width值,改宽一点。 用这个组件的时候首先要在pages.json里把导航栏变成自定义的: ,{"path" : "pages/test/test","style&…

Goose:Golang中的数据库迁移工具

Goose:Golang中的数据库迁移工具 在Golang开发中,数据库迁移是一个常见的任务,用于管理数据库模式的演化和版本控制。Goose是一个轻量级的、易于使用的数据库迁移工具,专为Golang开发者设计。本文将介绍Goose的基本概念、用法和优…

智能驾驶规划控制理论学习01-自动驾驶系统介绍、规划控制模块介绍

目录 一、自动驾驶系统概述 二、规划控制模块介绍 1、规划控制架构 2、规划控制目标 3、Cartesian和Frenet坐标系 4、Frenet坐标系概览 5、解耦式规划和联合式规划 一、自动驾驶系统概述 目前被国内外广为接受的自动驾驶级别划分标准是 SAE&#xff…

Nano 33 BLE Sense Rev2学习第二节——手机蓝牙接收数据

Nano 33 BLE Sense Rev2需要下载的程序 #include <ArduinoBLE.h> #include "Arduino_BMI270_BMM150.h"float x, y, z; int degreesX 0; int degreesY 0;BLEService ledService("19B10010-E8F2-537E-4F6C-D104768A1214"); // create service// cre…

XSS漏洞--概念、类型、实战--分析与详解[结合靶场pikachu]

目录 一、XSS概念简述 1、XSS简介&#xff1a; 2、XSS基本原理&#xff1a; 3、XSS攻击流程&#xff1a; 4、XSS漏洞危害&#xff1a; 二、XSS类型&#xff1a; 1、反射型XSS&#xff1a; 2、存储型XSS&#xff1a; 3、DOM型XSS&#xff1a; 三、靶场漏洞复现(pikach…

LVS四层负载均衡集群

简介 LVS&#xff08;Linux Virtual Server&#xff09;即Linux虚拟服务器&#xff0c;是由章文嵩博士主导的开源负载均衡项目&#xff0c;目前LVS已经被集成到Linux内核模块中。该项目在Linux内核中实现了基于IP的数据请求负载均衡调度方案&#xff0c;终端互联网用户从外部访…

diffusion model (扩散模型)原理

扩散模型分为正向过程和反向过程。 正向过程为一点点在图片上添加噪声的过程&#xff0c;反向过程为去噪声的过程。 图片的生成就是反向过程&#xff0c;给一张高斯噪声图片&#xff0c;逐步去噪生成图片。 扩散模型和VAE的区别&#xff0c; VAE是一步到位的&#xff08;通过…

一个基于差异同步数据库结构的工具 - Skeema

本文是 GO 三方库推荐的第 5 篇&#xff0c;继续介绍数据库 schema 同步工具&#xff0c;我前面已经写了两篇这个主题的文章。系列查看&#xff1a;Golang 三方库。 今天&#xff0c;推荐是的一个基于差异实现数据库 schema 迁移的工具库 - skeema&#xff0c;同样由 Go 实现。…

数字孪生10个技术栈:数据采集的八种方式

大家好&#xff0c;我是贝格前端工场&#xff0c;上期讲了数字孪生10个技术栈&#xff08;总括&#xff09;:概念扫盲和总体介绍&#xff0c;获得了大家的热捧&#xff0c;本期继续分享技术栈&#xff0c;大家如有数字孪生或者数据可视化的需求&#xff0c;可以联络我们。 一、…

【好书推荐-第九期】Sora核心技术相关书籍《扩散模型:从原理到实战》与《GPT 图解:大模型是怎样构建的》:Sora的两大核心技术,都藏在这两本书里!

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公众号&#xff1a;洲与AI。 &#x1f388; 本文专栏&#xff1a;本文收录…

解决DBeaver执行脚本报错No active connection

解决DBeaver执行脚本报错No active connection 1、报错问腿 2、问题解决 2.1、右键点击该数据库&#xff0c;选择SQL编辑器&#xff0c;选择新建SQL编辑器&#xff0c;然后将sql语句复制过去。 或者左击选中数据库后直接使用快捷键 Ctrl] 2.2、在Project-General中找到Scr…

Javaweb之SpringBootWeb案例之自动配置案例的自定义starter测试的详细解析

3.2.4.3 自定义starter测试 阿里云OSS的starter我们刚才已经定义好了&#xff0c;接下来我们就来做一个测试。 今天的课程资料当中&#xff0c;提供了一个自定义starter的测试工程。我们直接打开文件夹&#xff0c;里面有一个测试工程。测试工程就是springboot-autoconfigurat…

常见排序算法解析

芝兰生于深林&#xff0c;不以无人而不芳&#xff1b;君子修道立德&#xff0c;不为穷困而改节 文章目录 插入排序直接插入排序希尔排序 选择排序直接选择排序堆排序 交换排序冒泡排序快速排序优化挖坑法前后指针法非递归版 归并排序递归非递归 总结 插入排序 插入排序&#…

【编程小记】在Windows下使用C/C++代码判断一个文件是否被其他进程占用

在Windows下使用C/C代码判断文件是否被占用 一、原理二、函数简单介绍三、实例代码 一、原理 在Windows下有一个Windows API叫做CreateFile&#xff0c;通过这个接口我们可以创建或打开文件&#xff0c;我们打开文件时可以采用独占模式进行打开&#xff0c;如果能够打开文件说…

Word Game

题目链接&#xff1a;Problem - C - Codeforces 解题思路&#xff1a; 用map存字母和出现的次数&#xff0c;然后遍历三个字母数组&#xff0c;如果map值为1&#xff0c;则加三分&#xff0c;为2加1分&#xff0c;否则不加分。 下面是c代码&#xff1a; #include<iostrea…

第41期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

Linux学习-二级指针的使用

目录 ###指针传参时要用二级指针 ###函数体内部想要修改函数外部指针变量值的时候需要使用二级指针(指针变量的地址) ###指针传参时要用二级指针 char *str[5]; int Fun(char **ppstr,int len); ###函数体内部想要修改函数外部指针变量值的时候需要使用二级指针(指针变量的…

#微信小程序创建(获取onenet平台数据)

1.IDE&#xff1a;微信开发者工具 2.实验&#xff1a;创建一个小程序&#xff08;http get获取onenet平台数据&#xff09; 3.记录&#xff1a; 百度网盘链接&#xff1a;https://pan.baidu.com/s/1eOd-2EnilnhPWoGUMj0fzw 提取码: 2023 &#xff08;1&#xff09;新建一个工…

【C++STL详解 —— string类】

【CSTL详解 —— string类】 CSTL详解 —— sring类一、string的定义方式二、string的插入三、string的拼接四、string的删除五、string的查找六、string的比较七、string的替换八、string的交换九、string的大小和容量十、string中元素的访问十一、string中运算符的使用十二、…