精华内容
下载资源
问答
  • CPU vector operations(CPU vector指令 )

    千次阅读 2013-02-22 12:40:44
    vector operations 是现代CPU优化速度的一个技巧,就是把通过大寄存器把多个数据同时处理来加速,一般ICC GCC都会简单的预测做出vector operations的优化,但是也不纯粹,自己添加相关代码还是可以有一定的优化空间...

    vector operations 是现代CPU优化速度的一个技巧,就是把通过大寄存器把多个数据同时处理来加速,一般ICC GCC都会简单的预测做出vector operations的优化,但是也不纯粹,自己添加相关代码还是可以有一定的优化空间。

    Today's microprocessors have vector instructions that make it possible to do operations on
    all elements of a vector simultaneously. This is also called Single-Instruction-Multiple-Data
    (SIMD) operations. The total size of each vector can be 64 bits (MMX), 128 bits (XMM) or
    256 bits (YMM).
    Vector operations are useful when doing calculations on large data sets where the same
    operation is performed on multiple data elements and the program logic allows parallel
    calculations. Examples are image processing, sound processing, and mathematical
    operations on vectors and matrixes. Algorithms that are inherently serial, such as most
    sorting algorithms, are not suited for vector operations. Algorithms that rely heavily on table
    lookup or require a lot of data shuffling, such as many encryption algorithms, cannot easily
    be implemented as vector operations.
    The vector operations use a set of special vector registers. The maximum size of each
    vector register is 128 bits (XMM) if the SSE2 instruction set is available, or 256 bits (YMM) if
    the AVX instruction set is supported by the microprocessor and the operating system. The
    number of elements in each vector depends on the size and type of data elements


    http://www.agner.org/optimize/#vectorclass

    http://www.codeproject.com/Articles/332437/Fast-SIMD-Prototyping


    展开全文
  • vector的常见用法详解

    2021-01-12 14:07:06
    vector用途:(1)在一些元素个数个数不确定的场合可以很好的节约空间,(2)为了能够满足数据中间用空格隔开并且最后一个数据后面不能有额外的空格,可以先用vector记录所有需要输出的数据,然后一次性输出,(3)...

    vector用途:(1)在一些元素个数个数不确定的场合可以很好的节约空间,(2)为了能够满足数据中间用空格隔开并且最后一个数据后面不能有额外的空格,可以先用vector记录所有需要输出的数据,然后一次性输出,(3)用邻接表存储图

    1.vector的定义:

    vetctor<类型名> 变量名;

    2.vector容器内元素的访问:

    (1)通过下标访问:
    例如: vector vi;
    访问:vi[下标]

    (2)通过迭代器访问:
    定义为:
    vector<类型名> ::iterator it;

    例如

    #include<cstdio>
    #include<vector>
    using namespace std;
    int main()
    {
    	vector<int> vi;
    	for(int i=1;i<=5;i++)
    	{
    		vi.push_back(i);
    	} 
    	for(vector<int>::iterator it=vi.begin();it!=vi.end();it++)
    	{
    		printf("%d ",*it);
    	}
    	return 0;
    } 
    

    3.vector常用函数实例解析:
    vector<类型> vi;

    (1)push_back(x):就是往vi后面增加一个元素x;如:vi.push_back(3)
    (2)pop_back():删除vi中的最后一个元素,里面没有参数
    (3)size():来获取vi中的元素个数
    (4)clear():来清空vector中的元素个数
    (5)insert(it,x):用来向vector的任意迭代器it处插入一个元素

    #include<cstdio>
    #include<vector>
    using namespace std;
    int main()
    {
    	vector<int> vi;
    	for(int i=1;i<=5;i++)
    	{
    		vi.push_back(i);
    	} 
    	vi.insert(vi.begin()+2,-1);//将-1插入vi[2]的位置
    	for(int i=0;i<vi.size();i++)
    	{
    		printf("%d ",vi[i]);//1 2 -1 3 4 5
    	} 
    	return 0;
    } 
    

    (6)erase():
    第一种用法:删除单个元素:

    #include<cstdio>
    #include<vector>
    using namespace std;
    int main()
    {
    	vector<int> vi;
    	for(int i=1;i<=5;i++)
    	{
    		vi.push_back(i);
    	} 
    	vi.earse(vi.begin()+2);//将vi[2]删除 --3 
    	for(int i=0;i<vi.size();i++)
    	{
    		printf("%d ",vi[i]);//1 2 4 5
    	} 
    	return 0;
    } 
    

    第二中用法:删除一个区间内的所有元素:

    #include<cstdio>
    #include<vector>
    using namespace std;
    int main()
    {
    	vector<int> vi;
    	for(int i=1;i<=5;i++)
    	{
    		vi.push_back(i);
    	} 
    	vi.earse(vi.begin()+1,vi.begin()+3);//将vi[1].vi[2]删除 
    	for(int i=0;i<vi.size();i++)
    	{
    		printf("%d ",vi[i]);//1 4 5
    	} 
    	return 0;
    } 
    
    展开全文
  • RISC-V矢量指令集学习记录

    千次阅读 2019-04-03 22:54:43
    RISC-V矢量指令集学习记录 指令集草案:https://github.com/riscv/riscv-v-spec/blob/master/v-spec.adoc 关于这篇博客: 1、这是我RISC-V矢量指令集的学习记录,不是翻译,所以有错误的地方请联系我。 2、更新时间...

    RISC-V矢量指令集学习记录

    指令集草案:https://github.com/riscv/riscv-v-spec/blob/master/v-spec.adoc

    关于这篇博客:
    1、这是我RISC-V矢量指令集的学习记录,不是翻译,所以有错误的地方请联系我。
    2、更新时间不定时,按心情更新,也按我学习进度更新。
    3、最近都比较忙,暂时没有时间继续学习。

    1、 Introduction
    这个文档是RISC-V矢量扩展的草案。文档描述了基础矢量扩展的全部独特的特性。

    Note:这是一个草案,对于矢量规范的实施和评估提供稳定建议。一旦草案标签被移除,矢量扩展0.7版本就可以足够稳定地用于开放工具链、功能模拟器和芯片的初始实现,尽管会随着一些微小的变化或更新而不断发展。

    基础矢量扩展这个术语非正式地用于描述矢量ISA标准指令集。此草案规范旨在说明某个向量函数是如何通过向量指令来实现的,但对于一个给定的平台,没有强制规定使用哪些矢量指令。

    Note:每个实际平台的概述文件将正式指定该平台所采用的任何向量扩展的强制组件。The base vector extension can expected to be close to that which will eventually be used in the standard Unix platform profile that supports vectors. 其他平台,包括嵌入式平台,可以选择实现这些扩展的子集。The exact set of mandatory supported instructions for an implementation to be compliant with a given profile is subject to change until each profile spec is ratified.

    基础矢量扩展被设计为各种领域中额外矢量扩展的基础,包括密码学和机器学习。

    2、 定义 & 常量参数
    支持矢量扩展的每个hart都有三个参数的定义。

    1. 单个矢量元素的最大位数(ELEN),必须是2的幂,即2^n。
    2. 矢量寄存器的位数(VLEN)要大于或等于ELEN,同时也是必须是2的幂,即2^n。
    3. 位的分条间距(SLEN)要符合VLEN ≥ SLEN ≥ 32,同时也是必须是2的幂,即2^n。

    Note:平台配置文件可以对这些参数进行进一步的约束,例如,要求ELEN≥max(XLEN,FLEN),或要求最小VLEN值,或固定一个SLEN值。XLEN为系统的操作位数,如32位或64位,FLEN位浮点操作的位数。

    在某些约束下,ISA支持编写二进制代码,且这些二进制代码能在具有不同参数的harts上进行移植。

    Note:可以编写暴露这些参数差异的代码。

    Note:在harts之间如果VLEN、ELEN和SLEN这些参数存在差异,则执行期间无法迁移具有活性向量状态的线程上下文。

    3、 矢量扩展编程者模型
    矢量扩展增加32个矢量寄存器和5个CSRs(vstart,vxsat,vxrm,xtype,v1)到标准RISC-V ISA。如果标准RISC-V ISA不支持浮点,则额外增加一个fcsr的寄存器,用于保持vxsat和vxrm CSRs的镜像值。

    3.1. 矢量寄存器
    在标量RISC-V指令集基础上,矢量扩展增加了32个结构向量寄存器,v0-v31。每个矢量寄存器的位宽都是固定的VLEN位。

    Note:Zfine是一个正在考虑的ISA新选项,其使用情况是采用浮点指令时,浮点指令的参数可以从整形寄存器中活得。

    3.2. 矢量起始索引CSR寄存器(vstart)
    vstart是一个可读可写的CSR寄存器,指定矢量指令要执行的第一个元素的索引。
    通常,vstart只有在矢量指令陷阱时被硬件写入,同时vstart的值能反应陷阱时的元素(要不是同步异常就是异步中断),并且在处理可恢复陷阱后,应在该元素上恢复执行。
    所有向量指令都定义为从vstart CSR中给定的元素号开始执行,使目标向量中的早期元素不受干扰,并在执行结束时将vstart CSR重置为零。
    如果vstart寄存器中的值大于或等于向量长度vl,则不执行任何元素操作,虽然目标向量结束时,vl上的元素被归零,而vstart寄存器被重置为零。
    vstart CSR被定义为只有足够的可写位来保存最大元素索引(比最大vlmax少一个或是lg2(VLEN)位),vstart CSR的高位硬连线到零(读取零,写入被忽略)。

    Note:最大矢量长度通过最大LMUL设置(8)和最小SEW设置(8)获得,所以VLMAX_max=8VLEN/8=VLEN。例如,VLEN=256, vstart将由8位组成代表从0至255之间。*

    vstart CSR可由非特权代码写入,但非零的vstart值可能会导致向量指令在某些实现上运行速度大大减慢,因此应用程序员不应该使用vstart。一些向量指令不能用非零的vstart值执行,不然会引发下面定义的非法指令异常。

    3.3. 矢量定点舍入模式寄存器(vxrm)
    矢量定点舍入模式寄存器保存一个两位可读可写的字段,用于表示舍入模式。矢量定点取整模式被赋予一个单独的CSR地址,以允许独立访问,但也作为fcsr高位字段的一个反映。系统如果没有包含浮点操作,那在使用矢量扩展时必须增加fcsr CSR寄存器。

    Note:舍入模式可以通过csrwi指令来完成。

    3.4. 矢量定点饱和标志(vxsat)
    vxsat CSR寄存器包含一个读写位,该位指示定点指令是否必须使输出值饱和,以此适应目标格式。

    3.5. 在fcsr中的矢量定点域
    vxrm和vxsat可以通过单独CSR来实现,也能通过浮点CSR寄存器fcsr的字段来访问。系统如果没有包含浮点操作,那在使用矢量扩展时必须增加fcsr CSR寄存器。

    Note:字段被打包到fcsr中,以便更快地保存/恢复上下文。

    3.6. 矢量型寄存器(vtype)
    矢量类型CSR寄存器是一个只读、位宽为XLEN的CSR寄存器,只能被vsetvl{i}指令更新,并提供用于解释向量寄存器文件内容的默认类型。矢量类型决定每个矢量寄存器中元素的安排和多个矢量寄存器的分组方式。

    Note:早期的草案允许采用常规CSR写入方式来写入vtype。只允许vsetvl{i}指令进行vtype的更新,简化了vtype CSR寄存器的状态维护。

    在矢量基础扩展中,类型寄存器有三个文件内容,vill、vsew[2:0]和vlmul[1:0],它们的说明如下。

    Note: vtype最小的基本实现只需要四位存储空间,vsew[1:0]两位,vlmul[1:0]两位。可以使用vsew[1:0]中的非法64位组合对非法值进行编码,而不需要额外的存储位。

    Note: vediv[1:0]字段由的EDIV扩展使用,描述如下。

    Note: 对向量库的进一步标准和自定义扩展将扩展这些字段,以支持更多的数据类型。

    Note: 扩展指令编码长度允许在指令编码中指定这些字段,尽管vlmul可能希望随AVL而变化。

    3.6.1. 矢量标准元素宽度(vsew)
    vsew中的值设置动态标准元素宽度(SEW)。默认情况下,向量寄存器被划分为VLEN/SEW个标准宽度元素。在基础向量扩展中,SEW只需要支持(XLEN, FLEN)中的最大值。

    展开全文
  • 当mstatus.vs域被设置为初始状态或者干净的状态,只要执行了相关的指令改变了vpu(vector processor unit)状态,该域就会被改写成dirty的状态。 misa寄存器用于指示当前处理器的架构特性,该寄存器高两位用于表示...

    1.Introduction

      引用计算机体系结构中的一句话:执行可向量化应用程序最高效方法就是向量处理器。向量化的目的主要是为了去除程序中的loop,以减少不必要的指令开销。并且向量化可以将加载和存储的过程做到流水化,比较好的掩藏存储器延时,下面举个例子说明向量化的好处:
       for (i = 0 ; i <64 ; i++) {
            Y[i] = a * X[i]; 
        }
        
        //将该程序转换成riscv汇编:
        li a0,0 //用于loop中的条件
        li a1,64 //用于loop中的条件比较值
        li t0,COEF_A  //COEF_A为常量a
        li s0,BASE_ADDR1 //BASE_ADDR1为LW基地址
        li s1,BASE_ADDR2 //BASE_ADDR2为SW基地址
        loop:
        lw a2,0(s0)
        mul a3,a2,t0
        sw a3,0(s1)
        addi s0,s0,4
        addi s1,s1,4 
        addi a0,a0,1
        bne a0,a1,loop
        
        //将该程序转换成riscv-v汇编:
        //VLEN=512
        li t0,COEF_A
        li s0,BASE_ADDR1 //BASE_ADDR1为LW基地址
        li s1,BASE_ADDR2 //BASE_ADDR2为SW基地址
        vsetvli t1,t2,e32,m4
        vle32 v1,(s0)
        vmul.vx v2,v1,t0
        vsw32 v2,(s1)
        
        由上面的例子可以看出:
        1.纯标量汇编指令比向量化的riscv汇编指令,多出了(5 + 7*64 - 7)= 446条需要执行的指令。
        2.对于纯标量汇编指令,每次执行mul前后,都需要执行lw,sw。这个加载和存储的过程是不能流水起来的,而向量化的vl,vs指令,是可以做到流水化的,将整个取向量的时间分散在单个元素上,可以很好的掩藏存储器延迟。
    

    2.Implementation-defined Constant Parameters

    向量扩展spec定义了2个parameters(以下参数均需要满足:位宽为2的幂):
    1.ELEN:单个向量元素的最大位宽,ELEN>=8。(我的理解是:该参数应该根据硬件中ALU能处理的最大位宽决定)。//2030 4bit
    2.VLEN:通用向量寄存器的位宽。
    riscv中其他通用架构寄存器位宽命名:
    XLEN:INT类型通用寄存器位宽。
    FLEN:FLOAT类型通用寄存器位宽。
    
    基向量扩展spec要求VLEN>=128。这是权衡后的一个比较折中的值,尽管设置较大的VLEN可以减少短向量指令的条带挖掘代码,但是设置较大的VLEN意味着较大的硬件register的开销。并且riscv-v-spec提供了LMUL寄存器,该寄存器的值可以对向量寄存器进行分组。比如通用向量寄存器一共32个,设置LMUL为8,就可以将全部的通用寄存器分成4组,每组包含了8个连续的向量寄存器,这种分组的方式增加了向量寄存器组中元素的个数,同样减少了条带挖掘(strip mining)的代码。
    这里先解释一下条带挖掘的含义:条带挖掘技术是为了解决实际应用程序的向量长度长于硬件单次处理支持的最大长度的问题,条带挖掘代码是指生成一段代码,使得每个向量运算处理元素的长度都是小于或等于最大向量长度的。下面以计算机体系结构中的一段C程序来进一步解释条带挖掘代码:
    //假设一共要处理n个元素,vlmax为循环内每次最大处理的长度
    st = 0 ;//元素起始索引
    vl = (n % vlmax) ; //得到不规则部分的元素数量
    for (j =0 ; j <= (n/vlmax) ; j++) {
        for (i = st ; i <(st + vl) ; i++) {
            Y[i] = a * X[i] + Y[i] ; 
        }
        st = st +vl ; //计算下一次开始的元素索引
        vl = vlmax ; //将每次处理的元素长度恢复成最大的向量长度
    }
    

    3.Vector Extension Programmer’s Model

       向量扩展向基标量RISC-V ISA中增加了32个向量寄存器(v0-v31)以及7个无特权的CSRS(控制和状态寄存器),分别是vstart、vxsat、vxrm、vcsr、vl、vtype、vlenb。向量寄存器的位宽为固定的VLEN宽度。
        riscv 架构规定了一些只在机器模式下支持的寄存器,机器模式是riscv中硬件线程执行时的最高权限模式。机器模式对内存,I/O和一些对于启动和配置系统来说必要的底层功能有着完全的使用权。通常用户写的程序都在用户模式执行,用户态具有最低级别的权限。当用户程序需要使用一些底层硬件设备或者执行出现了异常又或者外围设备对正在执行的处理器发起了中断请求,那么cpu就会由用户态切换至内核态,也就是切换到机器模式下,将cpu的控制权,转交给内核程序。
    riscv把程序执行时出现的异常,或者外部中断统称为陷阱(陷阱其实很好理解,因为不管是异常还是中断,cpu都会由用户态陷入内核态,这个陷入内核的过程就可以理解成踩入了陷阱里,要经过一些其他的操作,才能爬出陷阱,恢复正常行走)。当遇到陷阱时,首先要将当前的pc保存下来,方便之后进行恢复。然后清空异常指令之前的流水线。接下来根据对应的陷阱类型,切换到对应的程序入口,开始执行内核程序( 内核程序也是由一条条指令组成的,同样需要在流水线上执行)。等内核程序执行完成后,在重新把cpu的控制权转交给用户程序,从之前保存的pc指针开始重新取指,执行。
    在重新回到riscv规定了一些只支持机器模式相关的寄存器的话题。mstatus寄存器是机器模式下的状态寄存器,其中mstatus[10:9]是向量上下文状态域(一个进程存储在处理器各寄存器中的中间数据叫做进程的上下文,所以进程的切换实质上就是被中止运行进程与待运行进程上下文的切换)。
    mstatus.vs域同mstatus.fs类似。当mstatus.vs域被写成0时,试图执行向量指令或者访问向量寄存器均会引发非法指令异常。当mstatus.vs域被设置为初始状态或者干净的状态,只要执行了相关的指令改变了vpu(vector processor unit)状态,该域就会被改写成dirty的状态。
    misa寄存器用于指示当前处理器的架构特性,该寄存器高两位用于表示当前处理器所支持的架构位数;该寄存器低26位用于指示当前处理器所支持的不同模块化指令集。riscv架构文档规定misa寄存器可读可写,从而允许处理器可以动态的配置某些特性。misa.v表示vector扩展指令集域,即使misa.v域被清0,mstatus.vs域也存在。这样设计可以简化mstatus.vs的处理。
    

    3.1.vstart

    vstart为可读可写寄存器,该寄存器规定了一条向量指令中第一个被执行的元素的索引。
    vstart寄存器通常在向量指令执行的过程中产生了陷阱被写入。该寄存器记录了进入陷阱时向量指令操作元素的索引,以便跳出陷阱之后能够继续执行剩下的元素。 所有向量指令都根据vstart中指定的元素索引开始执行。执行的过程保证该索引之前的目的寄存器的元素不被干扰,在执行的末尾,将vstart寄存器的值复位到0。当向量指令产生非法指令异常时,vstart寄存器将不会被改写。所以vstart寄存器被改写,只能是程序执行时出现了可恢复的同步异常,或者外部产生中断的情况。
    思考假如不能通过vstart存储异常时的元素索引,那么在执行向量指令过程中发生的可恢复异常,必须要等到这条向量指令执行完,才能进入异常处理程序。这就要求向量指令必须是原子的,增加了控制复杂度,并且对于一些长延时的指令,比如load,将会导致响应中断的时间特别的长。
    若vstart索引值大于vl的值,说明vstart指向的元素索引已经超过了当前所有元素的范围,该指令不会执行,并且同时会把vstart寄存器复位到0。
    vstart可写的bit位,根据VLMAX确定,在vl部分已经描述过。
    

    3.2.vxsat

    vxsat为可读可写寄存器,该寄存器不仅有独立的寄存器地址,并且在vcsr寄存器中也有对应的域。该寄存器有效表示输出结果做了饱和截位以适应目的寄存器格式。比如当运算发生正溢出时,保留结果为能取到的最大正值;当运算发生负溢出时,保留结果为负数最小值。
    

    3.3.vxrm[1:0]

    vxrm[1:0]为可读可写寄存器,该寄存器不仅有独立的寄存器地址,并且在vcsr寄存器中也有对应的域。该寄存器控制定点舍入模式,一共四种模式,分别是round-to-nearest-up(rnu)、round-to-nearest-even(rne)、round-down(rdn)、round-to-odd(rod)。(问题:可否解释一下定点数在内存中的存放格式)
    vxrm[1:0]寄存器通过单条csrwi指令写入值。
    假如源操作数是v,有低d bit数据要被截掉,那么做完rounding-mode之后的最终结果应该是(v>>d)+r,r就是根据不同的rounding mode得到的增量值。
    rnu:向距离近的方向进行舍入,当距离与两边都相等时,向上舍入。
    rne:向距离近的方向进行舍入,当距离与两边都相等时,向偶数方向舍入。
    rdn:向下舍入,直接取移位后的值。
    rod:舍入到奇数值方向。
    其中,v[d-1]表示权重位。当v[d-1]=0,表示距离舍的方向更近;当v[d-1]=1且v[d-2:0]=0时,距离舍入两个方向距离均相等;当v[d-1]=1,且v[d-2:0] != 0时,表示距离入的方向更近。
    

    3.4.vcsr

    vcsr[2:0]寄存器为可读可写寄存器,该寄存器由vxrm[1:0],以及vxsat组成。
    思考为什么vxsat和vxrm既有单独的寄存器地址,也在vcsr中占有对应的域:
     riscv spec中fflags和frm寄存器也采用的这样的设计。这类状态寄存器,希望方便快速的读写,比如有时候希望一条指令能把这两个寄存器的值同时读出来。又或者写这两个寄存器,如果要同时写,就得先做移位拼接在写,那有的时候只想改变其中一个寄存器的值,也做移位在拼接然后写的操作就比较慢,因此单独写对应的地址就显得尤为方便快速。考虑到适应不同的需求,这类状态寄存器就设计为既有各自单独的寄存器存储空间,又集中在一个寄存集中划分各自的域。
    

    3.5.vl

    vl寄存器为只读寄存器,该寄存器存储着一个无符号整数,用来规定一条向量指令需要更新多少个元素。
    该寄存器只能被vsetvli、vsetvl指令以及fault-only-first矢量读取指令的变体进行更新。
    目的寄存器中元素索引大于等于vl的元素,将不会被修改。如果vstart大于等于vl,那么目的寄存器的任何元素都不会被修改。
    vl的位宽由最小元素组成的最大向量长度决定。最小的元素位宽至少是8bit,最大的分组设置为LMUL等于8,那么VLMAX=LMUL * (VLEN / SEW) = VLEN。也就是说vl的位宽,直接由VLEN的大小决定。
    

    3.6.vtype

    vtype为只读寄存器,位宽同通用整型架构寄存器位宽(XLEN)。该寄存器提供了默认值用于解析向量寄存器中的内容,并且只能通过vsetvl{i}指令进行更新(这样做的目的是使得维护vtype寄存器的状态简单化)。
    vtype寄存器用于解析向量寄存器文件中的内容、决定单个向量寄存器中元素的组成以及决定多个向量寄存器是如分组的。
    vtype寄存器有五个域,分别是vill,vma,vta,vsew[2:0],vlmul[2:0]。
    

    3.6.1.vill

    当先前的vsetvl{i}指令试图写入一个不被支持的值到vtype寄存器,vtype的vill域置1,同时vtype的其他域均清0。后面其他依赖于vtype寄存器执行的指令,将会引发非法指令异常。
    

    3.6.2.vma &vta

    这两个域修改目标尾部元素和目标被屏蔽元素在向量指令执行过程中的行为。在向量指令执行过程中,尾部元素和被屏蔽元素,不会接收新的运算结果。
    vma:vector mask agnostic,用于控制目的寄存器被屏蔽元素的行为。0:undisturbed 1:agnostic
    vta:vector tail agnostic,用于控制目的寄存器尾部元素的行为。0:undisturbed 1:agnostic
    采用undisturbed策略,那么目的寄存器相应元素集合,将保持原来的值。
    采用agnostic策略,那么目的寄存器相应元素集合,既可以保持原值,也可以全部写1(spec上说全部写1而不是全部写0,是为了避免软件开发者依赖于这部分值,虽然我现在也没明白为啥写1就可以避免了。。。)。
    那么采用agnostic策略的好处是什么呢?举个例子:对于超标量的流水线,会采用寄存器重命名的方式,来避免WAW以及WAR这两类hazard。那程序的逻辑寄存器会映射到物理寄存器,映射后的对应关系会更新到重命名映射表中。那对于undisturbed策略,需要目的寄存器相应的元素保持原来的值。那么在用新的物理寄存器重命名时,还需要根据重命名映射表,查到原有的映射关系,再把这部分元素的值先读出来,写到重命名后的对应元素位置。这种方式对于压根儿不关心尾部元素集合或者被屏蔽元素集合的值的后续操作,就既降低了性能,又增加了不必要的功耗。对于普通的in-order流水线,可以采用这种undisturbed的策略。对于超标量的流水线,使用agnostic策略就显得更加明智。
    

    3.6.3.vsew[2:0]

    vsew[2:0]:vector selected element width,用于动态的设置元素位宽。
    一个向量寄存器被分成(VLEN/SEW)个元素。SEW=8*(2^vsew)。
    eg:假设VLEN=128,那么当vsew等于0时(SEW=8),表示一个向量寄存器含有16(128/8)个元素。
    SEW的值应该跟随LMUL的变化而变化(如果现在你不理解这句话,先带着问题往后看)。
    

    3.6.4.vlmul[2:0](有符号数)

    LMUL:for vector register grouping,LMUL=2^(vlmul[2:0])。
    多个向量寄存器可以组在一起,这样单条向量指令可以操作多个向量寄存器(这里多个向量寄存器组成一组,成为单条向量指令的一组操作数)。之前讲到过条带挖掘,其实就是一组多维的嵌套loop,切割任务,将任务划分为规则的部分和不规则的部分。对长向量的处理,如果硬件支持的单次处理的向量长度比较短,就需要loop多次,但是循环本身就增加了指令个数,还增加了一些划分的运算,因此对处理器性能的提高不是很理想。这里就解释了通过LMUL配置,增加组内寄存器个数,也就是增加了一条向量指令可操作的元素个数,避免了条带挖掘代码的出现,或者减少了条带挖掘的次数。
    if(LMUL>=1):表示组合在一起的向量寄存器个数(LMUL=1、2、4、8)。
    if(LMUL 是一个小数):用于减少一个向量寄存器中被使用的位宽。(LMUL=1/2、1/4、1/8)。设置LMUL为小数通常是用于混合位宽的向量指令操作。举个例子:比如源操作数向量用到的LMUL设置为1,计算后的元素位宽拓宽了2倍,保持元素个数不变的情况下,LMUL也需要增大两倍,也就是需要两个向量寄存器才能够存储下所有的向量运算结果。那假设源操作数本身只需要占据一个向量寄存器的一半,但是LMUL不能为小数,也就是占据小于等于1个向量寄存器宽度的都使用LMUL=1,那么这种情况再遇到位宽扩展指令,还是以刚才的例子就需要两个向量寄存器。但是很有可能扩展了的运算结果刚好只需要占据一个向量寄存器。这样设计就造成了向量寄存器的浪费。
    spec要求必须支持LMUL>=SEW(lmul1min)/SEW(lmul1max)的配置。其中SEW(lmul1min)表示在LMUL为1的条件下必须支持的最小元素位宽;SEW(lmul1max)表示在LMUL为1的条件下必须支持的最大元素位宽。因为SEW(lmul1max)是可以设置为与VLEN相等的,假如LMUL<SEW(lmul1min)/SEW(lmul1max),那么将不能保证一个寄存器剩余部分的位宽至少包含一个完整的元素。
    VLMAX = LMUL * (VLEN/SEW),表示单条向量指令能够操作的最大元素数量。
    掩码寄存器只包含在一个单独的向量寄存器中,与LMUL设置值无关。
    

    3.7.vlenb

    该寄存器为只读寄存器,表示以字节为单位的向量寄存器长度,vlenb=VLEN/8。vlenb是一个设计时常量,增加这个寄存器是为了减少一些需要直接用到vlenb的程序的额外计算指令的开销。
    

    4.Mapping of Vector Elements to Vector Register State

    这里spec上面举例描述的非常详细了,总结一下:
    1.元素以小端模式进行存放,也就是元素的数据低位放在向量寄存器的低位(低字节放在低地址)。
    2.当LMUL=1时,元素按照顺序,从寄存器最低有效位开始摆放,元素摆放填满整个向量寄存器。
    3.当LMUL<1时,元素按照顺序,从寄存器最低有效位开始摆放,只填LMUL * (VLEN / SEW)个元素,剩余的高位未被填充的位置,按照tail部分处理。
    4.当LMUL>1时,元素依然按照顺序,从寄存器最低有效位开始摆放,当索引最小的向量寄存器被填满,就按照顺序开始填下一个向量寄存器。
    5.当LMUL>1时。允许SEW>VLEN,也就是一个元素可以占多个向量寄存器,元素填充方式依然和前述一样。
    
    针对混合位宽操作解释一下:混合位宽操作比如扩位宽操作(源操作数元素位宽小于目的操作数元素位宽),或者缩位宽操作(源操作数元素位宽大于目的操作数元素位宽)。但是无论位宽怎么变,源操作数元素的个数和目的操作数元素的个数肯定是相等的。那么这就需要动态配置LMUL随着元素位宽(SEW)的变化而变化也就是说需要保持SEW/LMUL为常数。LMUL设置的值大,可以减少向量指令取指和分发的开销。
    向量掩码只占据一个向量寄存器。它的存储与SEW和LMUL的配置无关。每个向量元素具有对应的1bit向量掩码。
    

    5.Vector Instruction Formats

    向量扩展指令在已有的三类指令LOAD-FP、STORE-FP、AMO编码的基础上,重新定义了一些域 。并且新增了一类指令OP-V。
    向量扩展指令可以有标量或者矢量源操作数,也能产生标量或者矢量的结果。
    

    5.1.标量操作数

    标量操作数可以是立即数、整数通用寄存器、浮点通用寄存器,或者从矢量寄存器元素0获得。
    标量运算结果可以写到整数通用寄存器、浮点通用寄存器,也可以写到矢量寄存器元素0的位置。
    任意矢量寄存器都可以用于存储标量,与LMUL的设置无关。
    

    5.2.矢量操作数

    矢量操作数通过有效元素位宽(EEW)和有效分组模式(EMUL)来决定一个矢量寄存器组中元素的大小和位置。
    默认情况下,大多数指令的大多数操作数EEW=SEW,EMUL=LMUL。
    有一些指令的源操作数和目的操作数有相同数量的元素,但是却不是同样的位宽。所以EEW可能与SEW不同,EMUL也可能和LMUL不同。但是EEW/EMUL=SEW/LMUL。如果源操作向量寄存器与目的向量寄存器有重叠的部分,那么必须满足以下三个约束之一:
    1.目的向量寄存器的EEW与源向量寄存器的EEW相等(这句话我的理解是一一对应,目的寄存器与源寄存器完全相同)。
    2.目的向量寄存器EEW比源向量寄存器的EEW小,但是交叠部分只在源寄存器组最低索引部分。
    eg:vnsrl.wi v0,v0,3  #SEW = (2*SEW) >> SEW,当前LMUL=1
    该指令为narrow逻辑右移指令,源操作数EEW=2*SEW,目的操作数EEW=SEW。当前源寄存器组为v0,v1组成,目的寄存器为v0。当前目的寄存器和源寄存器的交叠部分就发生在源寄存器的低索引部分,这种交叠不会有问题,但是倘若把目的寄存器由v0换成了v1,那么当源寄存器v0在执行时,就覆盖了v1中的部分元素,也就是运算结果覆盖了还没有执行的元素的内容,这就会导致运算结果出错。
    3.目的向量寄存器EEW比源向量寄存器的EEW大,首先源向量寄存器LMUL必须大于等于1,并且交叠部分只在目的寄存器组最高索引部分。
    eg:vext.vf4 v0,v6 #Zero-extend SEW/4 source to SEW destination,当前LMUL=8
    该指令为0扩展指令,该指令为了使数据匹配对应的格式位宽。源操作数EEW=SEW/4,目的操作数EEW=SEW。
    src                    6 7
    dst 0 1 2 3  4 5 6 7 
    src中的v6寄存器将会扩展到目的寄存器的v0-3处。src中v7的第一个1/4的部分扩展到v4,第二个1/4的部分扩展到v5,第三个1/4的部分扩展到v6,此时v6里面的源操作数早已被用掉,因此该覆盖不会有问题,v7中最后1/4就覆盖整个v7。假如该指令变成vext.vf4 v4,v6,那么会出现源寄存器中的元素还没被执行,就被覆盖的错误情况。 
    以上三条约束,均是为了保证还没有被操作的源不要被运算结果所覆盖。示意图如下:
    

    在这里插入图片描述

    5.3.矢量掩码

    很多向量指令都支持掩码执行。被遮罩的元素不会产生异常。目的向量寄存器被遮罩的元素根据vtype.vma进行操作。遮罩的需求来源于循环中的if语句,本来单纯的循环,并且循环内元素没有dependency,是可以靠编译器进行向量化的。但是有一类循环内插入了条件语句,要当条件满足时,才能执行后续的操做,这种情况,如果指令集架构上没支持,那么编译器就不能成功将代码向量化。
    eg:
    for (i = 0 ; i <64 ; i++) {
        if(X[i] != 0) {
           Y[i] = a * X[i]; 
           }
     }
    将上诉代码转换为带有遮罩的向量指令:
    vmul.vx v2,v1,t0,v0.t
    在基向量扩展中,用来控制掩码向量指令执行的掩码值通过向量寄存器v0提供。一个v0寄存器就足够,是因为首先掩码寄存器中的元素都是1bit,其次是一条向量指令中能操作的最大元素个数由最大分组和最小元素位宽决定,前面已经分析过最大能取到的元素个数实际上就是VLEN大小。因此刚好一个向量寄存器就可以表示所有向量指令的掩码。riscv-v-spec中定义了一些掩码操做的指令,这些指令,源寄存器和目的寄存器中的元素都是掩码,并且这些指令不会受LMUL和SEW的影响。
    这里掩码在指令里,也可以看作操作数。只是受限于指令编码的位宽限制,没有多余的域再去规定掩码操作数的寄存器索引了,所以当前的spec上将v0作为掩码寄存器,任何掩码指令得到的掩码结果,在被需要掩码的指令使用之前,需要将掩码值写到v0寄存器中。
    

    5.3.1.掩码编码

    用v0[i]和指令编码中的vm域联合表示指令掩码。
    指令编码的vm域为1:表示无掩码的向量操作。
    指令编码的vm域为0:如果v0.mask[i]=1,表示对应的向量寄存器元素要被执行。若v0.mask[i]=0,那么对应的元素不被操做,目的寄存器对应的元素按照vma的配置执行相应的策略。
    

    5.4.元素定义

    向量指令执行期间的元素索引,可以分成四个没有交集的子集。
    prestart elememts:元素索引小于vstart寄存器中的初始值,对应的元素集合。该元素集合不产生异常,也不会更新目的向量寄存器;
    active elements:表示在向量指令执行过程中,在向量长度设置范围内,且掩码有效的元素集合。该元素集合可以产生异常并且也可以更新目的向量寄存器;
    inactive elements:表示在向量指令执行过程中,在向量长度设置范围内,但是掩码无效的严肃集合。该元素集合不产生异常,vma=0的设置下,不更新目的向量寄存器;vma=1的设置下,被掩码掩蔽的元素将被写1。
    tail elemememts:表示在向量执行过程中,超过了向量设置的长度。该元素集合不产生异常,vta=0的设置下,不更新目的向量寄存器;vta=1的设置下,tail元素将被写1。当分组因子LMUL<1时,超过LMAX的元素也纳入tail元素集合中。
    除上述定义的子集外。inactive和active的集合还可以统称body element。该集合在prestart elements之后,在tail elements之前。
    

    18.Exception Handling

    异常和中断从广义上来看,都叫做异常。RISC-V 架构规定,在处理器的程序执行过程中, 一旦遇到异常发生,则终止当前的程序流,处理器被强行跳转到一个新的 PC 地址。该过程在 RISC-V 的架构中定义为“陷阱(trap)”, 字面含义为“跳入陷阱”,更加准确 的意译为“进入异常”(摘自手把手教你设计CPU)。在一条向量指令执行的过程中遇到了陷阱,需要将当前指令pc保存在*epc中,并且需要记录当前遇到异常时的元素索引到vstart,以便退出异常服务程序后能恢复原先执行的程序。
    如果发生异常时,只记录指令的pc指针,没有用vstart记录发生异常时的元素索引,那么需要保证该指令的执行是原子的,这加大了设计控制难度。并且向量指令设计为原子的,在有些long latency的指令执行过程中,会导致中断的响应非常缓慢。(思考假如指令不是原子的,那么出现异常直接被打断,又没有保存被打断时的索引,那么下一次从这一条指令重新开始执行会有什么问题)
    中断通常是由外设(中断源)产生的,而异常通常是由程序执行过程中遇到的异常。因此中断是一种外因,通常不能精确定位到某条指令引起,因为外设发起中断的时间是偶然的,程序执行过程中遇到中断,任何指令都可能碰到,这些倒霉的指令只是一个背锅侠,因此称这种中断为异步异常。异常的产生通常是内因,比如某条指令解码的时候出错,或者执行的时候进行了除0的操做,这些异常都可以被精确的定位到某一条指令。并且同样的程序执行n遍,都是能复现的。因此称这种异常为同步异常。
    对于异步异常,还可以被细分为精确异步异常和非精确异步异常。精确和不精确的区分主要在于进入异常的程序能否精确区分到某条指令的边界。比如某条指令之前的指令都执行完了,而该条指令之后的指令都没有执行,外部的中断就是一种精确异步异常。而非精确异步异常是指当前处理器的状态可能是一个很模糊的状态,没法明确的根据一条指令划界。对于非精确异步异常举个例子,比如写存指令,访问存储器需要一定的时间,但是为了流水线高效率的执行,通常写存指令发出去,没等到写的响应有没有正确响应,后续的指令就在继续执行。那么有可能出现等访问完成,发现出现了异常,此时已经过去了很多条指令了,难以精准确的定位到某一条指令。
    异常可能发生在处理器流水线的各个阶段,为了保证处理器按照指令先后顺序处理异常,需要将异常的处理放在commit阶段。
    

    18.1.Precise vector traps

    精确的向量陷阱具有如下要求:
    1.所有比陷阱指令旧的指令都已经完成了commit过程。
    2.所有比陷阱指令新的指令都应该被flush掉,不允许新指令更改处理器的状态。
    3.陷阱指令中,所有小于vstart元素索引的元素操做都完成了commit。
    4.陷阱指令中,所有大于等于vstart元素索引的元素都不会被执行。但是如果操做重新开始该指令,可以将处理器恢复到正确的状态,那么该条约束可以放松。
    
    举个例子,对于具有幂等性的存储区域进行操做,就可以允许发生trap对应的索引之后的元素改变存储器的状态,而不具有幂等性的存储区域就不能允许大于等于vsart索引的元素更新存储器状态。幂等性是指多次执行一个操做与执行一次该操做得到的结果相同,该操做就可以说是幂等的。
    跳出异常服务程序时,需要从vstart记录的元素索引开始。这是因为比如有些指令源寄存器与目的寄存器有交叠,那么可能vstart之前的源寄存器内容已经被目的寄存器覆盖了,那么重新执行这部分元素,将会得到错物的结果。
    

    18.2.Imprecise vector traps

    非精确向量陷阱,spec中写的是比*epc新的指令可能已经commit,比*epc老的指令有可能还在执行。对这个地方我是有疑问的,因为即使是超标量流水线,也会经历一个inorder-outoforder-inorder的过程,也就是真正commit的时候,指令肯定是会按照程序指令顺序,依次改变处理器状态,怎么会出现比*epc老的指令还在执行。鉴于这部分理解的有分叉,我就不多描述了,希望能和大家一起讨论清楚。
    

    最后

    由于最近visio没法儿用,所以有些流程本来可以画图的部分只能文字描述,大家先凑合看。
    
    展开全文
  • C++ STL之vector常用指令

    2013-09-06 01:22:00
    vector,相当于动态数组,数组大小可变。声明vector以后,自动在内存中分配一块连续的内存空间进行数据存储。 vector在内部进行插入、删除操作时间复杂度O(n)。 1、初始化 vector<int> v1; //空的vector ...
  • (STL)vector的常用指令

    2016-05-06 14:04:37
    个人博客Vector成员函数 函数 表述 c.assign(beg,end) c.assign(n,elem) 将[beg; end)区间中的数据赋值给c。 将n个elem的拷贝赋值给c。 c.at(idx) 传回索引idx...
  • rs1储存需要处理的向量大小(外部提出的需求),即AVL(application vector length),该指令会将AVL与VLMAX作比较后取较小值赋给vl,并赋值给rd。这样做的目的是一方面如果AVL>VLMAX,用来处理的向量元素不超过SEW...
  • Vector的理解与使用大全(C++)

    千次阅读 2021-04-08 14:12:47
    关于vector二.vector的定义与使用三.vector相关函数四.相关链接 一.关于vector 1. 什么是vector? 向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container)。跟任意其它类型容器一样,它能够存放...
  • std::vector介绍

    千次阅读 2014-05-27 10:20:36
    ::std::vector a_; int i_; double d_; ::std::string s_; }; 本文介绍了C++标准库中的容器类vector,分析了它的优点,并且建议在应用程序中使用它作为动态数组的优先选择,而不是MFC的CArray   在一些...
  • intel 并行指令集编程手册
  • vector使用说明

    2010-06-04 00:48:10
    该资源全面介绍了vector NTI9.0的使用说明 为你的研究带来方便
  • Aparapi_vector_instruction 我的研究。 尝试将向量相关指令添加到 Aparapi 中。
  • Android使用Vector进行适配和瘦身

    万次阅读 2018-01-02 12:12:32
    Android Vector在android5.0开始google提供了Vector的支持,到现在为止google已经提供了低版本的兼容,Vector的技术也越来越完善,因此日后在android中使用Vector是一个趋势。Android Vector的优势: Vector图像可以...
  • Vector Pascal是针对SIMD多核指令集(例如AVX和SSE2或Xeon-Phi)的语言。 它具有一个SIMD编译器,该编译器支持并行向量运算,循环展开,公共子表达式删除等。它用Java实现。
  • RISCV 向量指令集和NICE接口学习笔记

    千次阅读 2021-04-05 09:08:12
    向量指令集用例 可以对照下面的说明来看这个例子 .text .balign 4 .global vvaddint32 # vector-vector add routine of 32-bit integers # void vvaddint32(size_t n, const int*x, const int*y, int*z) # { for ...
  • Android vector pathData命令介绍

    千次阅读 2018-01-28 00:03:46
    vector xmlns:android = "http://schemas.android.com/apk/res/android" android:width = "100dp" android:height = "100dp" android:viewportHeight = "100.0" android:viewportWidth = "100.0" > ...
  • GDB调试技巧-打印vector的元素值

    千次阅读 2019-05-28 00:16:22
    GDB调试技巧-打印vector的元素值 我们平常在使用GDB调试程序的时候,往往需要查看一个STL容器里面存储的元素的值是多少。但是用GDB的p命令打印容器,我们会得到一堆乱七八糟的东。比如有一个vector<int> nums ...
  • Android vector 标签

    2018-04-03 14:35:04
    Vect简介 Vector 就是 Android 的 SVG 实现。Vector是Android 5.0 之后才出来的,不过从 AppCompat23.2 开始,Google开始支持在5.0版本以下使用Vectorvector功能可以自己通过AS导入,可参考这边文章: ...
  • 本文介绍了用于Java开发机器学习和深度学习的Vector API 英语原文链接 https://software.intel.com/en-us/articles/vector-api-developer-program-for-java Vector API教程介绍什么是SIMD?什么是Vector API?Vector...
  • CAPL浏览器——概述 CAPL程序的编译 数据库的访问 导入/导出ASCII文件 CAPL编程概述 CAPL程序的应用 CAPL程序对于事件的响应 ...CAPL 指令块、CAPL 中输出文本 传输信号 周期性消息发送的CAPL示例 环境变量过程的示例
  • 说明: STL源码分析系列博客的使用的是https://www.sgi.com/tech/stl/download.html ...STL源码分析之vector(一) 基本数据类型及构造函数STL源码分析之vector(二)—核心函数 push_back及insert_aux STL源码分析之vecto
  • UDS(Unified diagnostic service) 是由ISO 14229-1标准定义的一套国际通用的诊断服务指令,规定了诊断请求和响应的格式。在整个体系中处于最上层的应用层。 诊断指令通过 CAN(ISO 11898) 网络进行传输,当然...
  • CPU指令集介绍

    千次阅读 2020-09-13 11:21:49
    (1)什么是指令集 参考: http://product.pconline.com.cn/itbk/bjbzj/notebook/1109/2522116.html 所谓指令集,就是CPU中用来计算和控制计算机系统的一套指令的集合,而每一种新型的CPU在设计时就规定了一系列...
  • Android Vector详解

    千次阅读 2016-12-23 10:07:05
    两年前写书的时候,就在研究Android L提出的Vector,可研究下来发现,完全不具备兼容性,相信这也是它没有被广泛使用的一个原因,经过Google的不懈努力,现在Vector终于迎来了它的春天。 4.jpg 在文章...
  • Vector Projection Vector Rejection向量投影 shaderforge 向量运算 以一张向量运算的全家福开始 Append(数据维度的附加) & Component Mask(数据维度的分解) 1. 节点说明 ...
  • GCC vector 叠加示例

    千次阅读 2012-04-24 16:01:06
    一个简单的vector叠加示例 GCC version 4.7.0   #include #include #include #include typedef union i4factor{ __v4si v; int u4[4]; } __attribute__((aligned(16))) i4factor_t; //typedef int v4sf _...
  • Vector标签解析

    千次阅读 2017-03-27 10:56:29
    Vector使用前言如果要说Vector,就不得不提SVG了,首先,需要讲解两个概念——SVG和Vector。SVG,即Scalable Vector Graphics 矢量图,这种图像格式在前端中已经使用的非常广泛了。Vector,在Android中指的是Vector ...
  • 1、模板类vector 1.1 基本定义及操作 vector是一种动态数组,可在运行阶段设置vector对象的长度,...其次,vector包含在名称空间std中,可使用using编译指令、using声明或std::vector第三,模板使用不同的语法来指出
  • 在C++11中,vector的初始化和等号赋值都出现了这样的语法 vector<int> nums({1, 2, 3, 4, 5}); vector<int> nums3 = vector({1, 2, 3, 4, 5}); vector<int> nums2; nums...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 49,101
精华内容 19,640
关键字:

vector指令