RISC-V Reader 笔记(六)RV32V

2023-09-13 01:48:52

RV32V

image-20230708224313312

早期并行数据计算:采用 SIMD 单指令多数据,把一个64位宽寄存器拆成若干个32 16 8 位长度部分并行计算。这种方法前期看起来十分简单诱人。但是后来如果要扩展 SIMD 寄存器宽度,也要复杂化指令集,复杂开销越来越大。
向量操作:把数据取出来放入长长的向量寄存器中,流水线并行运算后从向量寄存器中分别取回到内存。
并且,时钟周期内能进行的最大操作数和向量长度是分离的,不像 SIMD 程序员要考虑怎么分分几位,设计并行硬件的时候不用顾及程序员,程序员也不用重写代码。
向量操作在 ISA 中相比 SIMD 少见多了。

向量计算

基本每一个整数或者浮点运算都有向量版本。.vv 表示操作数都是向量,.vs 表示一个向量一个标量,即一个操作数来自x/f寄存器,一个来自v寄存器。比如 Y=aX+b。.sv 是给非对称运算比如减法 除法用的,表示操作数1是标量 如 Y=a-X。融合的乘法加法则有三个寄存器种类的指示变体。

向量寄存器

向量寄存器不固定大小,这样也减少了生成代码难度和程序员负担,也可以禁用未使用的向量寄存器。比如我们现在只有两个64位浮点类型向量寄存器,处理器有1024字节向量寄存器空间,则分给他俩每个512字节,512/8=64个元素,所以最大向量长度 mvl 为64.

源寄存器和目标寄存器都是有类型的(比如16位浮点 F16,32位浮点,64位浮点……)两者之间长度不兼容处理器会自动进行转换也就是隐式转换。可以通过 vsetdcfg 指令设定向量寄存器的类型。

Load Store

通常用来处理数组。

vld:按地址顺序填充向量寄存器。向量寄存器数据类型确定了元素大小,要取的元素数量存放在长度寄存器 vl 中。

vst:逆操作。

比如 vld v0,0(a0) v0是 x32 向量寄存器,那么 vld 就会从 a0 中的起始地址(假设是1024)开始,依次取1024 1028 1032 1036……(每次4字节)地址中的值直到到达 vl 的限制长度。

有一些结构比如以行优先序存储的二维数组,我们想对列进行顺序访问,就需要有一定的步长设定,而不是单纯的按顺序访问所有元素。可以使用 vlds 和 vsts 因为他们会通过一个寄存器来设定步长。

比如 vlds v0,a0,a1 ,a0 起始地址1024,a1行长64,那么取的位置就依次是1024,1024+64,1024+64*2……直到到达 vl 的限制长度。

第三对操作是针对稀疏数组进行操作的。

vldx v0,a0,v1 a0 起始地址还是1024,v1 中有这些字节索引:16 48 80 160,那么取的位置就是 1024+16 1024+48 1024+80 1024+160.

向量操作期间的并行性

他的并行性和早期的 SIMD 很像,首先取决于处理器每个时钟周期能操作几个多少位数。比如每个时钟周期内能操作4个64位数,那么也可以操作8个32位数,16个16位数,32个8位数。

不过 SIMD 的并行性是 ISA 架构师在设计过程中定好了的,因此寄存器数量增加指令也增加,而且编译器还要修改,相比之下 RVV 就不用改变程序,而且不管硬件结构就都能跑。

条件执行

RVV 的条件执行是每个向量里所有元素分别进行的,结果也是分别赋值给向量中对应的元素的。

RVV 有8个向量谓词寄存器 vpi。他们可以存储向量寄存的结果,也可以进行 vpand vpor vpxor 等运算。

如:vplt.vs vp0,v3,x0 就是判断 v3 中哪些元素是小于0的负数?把 vp0 中对应的这些元素都置1.

然后可以进行一些对部分元素单独进行的操作,比如 add.vv,vp0 v0,v1,v2 就是把前面 vp0 为1的部分替换令v0=v1+v2.

