性能优化-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.mzph.cn/news/645478.shtml

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

相关文章

库函数的模拟实现

目录 模拟实现strlen 模拟实现strcpy 模拟实现strcmp 模拟实现strcat 模拟实现strstr 模拟实现memcpy 模拟实现memmove 模拟实现strlen #define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>int MyStrlen(char* str) {int len 0;while (str[len] ! \0)len;retu…

GitLab升级版本(任意用户密码重置漏洞CVE-2023-7028)

目录 前言漏洞分析影响范围查看自己的GitLab版本升级路程 升级过程13.1.1113.8.8 - 14.0.1214.3.614.9.5 - 16.1.6 前言 最近GitLab发了个紧急漏洞需要修复&#xff0c;ok接到命令立刻着手开始修复&#xff0c;在修复之前先大概了解一下这个漏洞是什么东西 漏洞分析 1、组件…

单点登陆(SSO)基于CAS实现前后端分离的SSO系统开发「IDP发起」

关于其他前端常见登录实现单点登录方案&#xff0c;请见「前端常见登录实现方案 单点登录方案 」 前沿 单点登录&#xff08;SSO&#xff09;&#xff0c;英文全称为 Single Sign On。 SSO 是指在多个应用系统中&#xff0c;用户只需要登录一次&#xff0c;就可以访问所有相互…

redis-持久化-1

Redis 提供了2个不同形式的持久化方式。 RDB&#xff08;Redis DataBase&#xff09; AOF&#xff08;Append Of File&#xff09; 一、Redis持久化之RDB 1.什么是RDB 在指定的时间间隔内将内存中的数据集快照写入磁盘&#xff0c; 也就是行话讲的Snapshot快照&#xff0c…

搭建通讯猫类似的TCP服务端

最近需要一个公网的TCP服务端平台来做4G模组的发包测验&#xff0c;通讯猫(http://www.tongxinmao.com/App/Detail/id/1)貌似使用不了&#xff0c;就干脆在自己的腾讯云上搭建了简单的TCP服务端。 我们搭建可以在服务器上使用Python、Java、C#等语言自行编写服务器程序。 目前是…

KADB使用PXF连接KES验证

验证环境 KADB版本&#xff1a;Greenplum Database 6.0.0 build dev.V003R002C001B0181.d354cc9215 KES版本&#xff1a;KingbaseES V008R006C007B0012 Java版本&#xff1a;openjdk version "1.8.0_262" PXF部署 以下操作假设KADB和KES已经部署完成并且启动正常…

nomachine下载安装使用教程(远程桌面)

文章目录 参考链接 nomachine 参考链接 [1]【远程桌面】nomachine下载安装使用教程、zerotier下载安装使用教程超详细&#xff1a;https://blog.csdn.net/weixin_44029896/article/details/128555481 [2] 树莓派开发之旅&#xff08;一&#xff09;——使用Nomachine进行远…

selenium执行出现异常,SessionNotCreatedException ChromeDriver only supports

问题现状&#xff1a; 运行程序报错&#xff1a; selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version 114 Current browser version is 121.0.6167.85 with binary path /App…

重构改善既有代码的设计-学习(四):简化条件逻辑

1、分解条件表达式&#xff08;Decompose Conditional&#xff09; 可以将大块代码分解为多个独立的函数&#xff0c;根据每个小块代码的用途&#xff0c;为分解而得的新函数命名。对于条件逻辑&#xff0c;将每个分支条件分解成新函数还可以带来更多好处&#xff1a;可以突出条…

cartopy绘图特征

文章目录 添加特征add_feature区域截取 添加特征 cartopy中最基础的地图绘制函数是coastlines&#xff0c;即海岸线线条图&#xff0c;这很复合我们的绘图直觉&#xff0c;毕竟陆地和海洋的边缘&#xff0c;是最清晰的地理特征。而在此之上&#xff0c;还可以通过add_feature函…

【嵌入式学习】网络通信基础-项目篇:简单UDP聊天室

