性能优化-HVX 指令介绍

「发表于知乎专栏《移动端算法优化》」

本文主要介绍了 HVX 指令相关的知识,包括 HVX 寄存器相关内容,指令的背景依赖,部分常用 intrinsic HVX 指令。具体指令的详细内容及使用还需阅读 HVX 的指令文档,以及细致的实践操作。

🎬个人简介:一个全栈工程师的升级之路!
📋个人专栏:高性能(HPC)开发基础教程
🎀CSDN主页 发狂的小花
🌄人生秘诀:学习的本质就是极致重复!

目录

一、概述

二、HVX硬件介绍

2.1 处理器架构

2.2 HVX向量寄存器

三、HVX指令

3.1 VLIW

3.2 HVX指令分类

3.3 汇编指令格式介绍

四、HVX intrinsics指令介绍

4.1 向量类型格式

4.2 HVX intrinsics格式

4.3 Load/Store

4.4 Arithmetic

4.5 Logical and compare

4.6 Multiply

4.7 Shift

4.8 Permute

4.9 其他

五、总结


一、概述

随着骁龙多核AI引擎的发展,异构计算算力有了巨大提升,为了发挥CDSP HVX在AI和CV的优势,开发者必须熟练掌握HVX的指令。但是HVX指令多而复杂,基础指令就有上百条及一些复杂的特殊指令,写起来更是费劲。为了发挥其性能高的优势,开发者必须使用intrinsic或汇编的方式编排代码,因此完全掌握HVX指令至关重要。

本文接下来首先详细介绍HVX硬件架构,在了解架构的基础上着重介绍HVX指令相关内容,然后针对HVX开发中必须要掌握的HVX instrinsic展开图文并茂的说明。主要内容有HVX架构、寄存器、VLIW、汇编格式及ALU的Intrinsics指令使用,最后针对几个常用的Intrinsics 指令结合实例进行说明。

二、HVX硬件介绍

2.1 处理器架构

在上一篇文章中HVX架构简介详细介绍了HVX软件和硬件架构,为了能够更加深入理解HVX指令执行过程,此处详细说明Hexagon主处理器和hexagon HVX SIMD协处理的关系和指令执行过程。

Hexagon主处理和HVX协处理架构图

2.1.1 Hexagon主处理器的架构

Hexagon主处理器架构图

  • Hexagon 主处理器架构,由四个硬件线程组成,硬件线程共享一个指令cache(I)、数据cache(D)。
  • 每个硬件线程都可以访问执行 Hexagon 标量指令,这些指令在单个或成对 32 位寄存器上执行定点和浮点运算。
  • 每个硬件线程包含通用寄存器(R0-R31)、预测判断寄存器(P0-P3)及执行单元(S0-S3)。
  • 静态分组超长指令集架构(VLIW)。

2.1.2 HVX协处理器的架构

HVX协处理架构图

  • Hexagon HVX协处理架构,有四个HVX硬件线程,硬件线程间共享L2-cache和VTCM内存。
  • 每个硬件线程都可以访问执行HVX向量指令。
  • 每个硬件线程包含向量寄存器(V0-V31)、预测判断寄存器(Q0-Q3)、执行单元(ALU)及内存读写单元。
  • HVX指令可以使用Hexagon主处理器的标量操作数。
  • 静态分组超长指令集架构(VLIW)。

2.1.3 指令执行过程

Hexagon架构图

  • 指令执行过程从L2-cache加载指令和数据到I-cache和D-chche,经过Sequencer指令打包然后在执行单元(S0-S3)上执行。

2.1.4 Hexagon主处理器和HVX协处理指令共享

Hexagon core硬件线程通过访问 HVX 寄存器文件(HVX Context)来使用 HVX 协处理器。Hexagon core和HVX之间的指令包基于vector FIFO结构进行共享。因此同一个指令包中HVX指令可以与标量指令混合,如下所示,vector FIFO和HVX的处理器结构和指令混合汇编代码。