这种操作类似网络里的掩码,vp0 vp1 被规定用作控制向量操作的掩码。(比如我们要操作一个向量中的所有元素,那我们就得用一个全为1的掩码)。

RVV 里有 vpswap 指令可以快速把其他 vpi 的值交换到 vp0 或 vp1. 也可以选择谓词寄存器是否启用,可以通过禁用的方式来清零。

其他向量指令

setvl:把 vl 设置为源操作数和 mvl 中的最小值。如果 mvl 更大,则循环处理 mvl 长度;反之说明源操作数尾部的剩余部分长度小于最大向量长度,处理这一部分即可。

vselect:按第二个源操作数的索引位置,从第一个源操作数中取出一个新的向量。

vselect vedst, vsrc, vindices

比如 vindices 值是 8 0 4 2,则依次取出 vsrc 中第8 0 4 2索引处的元素值替换 vedst 的第0 1 2 3索引处的值。

vmerge:类似 vselect,有两个源操作数,vpo 指代替换的元素来自第一个 vsrc 还是第二个。

vmerge,vpo vdest, vsrc1, vsrc2

比如 vsrc1:1 2 3 4

vsrc2:10 20 30 40

vpo:1 0 0 1

vpo 指代取的数据是:v2的0位,v1的1位,v1的2位,v2的3位。

也就是最终赋值给 vdest 的值是 10 2 3 40。

vextract:从一个向量的中间位置开始取数,放到目标向量的起始位置。

如:vextract vdest, vsrc, start 如果向量长度64,start=32,那么就是从 vsrc 的中间取后32位数放到 vdest 开头32位。

我们可以利用 vextract 来做递归二分操作,比如循环求和所有向量寄存器元素,就可以做6次 vextract。

image-20230829184730094

上例是利用 RV3V 写 DAXPY 程序(y=ax+y,一个函数处理)。

  1. 启用向量寄存器:我们只需要启用两个,给 x y 两个数组用。
  2. 每次循环开始,设定向量长度,y=a*x+y。
  3. 继续遍历或跳出。

简简单单10条左右的指令,一轮循环下来访存3*64次,浮点运算2*64次。每条指令平均有 19 次访存和 13 次运算。

与其他 ISA 向量运算的比较

都拿上例 DAXPY 的例子来进行分析。

MIPS:MSA 寄存器128位宽,每次可以处理2个双精度浮点数。但没有向量长度的寄存器,因此需要单独判断n是否为奇数,奇数最后要多做一次运算。平均每个指令大约有 1 个访存和 0.5 个运算操作。

x86:部分支持 256 位宽,n可以=4. 加载数据时先创建副本并在进入主循环之前进行测试。但是和 MIPS 有相似的问题,需要结尾判断 %4!=0 的部分。每条指令平均有约 2 次访存和 1 次运算。

结语

可以看出向量的一些优势。

如果不用向量,我们就要反复把代码写上好几遍,编程难度大一些。不过新指令程序员记起来也是比较复杂。

RISC-V 的向量运算不受一些向量长度等的影响,比如我把长度修改了,他仍然会自动判断 mvl 并执行,封装的比较好,程序员编码时以及编译器不用考虑这一点的影响。

更多推荐

回归与聚类算法系列⑤:逻辑回归

目录1、介绍2、原理输入激活函数3、损失及其优化损失函数优化4、API5、案例:乳腺癌肿瘤预测数据集代码🍃作者介绍:双非本科大三网络工程专业在读,阿里云专家博主,专注于Java领域学习,擅长web应用开发、数据结构和算法,初步涉猎Python人工智能开发。🦅主页:@逐梦苍穹📕回归与聚类算法系列⭐①:概念简述⭐②:

Spring Bean&生命周期图&扩展接口介绍&spring的简化配置

目录1.生命周期简图2.扩展接口介绍2.1Aware接口2.2BeanPostProcessor接口2.3InitializingBean2.4DisposableBean2.5BeanFactoryPostProcessor接口3.spring的简化配置3.1项目搭建3.2Bean的配置和值注入3.3AOP的示例1.