源码已在GitHub开源&#xff1a;0clock/LearnEmbed-projects/chat 实现的功能 客户端功能&#xff1a; 上线发送登录的用户名[yes] 发送消息和接收消息[yes] quit退出 服务器端功能&#xff1a; 统计用户上线信息&#xff0c;放入链表中[yes] 接收用户信息并给其他用户发送消…

Windows11 鼠标拖动文件到CMD控制终端窗口无效,无法显示具体文件路径

对于某些用户来说&#xff0c;他们可能会在Windows 11上遇到鼠标拖动文件到CMD控制终端时&#xff0c;无法显示具体文件路径的情况。 系统更新&#xff0c;习惯基础操作无效了&#xff0c;真的有点烦&#xff0c;不会提问就无计可施。 果然善于提问&#xff0c;才有果子吃 问…

架构师之路(十五)计算机网络(网络层协议)

前置知识&#xff08;了解&#xff09;&#xff1a;计算机基础。 作为架构师&#xff0c;我们所设计的系统很少为单机系统&#xff0c;因此有必要了解计算机和计算机之间是怎么联系的。局域网的集群和混合云的网络有啥区别。系统交互的时候网络会存在什么瓶颈。 ARP协议 地址解…

分类预测 | Matlab实现GRU-Attention-Adaboost基于门控循环单元融合注意力机制的Adaboost数据分类预测/故障识别

分类预测 | Matlab实现GRU-Attention-Adaboost基于门控循环单元融合注意力机制的Adaboost数据分类预测/故障识别 目录 分类预测 | Matlab实现GRU-Attention-Adaboost基于门控循环单元融合注意力机制的Adaboost数据分类预测/故障识别分类效果基本描述程序设计参考资料 分类效果 …

SpringBoot项目多数据源配置与MyBatis拦截器生效问题解析

在日常项目开发中&#xff0c;由于某些原因&#xff0c;一个服务的数据源可能来自不同的库&#xff0c;比如&#xff1a; 对接提供的中间库&#xff0c;需要查询需要的数据同步数据&#xff0c;需要将一个库的数据同步到另一个库&#xff0c;做为同步工具的服务对接第三方系统…

Redis面试

1.说说什么事redis Redis是一种基于键值对的NoSql数据库。 Redis中的value支持string&#xff08;字符串&#xff09;、hahs&#xff08;哈希&#xff09;、list、set、zset&#xff08;有序集合&#xff09;、bitmaps&#xff08;位图&#xff09;&#xff0c;HyperLoglog等数…

Java基础数据结构之排序

一.排序 1.什么是稳定性 假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&#xff0c;若经过排序&#xff0c;这些记录的相对次序保持 不变&#xff0c;即在原序列中&#xff0c; r[i]r[j] &#xff0c;且 r[i] 在 r[j] 之前&#xff0c;而在排序后的序…

php 文件上传

目录 1 php.ini 配置文件的修改 2.系统返回码详解 错误级别 4.上传简单示例 5.php代码简单优化 1 php.ini 配置文件的修改 配置项说明file_uploads on 为 开启文件上传功能&#xff0c; off 为关闭 post_max_size 系统允许的 POST 传参的最大值 &#xff0c;默认 8M upl…

【JSON2WEB】03 go的模板包html/template的使用

Go text/template 是 Go 语言标准库中的一个模板引擎&#xff0c;用于生成文本输出。它使用类似于 HTML 的模板语言&#xff0c;可以将数据和模板结合起来&#xff0c;生成最终的文本输出。 Go html/template包实现了数据驱动的模板&#xff0c;用于生成可防止代码注入的安全的…

电信宽带配置动态域名和端口映射

需求: 家宽映射动态域名访问内网服务 动态域名:18081>电信光猫:18081>Openwrt软路由:18081>主机192.168.3.172:8081 目前网络结构&#xff1a; 电信光猫192.168.1.1 Openwrt软路由192.168.3.1 主机192.168.3.172上8081端口起了一个nginx-docker服务 前置条件&#x…