{r8 = add(r15,#128) //标量指令r14 = add(r13,#-1)r13 = add(r3,#128)v8 = vmem(r10+#0)  // HVX指令
}

以上讲解了整体架构,接下来着重说明HVX寄存器的内容。

2.2 HVX向量寄存器

2.2.1 HVX向量长度

HVX协处理随着架构不同向量寄存器长度分为64B(512bit)和128B(1024bit)模式,V66架构既包含64B也包含128B向量长度(两个HVX核为64B、两个HVX核为128B),V68架构以后只包含128B向量长度(四个HVX核均为128B),如下图所示。

向量寄存器长度

2.2.2 HVX向量寄存器数量及类别

本文中我们以V68架构展开说明,如上图右侧框内。每个HVX协处理器包含:

  • 32个1024bit的向量寄存器,V0-V31。
  • 4个128bit的预测寄存器,Q0-Q3。

向量寄存器

存放向量数据,每个向量元素的类型必须相同。向量寄存器根据处理元素的大小可以划分为 32/64/128个通道,对应数据类型为32bit/16bit/8bit。

1024-bit SIMD register

向量寄存器数据在SIMD指令操作中需要指定元素类型,例如:

// vo向量寄存器数据指定数据类型为signed byte
v0.b = vadd(v1.b,v2.b) //汇编
HVX_Vector Q6_Vb_vadd_VbVb(HVX_Vector Vu, HVX_Vector Vv) //intrinsic

常用的元素类型如下:

汇编标识intrinsic 标识含义
.bVbsigned byte
.ubVubunsigned byte
.hVhsigned halfword
.uhVuhunsigned halfword
.wVwsigned word
.uwVuwunsigned word

预测寄存器

预测寄存器每个bit位对应每个向量寄存器中的byte,既可以用来保存字节、半字和全字的比较结果。预测寄存器用Q表示,以下示例中,Q2代表预测寄存器的结果,在向量V5和V6中选择有效值在结果寄存器中。

// vmux选择指令,如果Q2为真,输出结果为V5,反之输出结果为V6
V4 = vmux(Q2, V5, V6)

三、HVX指令

这部分主要介绍VLIW、HVX指令分类和HVX汇编代码的格式和说明。

3.1 VLIW

3.1.1 VLIW解释说明

将指令的并行化(多发射)显式的声明在指令格式中,而处理器只负责执行指令,这种方式定义为超长指令字(Very Long Instruction Word,VLIW)体系结构。指令的并行化可由编译器完成,也可由开发者手写汇编代码完成,如下图所示红色圈内所示,即指令的并行化过程。

HVX的协处理指令集架构也是基于VLIW。因此,HVX协处理器有四个执行单元,理论上每个周期处理器可以执行4条指令。这4条指令看成一个指令包,取指、译码、执行单元每次都针对一个指令包进行操作,如下图所示,这就是HVX的VLIW。

指令包

虽然说指令按照VLIW并行化打包为指令包,但是也会有一定的打包规则,并不是任意组合,所以接下来会介绍HVX指令的打包规则。

3.1.2 HVX VLIW打包规则

HVX计算资源

HVX 提供了 6 种执行资源支持向量指令执行:loadstoreshiftpermute 以及 两个 multiply 。

  • 每条 HVX 指令均由上述部分资源组合而成,在S0-S3硬件资源执行
  • 每次并行执行一个指令包,由下图中指令单元负责打包分发
  • 每个指令包最多可以包含 4 条指令及一个循环结束指令,指令包需按照规则组合

VLIW打包规制

每个指令分别使用四个执行单元(shift、permute 以及 两个 multiply),且每个slot只能有一条指令,下表为HVX指令到slot执行单元的映射关系,手册中每条指令都会写可以占用的slot。

InstructionUsed Hexagon Slots
Aligned memory load0 or 1
Aligned memory store0
Unaligned memory load/store0Slot 1 must be empty.
Maximum of 3 instructions allowed in the packet
Multiplies2 or 3
Using full 32-64 bit R2 or 3
Simple ALU, permute, shif0, 1, 2, or 3

同时需要注意每个HVX指令对于执行单元的占用情况,在每条指令介绍下都会有说明,例如vmpa指令,下图红框内会对指令的HVX资源使用情况进行说明。

  • 指令包中只能包含两条load指令,或者一条load一条store指令
{V4 = vmem(iptrUp++#1)vmem(iptrUp++#1) = v4.curnopnop
}{V4 = vmem(iptrUp++#1)V5 = vmem(iptrUp++#1)nopnop
}
  • 若指令包中为简单的ALU,则均可以使用S0-S3
{ v14 = vnot(V14)                               // v14 = left bounary maskv16 = vsplat(R8)                              // 0xFF v12 = #0                                      // reset flag-collectorv6 = vlalign(v0,v2,#3)                        //[1]pixels #12
}
  • 不要在指令包中存在多个乘法,因为乘法只有在执行单元S2和S3上执行
//存在错误 -- vmpa指令需要使用S2和S3的两个乘法资源,指令包中只能包含一个vmpa指令
{ v1= vmem(iptr3++#1)                      //[1]v2.h += vmpa(v0.ub,c1c1c1c1.b)           //[1]v3.h += vmpa(v0.ub,c1c1c1c1.b)          
}

VLIW打包规制示例

以下样例为HVX协处理的指令包,指令在HVX处理器执行。

{v15 = vmem(R0++M1)vmem(R6++M1) = v14v18.uw = vrmpy(v16.ub, v17.ub)
}

上述HVX指令包完成一个1024bit数据加载,一次1024bit数据存储及地址更新,一次向量乘法(vrmpy占用两个乘法资源,在指令包中只能存在一条)。

3.2 HVX指令分类

根据指令使用的硬件资源类型不同,HVX指令分为以下几类 :

类别描述资源占用
ALU算术和逻辑运算操作使用任意一个硬件资源
Multiply乘法操作占用两个 mpy 资源或者占用两个中的一个
Shift & Bit Count位操作占用 shift 资源
Permute数据重排操作占用 xlane 资源
Load & Store数据存取操作对齐储存(vmem)会使用任意一个硬件资源 + ld 或 st 等资源。对齐取数据带 .tmp 的操作,只使用 ld资源;非对齐操作(vmemu)使用 ld 或 st + Xlane 资源;对齐存数据操作带 .new 时,只使用 st 资源。

3.3 汇编指令格式介绍

汇编格式

v<x><.><type> = <ops>(<input>)[:carry][:sat]
  • x表示32个向量寄存器的标识数字,type 为向量寄存器中数据的类型
  • type表示既可以向量寄存器中数据的类型:b、ub、h、uh、w、uw,也可以表示Aligned memory Load和Store数据的立即更新还是作为临时数据:.tmp、.new、.cur。
  • <ops> 为指令的具体指令操作标识,如 add、valignvaslvdmp 等等。

汇编示例

以有符号 halfword 的加法汇编指令为例,指令将 V4 中各 lane 的数据与 V3 中各 lane 的数据以有符号short进行进行相加,将结果存放在 V2 寄存器中。

V2.h=vadd(V3.h,V4.h)

HVX 汇编开发难点

HVX 的开发不仅仅是 SIMD 指令集的运用,汇编期 VLIW 指令的静态多发调度也尤为重要

相对于 intrinsic 指令而言

  • HVX 汇编开发需要对 HVX 指令集及硬件架构进行详细的学习 - 增加学习成本
  • HVX 汇编开发过程中需要对 HVX 指令打包,程序流程控制指令,.new\.tmp 指令,指令延迟,寄存器复用以及软件堆栈维护等多方面进行考虑 - 增加开发及维护难度,不利于迭代及增量开发
  • HVX 汇编指令严格对应硬件平台架构 - 不利于向后兼容扩展

因此,在非绝对性能瓶颈的实现中,建议使用 intrinsic 指令进行开发。

四、HVX intrinsics指令介绍

相比于汇编指令,HVX intrinsics 是一种更简单的编写 HVX 代码的方法,HVX Intrinsics 类似于 C 函数调用,在编译时由编译器替换为相应的汇编指令,使用时需要包含头文件hvx_hexagon_protos.h

4.1 向量类型格式

// 向量对格式
HVX_VectorPair
// 向量格式
HVX_Vector
// 判断向量格式
HVX_VectorPred

HVX的向量类型包含以上三种,其中HEXAGON_ARCH架构在V65以后HVX四核的向量长度为1024bit,V62到V65架构之前分为512bit和1024bit的HVX向量长度。HVX_Vector和HVX_VectorPred共用联合体,实现多种数据类型宽度兼容。

typedef union 
{size8u_t ud[MAX_VEC_SIZE_BYTES/8];size8s_t    d[MAX_VEC_SIZE_BYTES/8];size4u_t uw[MAX_VEC_SIZE_BYTES/4];size4s_t    w[MAX_VEC_SIZE_BYTES/4];size2u_t uh[MAX_VEC_SIZE_BYTES/2];size2s_t    h[MAX_VEC_SIZE_BYTES/2];size1u_t ub[MAX_VEC_SIZE_BYTES/1];size1s_t    b[MAX_VEC_SIZE_BYTES/1];size4s_t    qf32[MAX_VEC_SIZE_BYTES/4];size2s_t    qf16[MAX_VEC_SIZE_BYTES/2];size4s_t    sf[MAX_VEC_SIZE_BYTES/4];size2s_t    hf[MAX_VEC_SIZE_BYTES/2];
} mmvector_t, mmqreg_t;#ifdef __HVXDBL__typedef mmqreg_t        HEXAGON_VecPred128;typedef mmvector_t      HEXAGON_Vect1024;typedef mmvector_pair_t HEXAGON_Vect2048;#define HVX_VectorPred  HEXAGON_VecPred128#define HVX_Vector      HEXAGON_Vect1024#define HVX_VectorPair  HEXAGON_Vect2048
#elsetypedef mmqreg_t        HEXAGON_VecPred64;typedef mmvector_t      HEXAGON_Vect512;typedef mmvector_pair_t HEXAGON_Vect1024;#define HVX_VectorPred  HEXAGON_VecPred64#define HVX_Vector      HEXAGON_Vect512#define HVX_VectorPair  HEXAGON_Vect1024
#endif /*HVXDBL*/
  • HVX_VectorPair:向量对组合,由两个HVX_Vector向量组成,常用在指令运算后结果是奇偶排列结果中,例如如下数据扩展指令,把一个HVX_Vector向量扩展为HVX_VectorPair,Vdd.v[0]和Vdd.v[1]组成一个向量对(HVX_VectorPair),占用两个向量寄存器。

4.2 HVX intrinsics格式

Q6_<output_type>_<opname>_<input_type><input_type>_<mod>

1)Q6 表示高通第六代数字信号处理器,固定前缀

2) <output_type> 表示返回值类型,此处分为W和V开头,比如Ww,Vw等

  • Vw :表示输出类型为HVX_Vector,元素类型为word类型
  • Ww:表示输出类型为HVX_VectorPair,元素类型为word类型
标识符解释标识符解释
Vbint8 vectorWbint8 vector pair
Vubuint8 vectorWubuint8 vector pair
Vhint16 vectorWhint16 vector pair
Vuhuint16 vectorWuhuint16 vector pair
Vwint32 vectorWwint32 vector pair
Vuwuint32 vectorWuwuint32 vector pair

3) <opname> 表示具体操作,比如vadd,vmpy等

4) <input_type> 表示输入值类型,此处分为W、V、R和Q开头及组合,例如