学习Bootstrap 5的第十四天

目录Toast如何创建Toast实例打开Toast实例滚动监听(Scrollspy)如何创建滚动监听实例侧边栏导航(Offcanvas)如何创建Offcanvas侧边栏实例侧边栏的方向实例设置背景及背景是否可滚动实例侧边栏案例实例ToastToast组件类似警告框,当发生某些事情时(例如当用户单击按钮、提交表单等)时,

7.从句学习

目录一、从句。(1)从句总结。(2)从句类型。(3)引导词(常见的引导词)。(3.1)名词性从句:(3.2)形容词性从句:(3.3)副词性从句:(4)从句举例。(4.1)名词性从句举例。(4.2)形容词性从句举例。(4.3)副词性从句举例。一、从句。(1)从句总结。1.名词性从句:从句(引导词+句子/单词)直接充当句子

Redis RedLock算法和底层源码分析

Redlock红锁算法官网地址:DistributedLockswithRedis|Redis为什么要使用RedLock?解释:线程1首先获取锁成功,将键值对写入redis的master节点,在redis将该键值对同步到slave节点之前,master发生了故障;redis触发故障转移,其中一个slave升级为新的ma

Java开发面试--Redis专区

1、什么是Redis?它的主要特点是什么?答:Redis是一个开源的、基于内存的高性能键值对存储系统。它主要用于缓存、数据存储和消息队列等场景。高性能:Redis将数据存储在内存中,并采用单线程的方式处理请求,使得其读写速度非常快,能够达到10万+的读写操作每秒。数据结构丰富:Redis支持多种数据结构,包括字符串、列

ChunJun(OldNameIsFlinkX)

序言ChunJun主要是基于Flink实时计算框架,封装了不同数据源之间的数据导入与导出功能.我们只需要按照ChunJun的要求提供原始与目标数据源的相关信息给Chunjun,然后它会帮我们生成能运行与Flink上的算子任务执行,这样就避免了我们自己去根据不同的数据源重新编辑读入与读出的方案了cuiyaonan2000

Vue.js模板语法[下](事件处理,表单综合案例,自定义组件)---详细讲解

一,事件处理1.`.stop`:阻止事件冒泡。使用该修饰符可以阻止事件向父元素传播2.`.prevent`:阻止默认事件。使用该修饰符可以阻止事件的默认行为。3.`.capture`:使用事件捕获模式。默认情况下,事件是在冒泡阶段处理的,使用该修饰符可以改为在捕获阶段处理。4.`.self`:只在事件触发的元素自身上触

数据交易是什么?看这篇文章

数据已经成为第五类生产要素,数据交易可以有效发挥数据价值,释放数据要素潜力。但数据又具有高敏感性的特点,承载着复杂的权利内容和权利主体,故数据交易需要限定在特定范围内,并遵循特有的规则规范。哪些数据可以进行交易,如何进行交易,数据交易要注意什么问题,这些都是数据交易的基础性问题。本文将以贵阳、北京、上海这三所国内代表性

对权限的理解和使用

目录一:用户权限:★su命令★sudo命令二:文件权限★文件的类型+权限★文件夹的权限的使用▲文件夹的可读权限:▲文件夹的可写权限:▲文件夹的可执行权限:★权限的修改操作▲chmod命令★对于文件的用户分组的修改▲chown命令▲chgrp命令★权限掩码以及umask指令★粘滞位的使用场景及使用方法在Linux当中我们

一文教你如何设计出优秀的测试用例(文档+视频)

这篇文章我们主要聊一下软件测试工程师最通用的也是最根本的技能,测试用例的设计能力。测试用例测试用例是通过使用在测试计划中确定的测试技术,对于已确定的测试条件进行逐步推敲,精炼而设计出来的重点说明如何具体操作产生何种结果的文档。通俗的话就是要把想要测试的动作变成在什么情况下,做什么动作,用什么数据方式去做,最后想得到什么

热文推荐