当前位置: 首页 > news >正文

arm 内联汇编基础

一、 Arm架构寄存器体系熟悉

基于arm neon 实现的代码有 intrinsic 和inline assembly 两种实现。

1.1 通用寄存器

arm v7 有 16 个 32-bit 通用寄存器,用 r0-r15 表示。

arm v8 有 31 个 64-bit 通用寄存器,用 x0-x30 表示,和 v7 不一样的是,这 31 个寄存器也可以作为 32-bit 寄存器来用,用 w0-w30 表示,其中 wn 是 xn 的低 32 位,如下图所示:

函数前四个参数,会按顺序被放入寄存器 r0-r3(w0-w3), 剩下会采用压栈的方式保存

寄存器寄存器别名用途
r0a1第一个函数参数: Scratch 寄存器
r1a2第二个函数参数: Scratch 寄存器
r2a3第三个函数参数: Scratch 寄存器
r3a4第四个函数参数: Scratch 寄存器
r4v1寄存器变量
r5v2寄存器变量
r6v3寄存器变量
r7v4寄存器变量
r8v5寄存器变量
r9v6 rfp寄存器变量 实际的帧指针
r10sl栈接线
r11fp参数指针
r12ip临时
r13sp栈指针
r14lr连接寄存器
r15pc程序计数

1.2 向量寄存器

armv7 包含 16 个 128-bit 向量寄存器,用 q0-q15 表示,其中每个 q 寄存器又可以拆分成两个 64-bit 向量寄存器来用,用 d0-d31 来表示。

armv8 则有更多的向量寄存器,32 个 128-bit 向量寄存器,用 v0-v31 来表示。

每个 128-bit 向量寄存器可以当做:

  • 包含 2 个 64-bit 元素的向量寄存器来用,表达形式是 vn.2d;

  • 包含 4 个 32-bit 元素的向量寄存器来用,表达形式是 vn.4s;

  • 包含 8 个 16-bit 元素的向量寄存器来用,表达形式是 vn.8h;

  • 包含 16 个 8-bit 元素的向量寄存器来用,表达形式是 vn.16b;

或者每个向量寄存器也可以只用低 64-bit:

  • 1 个 64-bit 元素的向量寄存器来用,表达形式是 vn.1d;

  • 2 个 32-bit 元素的向量寄存器来用,表达形式是 vn.2s;

  • 4 个 16-bit 元素的向量寄存器来用,表达形式是 vn.4h;

  • 8 个 8-bit 元素的向量寄存器来用,表达形式是 vn.8b;