HVX_VectorPair Q6_Wh_vmpy_VubRb(HVX_Vector Vu, Word32 Rt)Q6_Wh_vmpy_VubRb
  • W:表示输入类型为HVX_VectorPair向量对
  • V:表示输入类型为HVX_Vector向量
  • Q:表示输入类型为HVX_VectorPred预测向量
  • R:表示输入类型为Word标量
标识符解释标识符解释标识符解释
Vbint8 vectorWbint8 vectorpairRbint8 word
Vubuint8 vectorWubuint8 vectorpairRubuint8 word
Vhint16 vectorWhint16 vectorpairRhint16 word
Vuhuint16 vectorWuhuint16 vectorpairRuhuint16 word
Vwint32 vectorWwint32 vectorpairRword
Vuwuint32 vectorWuwuint32 vectorpairQHVX_VectorPred

5) <mod>

  • sat:表示饱和操作,例如
// Vu加Vv的结果做饱和操作
HVX_Vector Q6_Vb_vadd_VbVb_sat(HVX_Vector Vu, HVX_Vector Vv)
  • rnd:表示四舍五入操作,例如
// Vu加Vu的结果做进位输出操作
HVX_Vector Q6_Vw_vadd_VwVwQ_carry(HVX_Vector Vu, HVX_Vector Vv, HVX_VectorPred* Qp)
  • s1:表示左移一位操作,例如
// Vu加Vb的结果取平均值的四舍五入结果
HVX_Vector Q6_Vb_vavg_VbVb_rnd(HVX_Vector Vu, HVX_Vector Vv)

下面的章节将对每种类型的HVX intrinsic指令做出详细的描述。

4.3 Load/Store

按照地址对齐或者地址非对齐的方式在向量寄存器和内存之间搬移数据

HVX LOAD指令

  • 示例:
// 对齐地址
HVX_Vector v1 = *(HVX_Vector *)src;// 非对齐地址
typedef long HEXAGON_Vect_UN __attribute__((__vector_size__(VLEN)))__attribute__((aligned(4)));
HEXAGON_Vect_UN v2 = *(HEXAGON_Vect_UN *)src; 
  • 作用:完成内存到寄存器的数据搬运
  • 伪代码:Vd=vmem(Rt+#0)
  • 结果示意图:紫色框内代表输入数据

  • 相关指令
// 地址对齐加载             // 地址非对齐加载 
Vd=vmem(Rt)                vd = vmemu(Rt) 
// 立即使用加载            
Vd.cur=vmem(Rt+#s4)    
// 临时立即使用加载
Vd.tmp=vmem(Rt+#s4) 

HVX STORE指令

  • 示例:
// 对齐地址
HVX_Vector v1;
*(HVX_Vector *)src = v1;// 非对齐地址
HEXAGON_Vect_UN v2;
typedef long HEXAGON_Vect_UN __attribute__((__vector_size__(VLEN)))__attribute__((aligned(4)));
*(HEXAGON_Vect_UN *)src = v2; 
  • 作用:完成寄存器到内存的数据搬运
  • 伪代码:vmem(Rt+#0)= Vd
  • 结果示意图

  • 相关指令
// 字节使能对齐存储
If (Qv4) vmem(Rt) = Vs
// 新存储
vmem(Rt)=Os8.new
// 对齐存储
vmem(Rt)=Vs
// 非对齐存储
vmemu(Rt)=Vs

HVX 非对齐内存操作

  • vmemu非对齐操作指令,需要进行两次内存访问,与地址对齐访问相比会增加功耗和带宽,因此开发中我们建议尽量使用对齐LOAD和STORE指令。
  • vmemu非对齐指令占用两个HVX单元,分别是slot0和slot1。

HVX 对齐内存操作

  • vmem对齐操作指令,内存地址必须保证128Byte对齐。
  • 可以使用memalign(128,xxx)申请128Byte对齐内存,也可以使用ION相关函数申请。

4.4 Arithmetic

基本加减算法指令,完成向量数据的算法操作

vector 算术指令

  • 示例:HVX_Vector Q6_Vb_vsub_VbVb_sat(HVX_Vector Vu, HVX_Vector Vv)
  • 作用:两个输入vector加减法运算
  • 伪代码
for (i = 0; i < VELEM(8); i++) 
{Vd.b[i] = sat_s8(Vu.b[i] - Vv.b[i]) ;
}
  • 示意图

  • 相关指令
// type 类型加法:Vd.type = Vu.type + Vv.type;
HVX_Vector Q6_Vtype_vadd_VtypeVtype(HVX_Vector Vu, HVX_Vector Vv);// type 类型加法,结果进行饱和操作:Vd.type = sat_type(Vu.type + Vv.type);
HVX_Vector Q6_Vtype_vadd_VtypeVtype_sat(HVX_Vector Vu, HVX_Vector Vv);// type 类型减法:Vd.b = Vu.b - Vv.b;
HVX_Vector Q6_Vtype_vsub_VtypeVtype(HVX_Vector Vu, HVX_Vector Vv);

type:b、ub、h、uh、w、uw

vector pair 算术指令

  • 示例:HVX_VectorPair Q6_Ww_vadd_VhVh(HVX_Vector Vu, HVX_Vector Vv)
  • 作用:向量寄存器 Vu 与 Vv 进行相加操作,结果输出长度是输入的两倍,存放于向量对寄存器里,按照奇偶的方式排列。
奇偶排列:输入数据下标索引02468的结果存放在单独寄存器是偶排列,反之,13579...127是奇排列。
  • 伪代码
for (i = 0; i < VELEM(32); i++) 
{Vdd.v[0].w[i] = Vu.w[i].h[0] + Vv.w[i].h[0];Vdd.v[1].w[i] = Vu.w[i].h[1] + Vv.w[i].h[1];
}
  • 指令示意图

vadd

  • 结果示意图

  • 相关指令
// type 类型加法:vdd.type = Vu.type + Vv.type;
HVX_VectorPair Q6_Wtype0_vadd_Vtype1Vtype1(HVX_Vector Vu, HVX_Vector Vv)// type 类型减法:vdd.type = Vu.type - Vv.type;
HVX_VectorPair Q6_Wtype0_vsub_Vtype1Vtype1(HVX_Vector Vu, HVX_Vector Vv)// type 类型加法累加:vdd.type += Vu.type + Vv.type;
HVX_VectorPair Q6_Wtype0_vaddacc_Wtype0Vtype1Vtype1(HVX_VectorPair Vxx, HVX_Vector Vu, HVX_Vector Vv)

type0: h、w

type1: ub、h、uh

4.5 Logical and compare

eq表示相等,gt表示大于,eqand表示等于判断及与操作,gtand表示大于判断及与操作,eqxacc表示等于判断及异或操作,gtxacc表示大于判断及异或操作,eqor表示等于判断及或操作,gtor表示大于判断及或操作。

条件比较指令

  • 示例:HVX_VectorPred Q6_Q_vcmp_eq_VbVb(HVX_Vector Vu, HVX_Vector Vv)
  • 作用:向量寄存器 Vu 与 Vv 进行比较运算,结果存储在预测寄存器中
  • 伪代码
for( i = 0; i < VWIDTH; i += 1) 
{QxV[i] =((Vu.b[i] == Vv.b[i]) ? 0x1 : 0);
}
  • 示例结果

  • 相关指令
// type 类型相等判断:Qd4 = Vu == Vv;
HVX_VectorPred Q6_Q_vcmp_eq_Vtype0Vtype0(HVX_Vector Vu, HVX_Vector Vv);// type 类型大于判断:Qd4 = Vu > Vv;
HVX_VectorPred Q6_Q_vcmp_gt_Vtype1Vtype1(HVX_Vector Vu, HVX_Vector Vv);// type 类型相等判断及与操作:Qx4 &= Vu == Vv;
HVX_VectorPred Q6_Q_vcmp_eqand_QVtype0Vtype0(HVX_VectorPred Qx, HVX_Vector Vu, HVX_Vector Vv);// type 类型大于判断及与操作:Qx4 &= Vu > Vv;
HVX_VectorPred Q6_Q_vcmp_gtand_QVtype1Vtype1(HVX_VectorPred Qx, HVX_Vector Vu, HVX_Vector Vv); // type 类型相等判断及异或操作:Qx4 ^= Vu == Vv;
HVX_VectorPred Q6_Q_vcmp_eqxacc_QVtype0Vtype0(HVX_VectorPred Qx, HVX_Vector Vu, HVX_Vector Vv);// type 类型大于判断及异或操作:Qx4 ^= Vu > Vv;
HVX_VectorPred Q6_Q_vcmp_gtxacc_QVtype1Vtype1(HVX_VectorPred Qx, HVX_Vector Vu, HVX_Vector Vv);// type 类型相等判断及或操作:Qx4 |= Vu == Vv;
HVX_VectorPred Q6_Q_vcmp_eqor_QVtype0Vtype0(HVX_VectorPred Qx, HVX_Vector Vu, HVX_Vector Vv);// type 类型大于判断及或操作:Qx4 |= Vu > Vv;
HVX_VectorPred Q6_Q_vcmp_gtor_QVtype1Vtype1(HVX_VectorPred Qx, HVX_Vector Vu, HVX_Vector Vv); 

type0:b、h、w

type1:b、ub、h、uh、w、uw

位操作指令

  • 示例:HVX_VectorPred Q6_Q_not_Q(HVX_VectorPred Qs)
  • 作用:将条件寄存器 Qs 进行取反操作,结果保存至条件寄存器中
  • 伪代码
for (i = 0; i < VELEM(8); i++) 
{QdV[i] = !QsV[i];
}
  • 示意图:蓝色框代表输入数据,红色框代表输出数据

  • 相关指令
// 条件寄存器非与操作:Qd4 = Qs & ((!Qt) 或者 (Qt))
HVX_VectorPred Q6_Q_and_QQ<n>(HVX_VectorPred Qs, HVX_VectorPred Qt);// 条件寄存器非或操作:Qd4 = Qs | ((!Qt) 或者 (Qt))
HVX_VectorPred Q6_Q_or_QQ<n>(HVX_VectorPred Qs, HVX_VectorPred Qt);// 条件寄存器非操作:Qd4 = !Qs
HVX_VectorPred Q6_Q_not_Q(HVX_VectorPred Qs);// Vu 与标量 Rt 进行 u8 类型位与操作,填充条件寄存器:
// Qd4 = Vu.ub & Rt.ub => Qd4[i] = Vu.ub[i] & Rt.ub[i % 4] ? 1 : 0;
HVX_VectorPred Q6_Q_vand_VR(HVX_Vector Vu, Word32 Rt);// Vu 与标量 Rt 进行 u8 类型位与操作,结果与 Qx 进行或操作,Qx4 |= (Vu.ub & Rt.ub);
HVX_VectorPred Q6_Q_vandor_QVR(HVX_VectorPred Qx, HVX_Vector Vu, Word32 Rt);// 根据 !Qu 使用 Rt 以 u8 类型选择性填充 Vd
HVX_Vector Q6_V_vand_QnR(HVX_VectorPred Qu, Word32 Rt);// 根据 !Qu 使用 Rt 以 u8 类型选择性填充 Vd, 然后与 Vx 进行或运算
HVX_Vector Q6_V_vandor_VQnR(HVX_Vector Vx, HVX_VectorPred Qu, Word32 Rt);// 根据 Qu 使用 Rt 以 u8 类型选择性填充 Vd
HVX_Vector Q6_V_vand_QR(HVX_VectorPred Qu, Word32 Rt);// 根据 Qu 使用 Rt 以 u8 类型选择性填充 Vd, 然后与 Vx 进行或运算
HVX_Vector Q6_V_vandor_VQR(HVX_Vector Vx, HVX_VectorPred Qu, Word32 Rt);// 根据条件寄存器 Qt,以 u8 类型从 Vu 及 Vv 中选择填充 Vd
HVX_Vector Q6_V_vmux_QVV(HVX_VectorPred Qt, HVX_Vector Vu, HVX_Vector Vv);// 根据条件寄存器 Qt,以 u8 类型对 Vu 及 Vv 进行交换操作,结果保存至 HVX_VectorPair 中
HVX_VectorPair Q6_W_vswap_QVV(HVX_VectorPred Qt, HVX_Vector Vu, HVX_Vector Vv);

逻辑操作指令

  • 示例:HVX_Vector Q6_V_vnot_V(HVX_Vector Vu)
  • 作用:对向量寄存器Vu按位逻辑取反
  • 伪代码
for (i = 0; i < VELEM(16); i++) 
{Vd.uh[i] = ~Vu.uh[i] ;
}
  • 示意图

  • 相关指令
// Vu和Vv向量寄存器按位逻辑与
HVX_Vector Q6_V_vand_VV(HVX_Vector Vu, HVX_Vector Vv)// 对Vu向量寄存器按位取反
HVX_Vector Q6_V_vnot_V(HVX_Vector Vu)// Vu和Vv向量寄存器按位逻辑或
HVX_Vector Q6_V_vor_VV(HVX_Vector Vu, HVX_Vector Vv)// Vu和Vv向量寄存器按位逻辑异或
HVX_Vector Q6_V_vxor_VV(HVX_Vector Vu, HVX_Vector Vv)

取极值操作

  • 示例指令:HVX_Vector Q6_Vb_vmax_VbVb(HVX_Vector Vu, HVX_Vector Vv)
  • 作用:将两个数值向量寄存器进行比较操作,输出最大值到目的寄存器
  • 伪指令
for (i = 0; i < VELEM(8); i++) 
{Vd.b[i] = (Vu.b[i] > Vv.b[i]) ? Vu.b[i] : Vv.b[i] ;
}
  • 示意图

  • 相关指令
// type 类型最小值: Vd = min(Vu, Vv);
HVX_Vector Q6_Vtype_vmin_VtypeVtype(HVX_Vector Vu, HVX_Vector Vv);// type 类型最大值: Vd = max(Vu, Vv);
HVX_Vector Q6_Vtype_vmax_VtypeVtype(HVX_Vector Vu, HVX_Vector Vv);

type:b、ub、h、uh、w、uw

饱和操作

  • 示例指令:HVX_Vector Q6_Vub_vsat_VhVh(HVX_Vector Vu, HVX_Vector Vv)
  • 作用:向量寄存器 Vu Vv 中 s16 类型数据,进行 u8 类型饱和操作,然后寄存器高低交替保存至 Vd 寄存器中
  • 伪代码
for (i = 0; i < VELEM(16); i++) 
{Vd.uh[i].b[0]=usat_8(Vv.h[i]);Vd.uh[i].b[1]=usat_8(Vu.h[i]);
}
  • 示意图

  • 相关指令
// s16 类型 Vu Vv 饱和至 u8 类型
HVX_Vector Q6_Vub_vsat_VhVh(HVX_Vector Vu, HVX_Vector Vv);// u32 类型 Vu Vv 饱和至 u16 类型
HVX_Vector Q6_Vuh_vsat_VuwVuw(HVX_Vector Vu, HVX_Vector Vv);// s32 类型 Vu Vv 饱和至 s16 类型
HVX_Vector Q6_Vh_vsat_VwVw(HVX_Vector Vu, HVX_Vector Vv;

绝对值操作

  • 示例指令:HVX_Vector Q6_Vb_vabs_Vb(HVX_Vector Vu)
  • 作用:向量寄存器以 s8 的数值类型进行取绝对值操作,结果依旧为 s8 类型
  • 伪代码
for (i = 0; i < VELEM(8); i++) 
{Vd.b[i] = ABS(Vu.b[i]);
}
  • 示例结果

  • 相关指令
// type 类型绝对值操作,结果进行饱和操作
HVX_Vector Q6_Vtype0_vabs_Vtype0_sat(HVX_Vector Vu);// type 类型绝对值操作
HVX_Vector Q6_Vtype0_vabs_Vtype0(HVX_Vector Vu);// type 类型绝对值差操作
HVX_Vector Q6_Vtype1_vabsdiff_Vtype2Vtype2(HVX_Vector Vu, HVX_Vector Vv);

type0:b、h、w

type1:ub、uh、uw

type2:ub、h、uh、w

数值扩展

  • 示例指令:HVX_VectorPair Q6_Wuh_vzxt_Vub(HVX_Vector Vu)
  • vzxt 为无符号扩展指令,将原数值类型进行正向调整,扩展后使用 HVX_VectorPair 进行存储,数值按照偶数位和奇数位分别存储至 Vdd.v[0] 和 Vdd.v[1] 中
  • 伪代码:注意结果寄存器存储时按照奇偶交替的顺序存储
// vzxt:u8 -> u16 类型转换
for (i = 0; i < VELEM(16); i++) 
{Vdd.v[0].uh[i] = Vu.uh[i].ub[0];Vdd.v[1].uh[i] = Vu.uh[i].ub[1];
}
  • 指令示意图

  • 相关指令
// u16 类型进行零填充扩展至 u32 类型
HVX_VectorPair Q6_Wuw_vzxt_Vuh(HVX_Vector Vu);// s16 类型进行有符号填充扩展至 s32 类型
HVX_VectorPair Q6_Ww_vsxt_Vh(HVX_Vector Vu);// s8 类型进行有符号填充扩展至 s16 类型
HVX_VectorPair Q6_Wh_vsxt_Vb(HVX_Vector Vu);

4.6 Multiply

向量乘标量运算

  • 示例:HVX_VectorPair Q6_Wh_vmpy_VubRb(HVX_Vector Vu, Word32 Rt)
  • 作用:向量寄存器 Vu 与标量 Rt 的元素进行乘法操作,数值按照偶数位和奇数位分别存储至 Vdd.v[0] 和 Vdd.v[1] 中。
  • 伪代码
for (i = 0; i < VELEM(16); i++) 
{Vdd.v[0].h[i] = (Vu.uh[i].ub[0] * Rt.b[(2*i+0)%4]);Vdd.v[1].h[i] = (Vu.uh[i].ub[1] * Rt.b[(2*i+1)%4]) ;
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// s16 类型向量乘 s16 标量运算,结果左移一位且四舍五入,做饱和操作
HVX_Vector Q6_Vh_vmpy_VhRh_s1_rnd_sat(HVX_Vector Vu, Word32 Rt)// s16 类型向量乘 s16 标量运算,结果左移一位且饱和操作
HVX_Vector Q6_Vh_vmpy_VhRh_s1_sat(HVX_Vector Vu, Word32 Rt)// s16 类型向量乘 s16 标量运算
HVX_VectorPair Q6_Wh_vmpy_VubRb(HVX_Vector Vu, Word32 Rt)// type 类型向量乘加 type 标量运算
HVX_VectorPair Q6_Wtype_vmpyacc_WtypeVtypeRtype(HVX_VectorPair Vxx, HVX_Vector Vu, Word32 Rt)// s16 类型向量乘加 s16 标量运算
HVX_VectorPair Q6_Ww_vmpyacc_WwVhRh_sat(HVX_VectorPair Vxx, HVX_Vector Vu, Word32 Rt)

向量乘加运算(乘标量)

  • 示例:HVX_VectorPair Q6_Wh_vmpa_WubRb(HVX_VectorPair Vuu, Word32 Rt)
  • 作用:向量对寄存器Vuu乘以Rt标量的各元素,数值按照偶数位和奇数位分别存储至 Vdd.v[0] 和 Vdd.v[1] 中。
  • 伪代码
for (i = 0; i < VELEM(16); i++) 
{Vdd.v[0].h[i] = (Vuu.v[0].uh[i].ub[0] * Rt.b[0]) + (Vuu.v[1].uh[i].ub[0] * Rt.b[1]);Vdd.v[1].h[i] = (Vuu.v[0].uh[i].ub[1] * Rt.b[2]) + (Vuu.v[1].uh[i].ub[1] * Rt.b[3]);
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// type 类型的 Vuu 与 type 类型的Rt进行乘加操作,结果为 type 类型
HVX_VectorPair Q6_Wtype0_vmpa_Wtype1Rtype2(HVX_VectorPair Vuu, Word32 Rt);// type 类型的 Vuu 与 type 类型的Rt进行乘加操作,结果与Vxx进行累加,结果为 type 类型
HVX_VectorPair Q6_Wtype0_vmpaacc_Wtype0Wtype1Rtype2(HVX_VectorPair Vxx, HVX_VectorPair Vuu, Word32 Rt);

type0:h、w

type1:b、ub、h、uh

type2:b、ub

乘法 vdmpy操作(乘标量)

  • 示例:HVX_Vector Q6_Vh_vdmpy_VubRb(HVX_Vector Vu, Word32 Rt)
  • 作用:向量寄存器 Vu 与标量 Rt 进行乘法操作,结果相邻对累加操作。
  • 伪代码
for (i = 0; i < VELEM(16); i++) 
{Vd.h[i] =  (Vu.uh[i].ub[0] * Rt.b[(2 * i    ) % 4]);Vd.h[i] += (Vu.uh[i].ub[1] * Rt.b[(2 * i + 1) % 4]) ;
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// type 类型向量与 type 类型标量乘法 2 路归约操作
HVX_Vector Q6_Vtype0_vdmpy_Vtype1Rb(HVX_Vector Vu, Word32 Rt);// type 类型向量与 type 类型标量完成 2 路归约操作
HVX_Vector Q6_Vtype0_vdmpyacc_Vtype0Vtype1Rb(HVX_Vector Vx, HVX_Vector Vu, Word32 Rt);
  1. type0:h、w
  2. type1:ub、h

乘法 vrmpy 操作(乘标量)

  • 示例:HVX_Vector Q6_Vuw_vrmpy_VubRub(HVX_Vector Vu, Word32 Rt)
  • 作用:向量寄存器 Vu 与标量 Rt u进行乘法操作,结果相邻 4 元素累加操作,输出结果为u32类型。
  • 伪代码:
for (i = 0; i < VELEM(32); i++) 
{Vd.uw[i]  = (Vu.uw[i].ub[0] * Rt.ub[0]);Vd.uw[i] += (Vu.uw[i].ub[1] * Rt.ub[1]);Vd.uw[i] += (Vu.uw[i].ub[2] * Rt.ub[2]);Vd.uw[i] += (Vu.uw[i].ub[3] * Rt.ub[3]) ;
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// u8 类型向量与 s8 类型标量完成 4 路归约操作,结果为 s32 类型
HVX_Vector Q6_Vtype0_vrmpy_VubRtype1(HVX_Vector Vu, Word32 Rt);// u8 类型向量与 u8 类型标量完成 4 路归约操作,然后进行 Vx 结果累加,结果为 u32 类型
HVX_Vector Q6_Vtype0_vrmpyacc_Vtype0VubRtype1(HVX_Vector Vx, HVX_Vector Vu, Word32 Rt);
  1. type0:w、uw
  2. type1:b、ub

4.7 Shift

立即数移位

  • 示例:HVX_Vector Q6_Vuw_vlsr_VuwR(HVX_Vector Vu, Word32 Rt)
  • 作用:向量寄存器 Vu 根据立即数 Rt 进行逻辑右移。
  • 伪代码
// vlsr
for (i = 0; i < VELEM(32); i++) 
{Vd.uw[i] = (Vu.uw[i] >> (Rt & (32 - 1)));
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// s16 类型根据 Rt 进行算数右移,结果四舍五入后饱和至 s8 类型,Vu Vv 移位结果奇偶交疊存储之 Vd 中
HVX_Vector Q6_Vb_vasr_VhVhR_rnd_sat(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt);// type 类型根据 Rt 进行逻辑右移,结果为 type 类型
HVX_Vector Q6_Vtype_vlsr_VtypeR(HVX_Vector Vu, Word32 Rt);// s16 类型根据 Rt 进行算数右移
HVX_Vector Q6_Vh_vasr_VhR(HVX_Vector Vu, Word32 Rt);// s16 类型算数右移 Rt,结果饱和至 s8 类型,Vu Vv 移位结果奇偶交疊存储之 Vd 中
HVX_Vector Q6_Vb_vasr_VhVhR_sat(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt);
  1. type:ub、uh、uw

Narrowing移位

  • 示例指令:

HVX_Vector Q6_Vh_vasr_VwVwR_sat(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt)

  • 作用:算术右移,结果饱和操作,输入位宽是输出的两倍。
  • 伪代码
for (i = 0; i < VELEM(32); i++) 
{shamt = Rt & 0xF; Vd.w[i].h[0]=[sat_16](Vv.w[i] >> shamt); Vd.w[i].h[1]=[sat_16](Vu.w[i] >> shamt); 
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// type类型(round)算术右移,结果饱和操作
HVX_Vector Q6_Vtype0_vasr_Vtype1Vtype1R_rnd_sat(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt)// type类型算术右移,结果饱和操作                                            
HVX_Vector Q6_Vtype0_vasr_Vtype1Vtype1R_sat(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt)
  1. type0:b、ub、h、uh
  2. type1:h、uh、w、uw

4.8 Permute

向量拼接

  • 示例:HVX_Vector Q6_V_valign_VVR(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt)
  • 作用:向量寄存器 Vu 和 Vv 根据 Rt 参数进行移位拼接,向量寄存器内数值按照 u8 类型处理。
  • 伪代码
unsigned shift = Rt & (VWIDTH-1); 
for(i = 0; i < VWIDTH; i++) 
{ Vd.ub[i] = (i + shift>=VWIDTH) ? Vu.ub[i + shift - VWIDTH] \: Vv.ub[i + shift]; 
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// 向量 Vu 与 Vv 根据 (Rt & (VWIDTH-1)) 的起始进行移位拼接操作
HVX_Vector Q6_V_valign_VVR(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt);// 向量 Vu 与 Vv 根据 (VWIDTH - (Rt & (VWIDTH-1))) 的起始进行移位拼接操作
HVX_Vector Q6_V_vlalign_VVR(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt);// 向量 Vu 根据 Rt 进行向右循环重排
HVX_Vector Q6_V_vror_VR(HVX_Vector Vu, Word32 Rt);

向量旋转

  • 示例:HVX_Vector Q6_V_vror_VR(HVX_Vector Vu, Word32 Rt)
  • 作用:用于1个vector内部的循环移位,Rt最大127
  • 伪代码
for (k = 0; k < VWIDTH; k++) 
{ Vd.ub[k] = Vu.ub[(k + Rt)&(VWIDTH - 1)]; 
}
  • 指令示意图

向量交织操作

  • 示例:HVX_Vector Q6_Vb_vshuffe_VbVb(HVX_Vector Vu, HVX_Vector Vv);
  • 作用:取 Vv 及 Vu 中的偶数位元素,然后进行交织存储到 Vd 寄存器
  • 伪代码
for (i = 0; i < VELEM(16); i++) 
{Vd.uh[i].b[0]=Vv.uh[i].ub[0];Vd.uh[i].b[1]=Vu.uh[i].ub[0] ;
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// 偶数位交织
HVX_Vector Q6_Vb_vshuffe_VbVb(HVX_Vector Vu, HVX_Vector Vv);// 奇数位交织
HVX_Vector Q6_Vb_vshuffo_VbVb(HVX_Vector Vu, HVX_Vector Vv);// 奇偶数位交织
HVX_VectorPair Q6_Wb_vshuffoe_VbVb(HVX_Vector Vu, HVX_Vector Vv);

向量Shuffle - Deal操作

  • 示例
// 将向量内偶数位元素存放至目标寄存器低半位,奇数位元素存放至寄存器高半位
HVX_Vector Q6_Vb_vdeal_Vb(HVX_Vector Vu);// 向量内以低半位和高半位为单位进行奇偶数位解交织操作
HVX_Vector Q6_Vb_vshuff_Vb(HVX_Vector Vu);
  • 作用: vdeal 指令实现将 Vu 向量内的偶数位元素存放到 Vd 寄存器的低半位,奇数位元素存放到 Vd 寄存器的高半位,实现向量中数据奇偶数位的重排(后续简称交织操作)。 vshuff 指令则以向量的低半位和高半位为基本单位,进行奇偶数位的交织重排,与vdeal 互为逆操作(后续简称解交织操作)。
  • 伪代码
//vdeal
for (i = 0; i < VELEM(16); i++) 
{Vd.ub[i ] = Vu.uh[i].ub[0];Vd.ub[i+VBITS/16] = Vu.uh[i].ub[1];
}//vshuff
for (i = 0; i < VELEM(16); i++) 
{Vd.uh[i].b[0]=Vu.ub[i];Vd.uh[i].b[1]=Vu.ub[i+VBITS/16];
}
  • 指令示意图

  • 结果示意图

  • 相关指令
// 两个向量的交织操作,接受一个常量 Rt 参数,Rt 参数影响交织操作的元素大小
HVX_VectorPair Q6_W_vdeal_VVR(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt);// 两个向量的解交织操作,接受一个常量 Rt 参数,Rt 参数影响交织操作的元素大小
HVX_VectorPair Q6_W_vshuff_VVR(HVX_Vector Vu, HVX_Vector Vv, Word32 Rt);

4.9 其他

上述章节讲解了基本指令,并没有对复杂指令展开说明,例如查表指令、直方图统计、gather/scatter和vrdelta等复杂指令,后面我们会专门针对以上提到的复杂指令做详细的讲解说明。例如如下所示的vrdelta、vdelta指令的交织网络结构,这种复杂的指令网络设计结构,必须结合设计paper和使用规则才能完全理解。

vrdelta/vdelta指令网络

五、总结

本文主要介绍了 HVX 指令相关的知识,包括 HVX 寄存器相关内容,指令的背景依赖,部分常用 intrinsic HVX 指令。具体指令的详细内容及使用还需阅读 HVX 的指令文档,以及细致的实践操作。

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

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

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

相关文章

(十二)Head first design patterns代理模式(c++)

代理模式 代理模式&#xff1a;创建一个proxy对象&#xff0c;并为这个对象提供替身或者占位符以对这个对象进行控制。 典型例子&#xff1a;智能指针... 例子&#xff1a;比如说有一个talk接口&#xff0c;所有的people需要实现talk接口。但有些人有唱歌技能。不能在talk接…

Flink中的容错机制

一.容错机制 在Flink中&#xff0c;有一套完整的容错机制来保证故障后的恢复&#xff0c;其中最重要的就是检查点。 1.1 检查点&#xff08;Checkpoint&#xff09; 在流处理中&#xff0c;我们可以用存档读档的思路&#xff0c;将之前某个时间点的所有状态保存下来&#xf…

JAVA 学习 面试(六)数据类型与方法

数据类型 基本数据类型 为什么float3.4报错 3.4 默认是浮点double类型的&#xff0c;如果赋值给float是向下转型&#xff0c;会出现精度缺失&#xff0c;&#xff0c;需要强制转换 Switch支持的数据类型&#xff1f; byte、short、int、char 、 enum 、 String 基本类型与包…

使用 Swift 代码优化项目编译速度

引言 软件的性能是评价一个软件质量的重要指标&#xff0c;尤其在今天这个时代&#xff0c;性能已成为大型项目不可或缺的考虑因素之一。对于用户量极大的软件&#xff0c;如网银系统、在线购物商城等&#xff0c;更是必须保证其高效稳定的性能。在这种背景下&#xff0c;优化…

Python-基础篇-类与对象/面向对象程序设计-py脚本

面向对象基础 第一个面向对象 class Cat:def eat(self):print("小猫爱吃鱼")def drink(self):print("小猫要喝水")# 创建猫对象 tom Cat()tom.eat() tom.drink()print(tom)addr id(tom) print("%x" % addr)新建两个猫对象 class Cat:def ea…

Dockerfile-xxxx

1、Dockerfile-server FROM openjdk:8-jdk-alpine WORKDIR /app COPY . . CMD java -Xms1536M -Xmx1536M -XX:UseG1GC -jar -Dlog4j2.formatMsgNoLookupstrue -Dloader.pathresources,lib -Duser.timezoneGMT-05 /app/server-main-1.0.0.jar 2、Dockerfile-bgd #FROM openjdk…

MySQL-SQL-DQL

DQL-介绍 DQL-语法 基本查询 1、查询多个字段 2、设置别名 3、去除重复记录 条件查询 1、语法 2、条件 聚合函数 1、介绍 2、常见的聚合函数 3、语法 分组查询 1、语法 2、where与having区别 排序查询 1、语法 2、排序方式 分页查询 1、语法 DQL-执行顺序

【代码随想录】刷题笔记Day54

前言 差单调栈就结束代码随想录一刷啦&#xff0c;回家二刷打算改用python补充进博客&#xff0c;小涛加油&#xff01;&#xff01;&#xff01; 647. 回文子串 - 力扣&#xff08;LeetCode&#xff09; 双指针法 中心点外扩&#xff0c;注意中心点可能有一个元素可能有两个…

MacOS受欢迎的数据库开发工具 Navicat Premium 15 中文版

Navicat Premium 15 Mac是一款数据库管理工具&#xff0c;提供了一个全面的解决方案&#xff0c;用于连接、管理和维护各种数据库系统。以下是Navicat Premium 15 Mac的一些主要功能和特点&#xff1a; 软件下载&#xff1a;Navicat Premium 15 中文版下载 多平台支持&#xff…

PLC从HTTP服务端获取JSON文件,解析数据到寄存器

智能网关IGT-DSER集成了多种PLC协议&#xff0c;方便实现各种PLC与HTTP服务端之间通讯。通过网关的参数配置软件绑定JSON文件的字段与PLC寄存器地址&#xff0c;配置URL&#xff0c;即可采用POST命令&#xff0c;将JSON文件提交给HTTP的服务端&#xff1b; 服务端有返回的JSON&…

从 Context 看 Go 设计模式:接口、封装和并发控制

文章目录 Context 的基本结构Context 的实现和传递机制为什么 Context 不直接传递指针案例&#xff1a;DataStore结论 在 Go 语言中&#xff0c; context 包是并发编程的核心&#xff0c;用于传递取消信号和请求范围的值。但其传值机制&#xff0c;特别是为什么不通过指针传递…

RTDETR 引入 UniRepLKNet:用于音频、视频、点云、时间序列和图像识别的通用感知大卷积神经网络 | DRepConv

大卷积神经网络(ConvNets)近来受到了广泛研究关注,但存在两个未解决且需要进一步研究的关键问题。1)现有大卷积神经网络的架构主要遵循传统ConvNets或变压器的设计原则,而针对大卷积神经网络的架构设计仍未得到解决。2)随着变压器在多个领域的主导地位,有待研究ConvNets…

Ubuntu Desktop 隐藏 / 显示文件和文件夹

Ubuntu Desktop 隐藏 / 显示文件和文件夹 1. GUI hot key2. Show hidden and backup filesReferences 1. GUI hot key Ctrl H: 隐藏 / 显示文件和文件夹 2. Show hidden and backup files Edit -> Preferences -> Views References [1] Yongqiang Cheng, https://yo…

【分布式技术】消息队列Kafka

目录 一、Kafka概述 二、消息队列Kafka的好处 三、消息队列Kafka的两种模式 四、Kafka 1、Kafka 定义 2、Kafka 简介 3、Kafka 的特性 五、Kafka的系统架构 六、实操部署Kafka集群 步骤一&#xff1a;在每一个zookeeper节点上完成kafka部署 ​编辑 步骤二&#xff1a…

【数据结构】 链栈的基本操作 (C语言版)

目录 一、链栈 1、链栈的定义&#xff1a; 2、链栈的优缺点&#xff1a; 二、链栈的基本操作算法&#xff08;C语言&#xff09; 1、宏定义 2、创建结构体 3、链栈的初始化 4、链栈的进栈 5、链栈的出栈 6、获取栈顶元素 7、栈的遍历输出 8、链栈的判空 9、求链…

一周时间,开发了一款封面图生成工具

介绍 这是一款封面图的制作工具&#xff0c;根据简单的配置即可生成一张好看的封面图&#xff0c;目前已有七款主题可以选择。做这个工具的初衷来自平时写文章&#xff0c;都为封面图发愁&#xff0c;去图片 网站上搜索很难找到满意的&#xff0c;而且当你要的图如果要搭配上文…

Eureka整合seata分布式事务

文章目录 一、分布式事务存在的问题二、分布式事务理论三、认识SeataSeata分布式事务解决方案1、XA模式2、AT模式3、SAGA模式4.SAGA模式优缺点&#xff1a;5.四种模式对比 四、微服务整合Seata AT案例Seata配置微服务整合2.1、父工程项目创建引入依赖 2.2、Eureka集群搭建2.3、…

02-编程猜谜游戏

上一篇&#xff1a;01-开始Rust之旅 本章通过演示如何在实际程序中使用 Rust&#xff0c;你将了解 let 、 match 、方法、关联函数、外部crate等基础知识。 本章将实现一个经典的初学者编程问题&#xff1a;猜谜游戏。 工作原理如下&#xff1a;程序将随机生成一个介于 1 和 10…

Qt —— 自定义飞机仪表控件(附源码)

示例效果 部署环境 本人亲测版本Vs2017+Qt5.12.4,其他版本应该也可使用。 源码1 qfi_ADI::qfi_ADI( QWidget *parent ) :QGraphicsView ( parent ),m_scene ( nullptr )

牛客周赛 Round 18 解题报告 | 珂学家 | 分类讨论计数 + 状态DP

前言 整体评价 前三题蛮简单的&#xff0c;T4是一个带状态的DP&#xff0c;这题如果用背包思路去解&#xff0c;不知道如何搞&#xff0c;感觉有点头痛。所以最后还是选择状态DP来求解。 欢迎关注 珂朵莉 牛客周赛专栏 珂朵莉 牛客小白月赛专栏 A. 游游的整数翻转 这题最好…