利用指令集加速,无一例外地要利用专用寄存器这种在 CPU 上稀少、宝贵的资源。专用寄存器用少了 CPU 的性能不能充分发挥,用多了则会产生寄存器溢出 (Register Spilling)(https://blog.csdn.net/qq_41112170/article/details/90286091) 这种对性能有明显负面影响的问题。因此,我们至少需要了解在编写 Neon 代码时,有多少个专用寄存器可供利用。

二、内联汇编

2.1 基础写法

__asm__ qualifiers ( // 汇编代码部分 
: OutputOperands //在内联汇编代码中被修改的变量列表 
: InputOperands //在内联汇编代码中用到的变量列表 
: Clobbers //在内联汇编代码中用到的寄存器列表 );

qualifiers:一般是用 volatile 修饰词 ,关键字__volatile__:也可以写“volatile”,理由同上;__volatile__是可选的,作用是禁止编译器对后面汇编指令再进行优化。一般自己写的汇编,考虑性能,已经做过优化,编译器再优化的话,可能效果反而更差,所以通常还是带上这个关键字;

括号里:是真正的汇编代码,主要有四部分组成,第一部分是具体的汇编代码,是必须的;其他三个为辅助参数,可选;各部分之间用冒号“:”分割,即使参数为空,也要加冒号;

  • OutputOperands:在内联汇编中会被修改的变量列表,变量之间用','隔开, 每个变量的格式是: [asmSymbolicName] "constraint"(cvariablename) cvariablename:表示变量原来的名字; asmSymbolicName:表示变量在内联汇编代码中的别名,一般和 cvariablename 一样,在汇编代码中就可以通过%[asmSymbolicName]去使用该变量; constraint: 一般填=r,具体解释见文档[6]

  • InputOperands:在内联汇编中用到的所有变量列表,变量之间用','隔开, 每个变量的格式是: [asmSymbolicName] "constraint"(cexpression) 和输出不一样地方是,首先要按OutputOperands列表的顺序再列一遍,但是constraint用数字代替从0开始,然后才是写其他只读变量,只读变量constraintr

  • Clobbers: 一般是"cc", "memory"开头,然后接着填内联汇编中用到的通用寄存器和向量寄存器 "cc"表示内联汇编代码修改了标志寄存器; "memory"表示汇编代码对输入和输出操作数执行内存读取或写入操作(读写参数列表之一的变量指向的内存);

  • 输入列表 ("r" (some_input)): 这表明 some_input 是一个输入操作数,它的值在汇编执行前被读取。"r" 约束表示 some_input 被存储在某个寄存器中,具体哪个寄存器由编译器决定。

  • 输出列表 ("+r" (result)): 这表明 result 是一个输出操作数,它的值在汇编执行后被写回。"+" 约束表示 result 既可以作为输入也可以作为输出,汇编代码可以读取它的初始值,并在执行过程中更新它的值。

约束说明:

  • "r":将值放入任意一个可用的寄存器中。

  • "+r":将值放入任意一个可用的寄存器中,并且该寄存器在操作后还会被写回,即它既可以作为输入也可以作为输出。

  • "+w":类似于 "+r",但表示该值在汇编代码中可能会被修改,并且修改后的值需要写回原始变量。

  • "m":表示该值应该被加载到内存地址中,通常与指针一起使用。

asm("mov %0,%1":"+r"(val1):"r"(val2):);

由上面对指令语法的描述进行分析:

  • 输出操作数为 val1,属性为 "=r"。

  • 输入操作数为 val2,属性为 "r"

  • code 部分为 mov %1,%0,

  • %0 表示输入输出列表中的第一个操作数,

  • %1 表示操作数列表中提供的第二个操作数,以此类推,这条汇编指令很明显就是将第二个操作数(val2)赋值给第一个操作数(val1),所以最后的结果为 val1 = 222. 。

    int x=10, y;
    __asm__ ("mov %[in],%[out]": [out]"=r"(y): [in]"r"(x):
    );
    

如果指定了别名的话,那在汇编模板中,引用该变量,就可以使用别名,增加可读性,

2.2 操作符含义

  • "=" 表示只写,通常用于所有输出操作数的属性

  • "+" 表示读写,只能被列为输出操作数的属性,否则编译会报错。

  • & :只能用作输出

限定符

ARM指令集含义

r

通用寄存器

f

浮点寄存器

m

内存地址

为保持寄存器,内存数据一致性,提供三个类型

类型作用
r0…r15告诉编译器汇编代码中修改了寄存器r0…r15 (v8 是x, v)
cc告诉编译器汇编代码会导致CPU状态位的改变
memory告诉编译器汇编代码会读取或修改内存中某个地址存放的值

三、样例分析

对于刚入门优化的同学,改写汇编最好先从 C++ 改写 intrinsic 开始,然后再根据 intrinsic 的代码去改写汇编,一般 intrinsic 的指令和汇编指令都能对应的上,当然高手可以直接跳过去写汇编,但是对于新手建议还是一步步来。

而且比较重要的一点是,我认为 算法上的改进更为重要,假设你 C++ 算法层面代码已经定下来了,对于性能还想有更进一步的提升,那么可以尝试去写 neon 汇编(内联或者纯汇编),但不是说汇编是万能的,这个和你的优化经验还有算法本身的复杂度有很大关系,可能你吭哧坑次改完,发现还做了负优化,因为编译器本身也会做向量化。

3.1 两个数组加权和

第一个例子是两个数组对应元素加权和,例子足够简单,方便讲解改写汇编的一些思路。 下面代码为了可读性会相应的作简.

3.1.1 c++ 实现

bool arrWeightedAvg(const float *arr1,const float arr1Weight,const float *arr2,const float arr2Weight,const int len,float *resultArr) {for (int i = 0; i < len; ++i) {resultArr[i] = arr1[i] * arr1Weight + arr2[i] * arr2Weight;}return true;
}

3.1.2 改 intrinsic

对于 intrinsic 代码是兼容 armv7 和 v8 的,所以不同架构之间迁移也方便,不需要改代码:

bool arrWeightedAvgIntrinsic(const float *arr1,const float arr1Weight,const float *arr2,const float arr2Weight,const int len,float *resultArr) {int neonLen = len >> 2;int remain = len - (neonLen << 2);// 这里向量化主要思路是循环内每次// 处理4个元素的加权和// 所以neonLen是数组长度len除4// 而剩下的尾部元素按正常处理float *resultArrPtr  = resultArr;const float *arr1Ptr = arr1;const float *arr2Ptr = arr2;// 因为一次处理4个元素// 所以权值要拷贝4份放到// 一个float32x4_t类型变量中// 也相当于是128-bit向量寄存器float32x4_t arr1Wf4 = vdupq_n_f32(arr1Weight);float32x4_t arr2Wf4 = vdupq_n_f32(arr2Weight);for (int i = 0; i < neonLen; ++i) {// 分别读4个数组元素float32x4_t arr1f4 = vld1q_f32(arr1Ptr);float32x4_t arr2f4 = vld1q_f32(arr2Ptr);// eltwise乘法arr1f4 = vmulq_f32(arr1f4, arr1Wf4);arr2f4 = vmulq_f32(arr2f4, arr2Wf4);// eltwise加法float32x4_t resultf4 = vaddq_f32(arr1f4, arr2f4);// 写结果vst1q_f32(resultArrPtr, resultf4);arr1Ptr += 4;arr2Ptr += 4;resultArrPtr += 4;}// 处理尾部元素for (; remain > 0; remain --) {*resultArrPtr = (*arr1Ptr) * arr1Weight + (*arr2Ptr) * arr2Weight;resultArrPtr ++;arr1Ptr ++;arr2Ptr ++;}return true;
}

3.1.3 arm v7 内联汇编

bool arrWeightedAvgIntrinsic(const float *arr1,const float arr1Weight,const float *arr2,const float arr2Weight,const int len,float *resultArr) {int neonLen = len >> 2;int remain = len - (neonLen << 2);// 这里向量化主要思路是循环内每次// 处理4个元素的加权和// 所以neonLen是数组长度len除4// 而剩下的尾部元素按正常处理float *resultArrPtr  = resultArr;const float *arr1Ptr = arr1;const float *arr2Ptr = arr2;// 因为一次处理4个元素// 所以权值要拷贝4份放到// 一个float32x4_t类型变量中// 也相当于是128-bit向量寄存器float32x4_t arr1Wf4 = vdupq_n_f32(arr1Weight);float32x4_t arr2Wf4 = vdupq_n_f32(arr2Weight);for (int i = 0; i < neonLen; ++i) {// 分别读4个数组元素float32x4_t arr1f4 = vld1q_f32(arr1Ptr);float32x4_t arr2f4 = vld1q_f32(arr2Ptr);// eltwise乘法arr1f4 = vmulq_f32(arr1f4, arr1Wf4);arr2f4 = vmulq_f32(arr2f4, arr2Wf4);// eltwise加法float32x4_t resultf4 = vaddq_f32(arr1f4, arr2f4);// 写结果vst1q_f32(resultArrPtr, resultf4);arr1Ptr += 4;arr2Ptr += 4;resultArrPtr += 4;}// 处理尾部元素for (; remain > 0; remain --) {*resultArrPtr = (*arr1Ptr) * arr1Weight + (*arr2Ptr) * arr2Weight;resultArrPtr ++;arr1Ptr ++;arr2Ptr ++;}return true;
}

3.1.4 armv8 内联汇编

#ifdef __aarch64__  // armv8__asm__ volatile("mov   x0, %[arr1Weight]                  \n"   // 将weight1的值移动到通用寄存器x0中。"dup   v0.4s, w0                          \n"   //w0是x0的低32位, 复制值到向量寄存器v0中,当成4*32来使用。"mov   x1, %[arr2Weight]                  \n""dup   v1.4s, w1                          \n""0:                                       \n"    //循环结束条件,小于0."prfm  pldl1keep, [%[arr1Ptr], #128]      \n"    //预读取arr1地址开始的128bit 数据,就是4个32bit的数据。"ld1      {v2.4s}, [%[arr1Ptr]], #16      \n"    // 将数据加载到v2 向量寄存器中, 并且地址自增16个字节。"prfm  pldl1keep, [%[arr2Ptr], #128]      \n""ld1      {v3.4s}, [%[arr2Ptr]], #16      \n""fmul       v4.4s, v2.4s, v0.4s           \n"    //数组1和权重相乘。保存在v4 寄存器中。"fmul       v5.4s, v3.4s, v1.4s           \n"    // 数据2和权重相乘,保存在v5 寄存器中。"fadd       v6.4s, v4.4s, v5.4s           \n"   //将寄存器v4, v5的值相加, 保存在v6寄存器中。"subs       %[neonLen], %[neonLen], #1    \n"  // 对应 neonLen--  sub指令后面加个s表示会更新条件flag"st1    {v6.4s}, [%[resultArrPtr]], #16   \n" //将寄存器v6的结果写入到目的地址resultarrptr, 地址自增16字节。(4个数,一个数四字节)"bgt        0b                            \n"  //b跳转指令, gt 判断是不是大于0条件判断, 大于0, 跳转到0的位置。:[arr1Ptr]        "+r"(arr1Ptr),[arr2Ptr]        "+r"(arr2Ptr),[resultArrPtr]   "+r"(resultArrPtr),[neonLen]        "+r"(neonLen):[arr1Weight]     "r"(arr1Weight),[arr2Weight]     "r"(arr2Weight):"cc", "memory", "x0", "x1", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7");

反编译系统的编译文件,进行汇编代码对比,学习。

./llvm-objdump -d /home/lsq/wind/wind_develop_my/wind/build_android/src/backend/cpu/CMakeFiles/Wi

ndCPU.dir/kernel/neon/matmul_quant_test.cc.o

内联汇编的目的是进行汇编指令的优化,尽可能的直接操作寄存器,利用新特性,进行代码的加速。更多的指令需要查找官方文档进行学习。

3.2 汇编指令对应的机器码生成

.inst 0x4e80a4d8 是一个汇编指令,用于在 ARM 架构中直接插入机器码。这个指令的格式是 .inst <机器码>,其中 <机器码> 是一个 32 位或 64 位的十六进制值,表示一条机器指令。

具体来说,0x4e80a4d8 是一个 32 位的机器码。为了理解这个机器码是如何编码的,我们需要查看 ARMv8 指令集的文档,特别是 NEON 指令集的文档。

3.2.1 使用 LLVM 工具

可以安装 llvm 工具链,然后运行如下命令:、

echo "smmla v16.4s, v4.16b, v0.16b" | llvm-mc -arch=aarch64 -mattr=+neon,+i8mm -show-encoding

这将会输出汇编指令对应的机器码。如果没有安装 llvm-mc 工具,可以参考以下汇编器指令来生成机器码。

3.2.2 使用 GNU 汇编器

你可以使用 arm-none-eabi-as 工具来编译汇编代码并生成机器码。下面是一个示例:

echo ".arch armv8-a; smmla v16.4s, v4.16b, v0.16b" | arm-none-eabi-as -o - -a -

3.2.3 在线工具

https://armconverter.com/

3.2.4 反编译编译产物

./llvm-objdump -d /home/lsq/wind/wind_develop_my/wind/build_android/src/backend/cpu/CMakeFiles/WindCPU.dir/kernel/neon/matmul_quant_test.cc.o 反汇编结果:

".inst 0x4e80a490 \n" // smmla v16.4s, v4.16b, v0.16b //v0_01s// *y0_0".inst 0x4e81a4b5 \n" // smmla v21.4s, v5.16b, v1.16b //v0_0hs// *y0_1".inst 0x4e82a4da \n" // smmla v26.4s, v6.16b, v2.16b //v0_1ls// *y0_2".inst 0x4e83a4ff \n" // smmla v31.4s, v7.16b, v3.16b// v0_1hs

四、附录

https://medium.com/@warmap_/%E8%BD%AC-%E5%A6%82%E4%BD%95%E5%9C%A8c%E6%88%96c-%E4%BB%A3%E7%A0%81%E4%B8%AD%E5%B5%8C%E5%85%A5arm%E6%B1%87%E7%BC%96%E4%BB%A3%E7%A0%81-a3704e164de8

http://giantpandacv.com/project/%E9%83%A8%E7%BD%B2%E4%BC%98%E5%8C%96/AI%20%E7%A7%BB%E5%8A%A8%E7%AB%AF%E7%AE%97%E6%B3%95%E4%BC%98%E5%8C%96/%E7%A7%BB%E5%8A%A8%E7%AB%AFarm%20cpu%E4%BC%98%E5%8C%96%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/%E7%A7%BB%E5%8A%A8%E7%AB%AFarm%20cpu%E4%BC%98%E5%8C%96%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E7%AC%AC4%E5%BC%B9--%E5%86%85%E8%81%94%E6%B1%87%E7%BC%96%E5%85%A5%E9%97%A8/

相关文章:

arm 内联汇编基础

一、 Arm架构寄存器体系熟悉 基于arm neon 实现的代码有 intrinsic 和inline assembly 两种实现。 1.1 通用寄存器 arm v7 有 16 个 32-bit 通用寄存器&#xff0c;用 r0-r15 表示。 arm v8 有 31 个 64-bit 通用寄存器&#xff0c;用 x0-x30 表示&#xff0c;和 v7 不一样…...

Java语言程序设计——篇五(1)

数组 概述数组定义实例展示实战演练 二维数组定义数组元素的使用数组初始化器实战演练&#xff1a;矩阵计算 &#x1f4ab;不规则二维数组实战演练&#xff1a;杨辉三角形 概述 ⚡️数组是相同数据类型的元素集合。各元素是有先后顺序的&#xff0c;它们在内存中按照这个先后顺…...

【香橙派开发板测试】:在黑科技Orange Pi AIpro部署YOLOv8深度学习纤维分割检测模型

文章目录 &#x1f680;&#x1f680;&#x1f680;前言一、1️⃣ Orange Pi AIpro开发板相关介绍1.1 &#x1f393; 核心配置1.2 ✨开发板接口详情图1.3 ⭐️开箱展示 二、2️⃣配置开发板详细教程2.1 &#x1f393; 烧录镜像系统2.2 ✨配置网络2.3 ⭐️使用SSH连接主板 三、…...

集成学习在数学建模中的应用

集成学习在数学建模中的应用 一、集成学习概述&#xff08;一&#xff09;基知&#xff08;二&#xff09;相关术语&#xff08;三&#xff09;集成学习为何能提高性能&#xff1f;&#xff08;四&#xff09;集成学习方法 二、Bagging方法&#xff08;一&#xff09;装袋&…...

WebKit 的 Web SQL 数据库:现代浏览器的本地存储解决方案

WebKit 的 Web SQL 数据库&#xff1a;现代浏览器的本地存储解决方案 随着Web应用的不断发展&#xff0c;对本地存储的需求也日益增加。WebKit作为许多现代浏览器的核心引擎&#xff0c;提供了一种强大的本地存储解决方案&#xff1a;Web SQL 数据库。本文将详细探讨Web SQL 数…...

Yolo-World网络模型结构及原理分析(三)——RepVL-PAN

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言1. 网络结构2. 特征融合3. 文本引导&#xff08;Text-guided&#xff09;4. 图像池化注意力&#xff08;Image-Pooling Attention&#xff09;5. 区域文本匹配&…...

代码随想录——一和零(Leetcode474)

题目链接 0-1背包 class Solution {public int findMaxForm(String[] strs, int m, int n) {// 本题m&#xff0c;n为背包两个维度// dp[i][j]:最多右i个0和j个1的strs的最大子集大小int[][] dp new int[m 1][n 1];// 遍历strs中字符串for(String str : strs){int num0 …...

力扣题解(组合总和IV)

377. 组合总和 Ⅳ 给你一个由 不同 整数组成的数组 nums &#xff0c;和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。 题目数据保证答案符合 32 位整数范围。 思路&#xff1a; 本题实质上是给一些数字&#xff0c;让他们在满足和是targ…...

Postgresql主键自增的方法

Postgresql主键自增的方法 一.方法&#xff08;一&#xff09; 使用 serial PRIMARY KEY 插入数据 二.方法&#xff08;二&#xff09; &#x1f388;边走、边悟&#x1f388;迟早会好 一.方法&#xff08;一&#xff09; 使用 serial PRIMARY KEY 建表语句如下&#xf…...

【源码阅读】Sony的go breaker熔断器源码探究

文章目录 背景源码分析总结 背景 在微服务时代&#xff0c;服务和服务之间调用、跨部门调用都是很常见的事&#xff0c;但这些调用都存在很多不确定因素&#xff0c;如核心服务A依赖的部门B服务挂掉了&#xff0c;那么A本身的功能将会受到直接的影响&#xff0c;而这些都会影响…...

LeetCode题(66,69,35,88)--《c++》

66.加一 // // Created by wxj05 on 2024/7/20. // //法一 class Solution { public:vector<int> plusOne(vector<int>& digits) {bool carry true; // 进位标志for (int i digits.size() - 1; i > 0 && carry; --i) {digits[i] 1;carry digit…...

来参与“向日葵杯”全国教育仿真技术大赛~

可点击进行了解&#xff1a;“向日葵杯”全国教育仿真技术大赛 (sunmooc.cn) 本次大赛共分为四个赛道&#xff1a;自主命题赛道、教育知识图谱设计赛道、FPGA硬件扑克牌对抗赛道、EasyAR元宇宙空间设计赛道。 参赛对象 &#xff1a; 具有正式学籍的在校研究生&#xff0c;本科…...

SQL每日一题:删除重复电子邮箱

题干 表: Person -------------------- | Column Name | Type | -------------------- | id | int | | email | varchar | -------------------- id 是该表的主键列(具有唯一值的列)。 该表的每一行包含一封电子邮件。电子邮件将不包含大写字母。 编写解决方案 删除 所有重复…...

3、宠物商店智能合约实战(truffle智能合约项目实战)

3、宠物商店智能合约实战&#xff08;truffle智能合约项目实战&#xff09; 1-宠物商店环境搭建、运行2-webjs与宠物逻辑实现3-领养智能合约初始化4-宠物领养实现5-更新宠物领养状态 1-宠物商店环境搭建、运行 https://www.trufflesuite.com/boxes/pet-shop 这个还是不行 或者…...

数据库系列

目录 一、数据库的概念和作用 1.数据库的特点 2.数据模型 二、数据库系统 1.数据库管理系统 2.数据库的基本操作 一、数据库的概念和作用 数据库是指长期存储在计算机内&#xff0c;有组织的、可共享的数据集合。它可视为一个电子化的文件柜&#xff0c;用来存储电子文件…...

极狐GitLab如何启用和配置PlantUML?

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab &#xff1a;https://gitlab.cn/install?channelcontent&utm_sourcecsdn 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署…...

Shell 构建flutter + Android 生成Apk

具体步骤 #shell 具体实现和说明如下: echo "build_start_apk!" echo "编译此脚本的前提条件如下:" #在Android 项目的主工程下,进入主工程文件夹,创建build-android 文件夹,在其文件夹下有build-android.sh文件,此文件就是整个文章的脚本内容(…...

如何用手机压缩视频?手机压缩视频方法来了

高清视频的大文件大小常常成为分享和存储的障碍&#xff0c;尤其是在数据流量有限或存储空间紧张的情况下。幸运的是&#xff0c;无论是智能手机还是个人电脑&#xff0c;都有多种方法可以帮助我们轻松压缩视频文件&#xff0c;以适应不同的需求和情境。本文将介绍如何在手机上…...

Linux下如何安装配置Elastic Stack日志收集系统

安装和配置Elastic Stack日志收集系统&#xff0c;包括Elasticsearch、Logstash和Kibana&#xff0c;是一个相对复杂的过程。本篇文章将逐步引导您完成整个过程。 安装Java Elasticsearch、Logstash和Kibana都需要Java运行环境。首先&#xff0c;您需要在Linux系统上安装Java…...

【深入C++】map和set的使用

文章目录 C 中的容器分类1. 顺序容器2. 关联容器3. 无序容器4. 容器适配器5. 字符串容器6. 特殊容器 set1.构造函数2.迭代器3.容量相关的成员函数4.修改器类的成员函数5.容器相关操作的成员函数 multiset1.equal_range map1.初始化相关的函数2.迭代器3.容量相关的成员函数4.访问…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...