动态量化:大模型在端侧CPU快速推理方案

f154ca3c1181322a59f1f99608dd68d1.gif

作为一款高性能的推理引擎框架,MNN高度关注Transformer模型在移动端的部署并持续探索优化大模型在端侧的推理方案。本文介绍权重量化的模型在MNN CPU后端的推理方案:动态量化。动态量化指在运行时对浮点型feature map数据进行8bit量化,然后与8bit/4bit的权重数据做矩阵乘法并将结果反量化为浮点类型输出。

虽然动态量化方案设计的初衷是在移动端高效地推理LLM模型,但方案本身适用于所有模型的各个推理场景,例如语音识别类模型TTS、ASR等进行权重量化后也能在端侧实现高效推理,推理耗时和运行时内存占用都能降低50%,和浮点模型相比最大相对误差可以控制在10%以内。文章首先从数学表达式开始描述动态量化的计算原理,接着从模型推理的角度拆解计算过程,然后介绍MNN CPU后端的矩阵乘逻辑,最后以Qwen2-1.5B模型为例,介绍在CPU后端使用动态量化进行LLM模型推理的详细步骤并测试MNN CPU后端对LLM模型和传统CV模型的推理性能。

595fc085ee442b7944b49fc0b86c060b.png

基础知识

  态量化计算原理的解析式

032e1ed86853fe89da620caa17b45daa.png

  符号解释
  • x: 动态量化之后的 int8_t 类型输入数据,[E, L];

  • x0: 动态量化Input数据时的量化参数zeropoint值;

  • sx: 动态量化Input数据时的量化参数scale值;

  • y: 在转模型时就已经量化过的int8_t类型权重,[L, H];

  • y0: 直接从模型文件中读取权重的量化参数zeropoint

  • sy: 直接从模型文件中读取权重的量化参数scale

  • c: 常量值,下文称做bias值,[1, H]

注:

  • 下文中x,y 的反量化zeropoint特指 x0, y0;但x,y的反量化bias则指(-x0 * sx),(-y0 * sy);

  • x的反量化过程是:xf = (x - x0) * sx = x * sx + (-x0 * sx),y的反量化同理

  拆解计算过程
  • 可离线计算的项

da5f4842e4b4ad073cbaf3200993269e.png:在L维度对y reduce sum, shape: [1, H]

721ee902c1ca67ddec35c80609674622.png:y的量化参数bias,向量 [1, H]

  • 需在线计算的项

dea5a642d4f2d8de71c3aca9d462aa67.png:int8_t 类型数据矩阵乘法累加到int32_t类型, [E, H]

fe4764030cbe0dee2230303085809654.png:x和y的量化参数Scale相乘,[1, H]

7a3121b4cedb0b4c42c6218d3bf17f86.png:在L维度对 x reduce sum, shape: [E, 1]

23bd17aafd344c0abcc1d9961c745ce6.png:x 的量化参数bias, 标量

  • 在矩阵乘Kernel 内部计算的项

在矩阵乘Kernel内部计算的项一定属于1.3.2节“需在线计算的项”:

fe3d90c3e2275816b54fcfa9dd119756.png:int8_t 类型数据矩阵乘法累加到int32_t类型, [E, H]

  • kernel外计算之后传入矩阵乘Kernel的项

离线计算的项和一些需在线计算的项会通过 QuanPostTreatParameters 结构体传入矩阵乘kernel中。

struct QuanPostTreatParameters {
    const float* scale;
    const float* biasFloat;
    int32_t maxValue;
    int32_t minValue;
    int32_t useInt8 = 1; // Save result as int8_t dataType; otherwise float32.
    float roundValuePos = 0.5f;
    float roundValueNeg = -0.5f;
    float* srcKernelSum;
    float* weightQuanBias;
    float* fp32minmax;
    ssize_t blockNum = 1;
    const int32_t* bias;
    const float* extraScale = nullptr;
    const float* extraBias = nullptr;
};
// (sx * sy): x和y的量化参数Scale相乘,[1, H]
QuanPostTreatParameters.scale
// (x * sx): 在L维度对 x reduce sum,shape: [E, 1]
QuanPostTreatParameters.srcKernelSum
// (-y0*sy): 权重反量化的参数bias,shape: [1, H]
QuanPostTreatParameters.weightQuanBias
// (-x0*sx)(y-y0)*sy+c: 原矩阵乘bias与计算过程中“新增项”相加之后的bias,shape: [1, H]
QuanPostTreatParameters.biasFloat
/* 该结构体中的其他项暂时不要关注,后续会有说明。*/
MNN CPU 矩阵乘计算逻辑

本节分为两个方面,第一,MNN CPU的矩阵乘的分块计算逻辑;第二,MNN CPU的矩阵乘算子在各平台的计算加速方法。

  矩阵乘的分块计算

分块计算的优点是降低读取Input和weight数据时的cache miss率;各CPU后端(Arm、x86_x64)可使用的汇编指令不同,决定了各后端的分块大小也不同。

  • 符号说明

input shape: [E, L] ,weight shape [L, H],output shape [E, H],L是reduce维度。

EP, LP, HP 分别表示 E, L, H 维度的分块大小

  • Input和weight分块后在内存中的排列逻辑

以weight分块为例,如图1。在L和H维度分别按照LP和HP分块,如果L或H不能被LP或HP整除,会在最后一个子块补0以保证分块之后的每一个小方块的大小都是(HP*LP)。分块之后weight矩阵的12个子块之间会按照图示中的“1,2,3,...,12”的顺序在内存中排列。在各子块内部,如编号为“1”的块中有16个数据,其排列顺序按照图1. 中的“1,2,3,...,16”的顺序在内存中排列。

1d596b681e2548d49bd06c2d5f5a48d8.png

6d13009082284d0c314a0af0406a18de.png

  • 矩阵乘内部分块计算的顺序

下文中描述shape时,默认在内存中的排列顺序是括号内从右到左的顺序。图2. 描述了矩阵乘kernel内部input和weight分块后的计算逻辑:

  1. 最小计算单元是:(EP, 4*LP)的输入数据和(HP, 4*LP)的weight数据做乘加操作得到(EP, HP)的输出子块。

  2. 按照图2. 中output矩阵的子块编号,依次计算1,2,3号子块,矩阵乘kernel内部最多计算得到output矩阵中(EP * H)个数据。

  • 矩阵乘的外部调用逻辑

如果一个ONNX模型中含有MatMul算子,并且该算子的两个输入中的一个是常量,那么MNN转模型工具(MNNConvert)会将该MatMul算子转换成MNN模型中的卷积算子。MNN的矩阵乘Kernel通常在卷积算子中被调用,而MNN CPU 的卷积算子的输入输出数据在内存中的排布都是(C/4, N, H, W, 4),其中C表示Channel,N表示Batch,H和W分别表示Height和Width。上文中E、 L、 H分别对应卷积中的(N * OW * OH)、(OC * IC * KH * KW)、OC。

做矩阵分块的过程中,会将Input 数据从 (IC/4, N, IH, IW, 4)重排为(E/EP, L/LP, EP, LP),weight 数据从(OC, IC, KH, KW) 重排为(H/HP, L/LP, HP, LP)。每一个矩阵乘kernel完成的计算量最多是EP*H,所以要完成E*H的计算量,需要调用矩阵乘kernel ((E+EP)/EP) 次。如下图3. 所示,要得到最终的output矩阵的结果,则需要分2次调用矩阵乘kernel. 虽然得到的输出子块是(EP, HP)的大小,在往目标地址写入输出数据时直接按照(C/4, N, H, W, 4)的顺序。

71b584b5f3d1e55ead63f33b4fa21e55.png

  MNN CPU的矩阵乘算子在各平台的计算加速

下图4. 表示用int8_t类型的输入数据和权重数据得到浮点输出的计算过程,该过程可分为三个阶段:

第一阶段:基于 int8_t 类型的feature map和weight做矩阵乘法得到int32_t类型output,再将int32_t output 反量化到float32_t output;

第二阶段:基于 float32_t output 加上 weight_dequant_bias * intput_reduce_sum_by_axis_L;

第三阶段:最后加上 bias (维度:H),这里的bias 来自于矩阵乘A*B+C中的C和input数据的量化bias乘以weight_reduce_sum_by_axis_L之后的和。

e0c2a4a30e30d9407810f80e03b95052.png

第一阶段的计算耗时是矩阵乘kernel计算耗时中占比最大的部分,在不同CPU后端对矩阵乘的加速几乎都集中在该阶段。诸如Armv8.6和v8.2后端都有指令专门针对int8_t类型数据的内积进行计算加速,另外x86_x64的AVX512后端提供了VNNI指令集也对int8_t类型数据的计算提供了优化。

  • CPU Arm后端的矩阵乘优化

1. 在不支持SMMLA和SDOT指令的Armv8/v7后端,只能使用最基础的smull和smlal指令做int8_t数据的内积操作。为了提高每次读取数据的效率,设置 LP=16保证一次读取16个int8_t数据填满向量寄存器。另外EP=4, HP=4.

2. Armv8.6后端支持Arm汇编指令SMMLA,Armv8.2后端支持汇编指令SDOT(计算原理分别如图5. 上下图所示)。

  • SMMLA指令的输入数据是两个128位的向量寄存器,将第一个输入向量寄存器中的128位数据看作一个(2x8)的矩阵,其中每个数据类型是int8_t. 将第二个输入向量寄存器中的128位数据看作一个(8x2)的矩阵,其中每个数据类型也是int8_t. smmla指令将两个源向量寄存器做矩阵乘法得到一个(2x2)的输出矩阵,并存储在一个128位的向量寄存器中,该输出寄存器中的数据由4个int32_t类型数据组成。

  • SDOT指令的输入数据被看做是两个一维的向量,每4个对应位置的int8_t数据做内积得到1个int32_t的数据,最终结果是4个int32_t的数据。

80a8caf418febf2537a023169246c471.png

根据Armv8.6和v8.2后端指令特点,矩阵乘的分块大小分别是:

Armv8.6:EP=10, LP=8, HP=8(因为SMMLA指令对每8个int8_t数据做内积,所以LP=8)

Armv8.2:EP=12, LP=4, HP=8(因为SDOT指令对每4个int8_t数据做内积,所以LP=4)

从把向量寄存器用满的角度,分别设置EP=10和12;从最大程度降低读取weight值的cache miss率的角度,设置HP=8

  • CPU x86_x64后端的矩阵乘优化

AVX512后端的VNNI指令集专门针对int8_t整数运算进行了优化,其他后端因为没有该指令集故基于int8_t数据的矩阵乘法性能一般比基于float数据的矩阵乘差。这里直接列举出x86_x64个后端的分块大小:

不使用SSE、AVX2、AVX512等指令的C++版本:EP=2,LP=16,HP=4

SSE: EP=4,LP=16,HP=4

AVX2: EP=4,LP=4,HP=8

AVX512: EP=4,LP=4,HP=64

MNN CPU后端对端侧LLM模型推理的精度优化
  weight分组量化
  • 转模型阶段的支持

通常权重矩阵[OC, IC, KH, KW]的量化参数维度是[1, OC],即每(IC*KH*KW)个数据共享一个量化scale和zeropoint,最后求得OC * 2个量化参数(包含scale和zeropoint)。(IC*KH*KW)个数据分布差异较大会影响权值量化的精度,因此在转模型阶段会对将(IC*KH*KW)个数据分为m组进行量化,即最后得到的量化参数有(OC*m*2)个。转模型工具MNNConvert 接受可选参数 --weightQuantBlock 来指定共享量化参数的元素个数,如 --weightQuantBlock 128 会得到 (OC*(IC*KW*KH/128)*2)个量化参数。当且仅当weightQuantBlock参数设置的值能整除 (IC * KH * KW)时才会生效,否则依然得到(OC * 2)个量化参数。

  • CPU后端支持

一些计算项shape的变化:当采取weight分组量化后,等价于 reduce axis L被分成m组,上文中结构体QuanPostTreatParameters.blockNum=m. 相应地,一些项的shape也发生如下改变。和上文保持一致,该项在内存中按照方括号内从右往左的顺序排列。

5d3130cb68b8a021015e92b0053b55a2.pngshape 变化:[1, H] -> [m, H]

eed9c5d5e6647e50ccda424078c317d8.png:y的量化参数bias,向量 [m, H]

292a7a4f0688c41d54e0fe67d1f79e20.png:x和y的量化参数Scale相乘,[m, H]

30dca525d4d4c98683118214d5884135.png:在L维度对 x reduce sum, shape: [(E+EP-1)/EP, m, EP],在内存中如下图方块编号顺序排列。

0cb4692164f1fc81ed3edadbcaeb46ad.png

1. 矩阵乘kernel调用逻辑的变化:得到一个output数据的子块(EP, H)之前只需要调用一次矩阵乘kernel,在对weight进行分组量化后由于每组的量化参数不同,需要在外层调用m次矩阵乘kernel。

2. 矩阵乘内部的计算逻辑变化:首次调用矩阵乘kernel会将结果数据直接写入目标地址,后面的(m-1)次调用都需要将本次计算的结果在目标地址中的已有值基础上进行累加之后再写入目标地址。

  在LLM推理的prefill阶段分sequence量化

为提高端侧LLM模型权重量化后的推理精度,MNN在动态量化推理时prefill阶段对不同sequence的数据分别求解量化参数进行动态量化。在MNN CPU的动态量化卷积算子中,sequence对应的是卷积输入输出数据的Batch值,后文表述中会把在prefill阶段对输入数据分sequence量化简称为“Batch quant”或“Batch量化”。

当前仅支持input->width==1&&input->height==1&&output->width==1&& output->height==1的数据做Batch量化。虽然在非Batch量化中对输入数据是采取非对称量化的方式;但为了降低矩阵乘算子内部计算的复杂度,Batch量化中对输入数据是采取对称量化的方式,即x0=0. MNN CPU后端支持Batch量化应用在权重进行分组量化的模型中,即与3.1节所述是不冲突的。

为了快速适配现有的矩阵乘kernel和CPU动态量化算子,在QuanPostTreatParameters.extraScale中存储Input数据不同batch所对应的dequant scale值。此时矩阵乘kernel外部不会计算d43d17859ed2333028b26e58ea3300f0.png项,直接将权重的反量化Scale3a466ab4d4e62a0813a0ca8273edacfe.jpeg传入QuanPostTreatParameters.scale。同时在矩阵乘kernel内部根据QuanPostTreatParameters.extraScale是否为空指针决定是否乘以该项。

97cc5b1b7f3a58051cf8e9dd747b7614.png

代码逻辑

7686fd95a5be75dfbb44a4dce8c7f87e.png

CPU 动态量化算子:DenseConvInt8TiledExecutor

CPU 动态量化算子会根据不同后端来选择矩阵乘算子和矩阵乘的分块大小,目前支持ARM后端(Armv8.6、Armv8.2、Armv8、Armv7)和x86_x64后端(SSE、AVX2、AVX512)。

CPU 动态量化算子支持输出float32或float16,由用户指定精度,precision low表示使用输出float16,precision normal/high表示输出float32。

MNN中有两个结构体 CoreInt8Functions 和 CoreFunctions 与后端紧密联系,在模型加载阶段会根据推理后端为结构体中的各函数指针赋值内容。CoreInt8Functions 包含的通常是与int8_t类型相关的kernel或经常在量化场景下使用的kernel,CoreFunctions 包含的通常是浮点类型相关的kernel.

// CPU 后端结构体指针,如果当前推理使用的是ARM V8.2后端,
// core和gcore指向ARM V8.2的函数结构体
auto core = static_cast<CPUBackend*>(backend())->int8Functions();
auto gcore = static_cast<CPUBackend*>(backend())->functions();
// 例如:获取ARM V8.2后端的Int8矩阵乘分块
int HP, LP, EP;
core->MNNGetGemmUnit(&HP, &LP, &EP);
// 例如:获取ARM V8.2后端的Int8矩阵乘kernel
auto mGemmKernel = core->Int8GemmKernel;

MNN中有结构体 CoreInt8Functions 包含与int8_t类型相关的各类函数指针,其中在动态量化算子中被调用的矩阵乘kernel函数指针有:

  • Int8GemmKernel:weight是8bit量化的,输出结果是float32

  • Int8GemmKernel_W4:weight是4bit量化的,输出结果是float32

  • MNNGemmInt8AddBiasScale_Unit_FP16:weight是8bit量化的,输出结果是float16

  • MNNGemmInt8AddBiasScale_w4_Unit_FP16:weight是4bit量化的,输出结果是float16

不同后端的矩阵乘分块大小也通过CoreInt8Functions中的函数指针MNNGetGemmUnit得到。

a7a98abf49bad84a836efe489e7f77e1.png

MNN CPU动态量化的使用及性能加速效果

重要:使用MNN CPU进行权重量化模型推理时,当且仅当打开编译宏 MNN_LOW_MEMORY,并且设置推理时memory=low才会使用动态量化进行推理。

  使用MNN CPU动态推理教程:以安卓手机测试LLM模型为例

关于MNN中指定推理时memory的办法:

  • 自行编写demo时设置

    BackendConfig backendConfig;  backendConfig.memory=Memory_Low;

  • 若使用MNN 测试工具llm_demo,只需要在config.json文件中指定"memory": "low"

  • 在llm.mnn和llm.mnn.weight的同一个目录下新建推理时需要的config.json文件

必须包含的内容:

  • llm_model:大语言模型文件名

  • llm_weight: 与llm_model对应的大语言模型的权值文件

可选项:

  • backend_type: 推理后端,默认0表示CPU推理

  • thread_num:推理时使用的线程数,默认4线程

  • precision:推理精度,默认nomal。low表示动态量化输出数据是float16类型,使用CPU推理时 normal/high都表示动态量化输出数据是float32类型

  • memory:推理时内存,默认normal。要使用CPU动态量化特性需设置成low.

在安卓手机上的编译和demo运行:

  • git clone:https://github.com/alibaba/MNN.git

  • cd MNN&&mkdir build&&cd build

  • 命令行设置编译选项编译

cmake .. \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI="arm64-v8a" \
-DANDROID_STL=c++_static \
-DANDROID_NATIVE_API_LEVEL=android-21  \
-DMNN_BUILD_FOR_ANDROID_COMMAND=true \
-DCMAKE_BUILD_TYPE=Release \
-DMNN_LOW_MEMORY=ON \
-DMNN_SUPPORT_TRANSFORMER_FUSE=ON \
-DMNN_ARM82=ON \
-DMNN_BUILD_LLM=ON
make -j4

运行:./llm_demo Qwen2-1.5B-Instruct/config.json ./prompt.txt

  LLM 模型在MNN CPU后端使用动态量化推理时的性能
  • 测试机器:小米14(骁龙8Gen3)

  • 测试模型: Qwen2-1.5B-Instruct

  • 线程数:4

  • config.json和prompt.txt内容如下,为了保持各配置下输出token一致,这里设置了"max_new_tokens": 300

  • 测试包含float32和float16的性能数据,只需要更改config.json文件中"precison": "low" 或 "high". "low"表示float16,"high"表示float32。

  • "memory"必须设置为"low"才能使用动态量化推理,否则会在模型加载阶段将权重反量化为浮点,然后使用浮点型矩阵乘推理,严重影响LLM模型推理速度。

{
  "llm_model": "llm.mnn",
  "llm_weight": "llm.mnn.weight",
  "max_new_tokens": 300,
  "backend_type": "cpu",
  "thread_num": 4,
  "precision": "low",
  "memory": "low"
}
In recent years, with the rapid development of technology and the deepening of globalization, the digital economy has become a new engine driving the growth of the world economy. The digital economy has not only changed people's way of life and promoted the rapid flow of information and resources, but also reshaped the business models and competitive landscape of traditional industries. Although the development of the digital economy has provided new momentum for global economic growth, it has also brought a series of challenges, such as data security, privacy protection, digital divide, and market monopolies. In light of these background, please analyze in detail the role of the digital economy in promoting world economic growth, including but not limited to its contribution to improving productivity, creating employment opportunities, and promoting sustainable development. At the same time, discuss how to address the challenges that arise during the development of the digital economy, including how to protect personal data security and privacy, narrow the digital divide to ensure the inclusiveness and fairness of the digital economy, and how to formulate effective policies to avoid the emergence of market monopolies, ultimately achieving the healthy and sustainable development of the digital economy.

表格中的第一个数据表示Prefill速度,第二个数据表示Decode速度。

   权重量化方法

Precison

4 线程, Armv8.6

权重4bit量化

high

302.49 tok/s - 41.42 tok/s

  CV 模型在MNN CPU后端使用动态量化推理时的性能

第三列括号内的红色数据是动态量化较浮点模型推理的加速比。

测试机器:小米14(骁龙8Gen3)

模型

推理参数

动态量化推理

浮点模型

mobilenetv3

fp32 - 四线程

2.63 ms (加速比1.46)

3.86 ms

mobilenetv3

fp32 - 单线程

4.03 ms(加速比1.69)

6.85 ms

mobilenetv3

fp16 - 四线程

2.29 ms(加速比1.05)

2.41 ms

mobilenetv3

fp16 - 单线程

3.19 ms(加速比1.14)

3.65 ms

mobilenetv2

fp32 - 四线程

2.55 ms(加速比1.62)

4.13 ms

mobilenetv2

fp32 - 单线程

4.42 ms (加速比1.84)

8.15 ms

mobilenetv2

fp16 - 四线程

2.13 ms(加速比1.11)

2.37 ms

mobilenetv2

fp16 - 单线程

3.58 ms(加速比1.14)

4.08 ms

resnet50

fp32 - 四线程

13.09 ms (加速比3.31)

43.29 ms

resnet50

fp32 - 单线程

23.09 ms (加速比3.08)

71.18 ms

resnet50

fp16 - 四线程

11.82 ms (加速比1.87)

22.09 ms

resnet50

fp16 - 单线程

21.71 ms (加速比1.63)

35.38 ms

yolov4

fp32 - 四线程

181.43 ms(加速比1.99)

362.68 ms

yolov4

fp32 - 单线程

383.78 ms(加速比1.68)

644.92 ms

yolov4

fp16 - 四线程

193.74 ms(加速比1.21)

235.59 ms

yolov4

fp16 - 单线程

454.80 ms(加速比1.10)

501.23 ms

7f8fafce134412ec6d05adf4d24aecee.png

结语

MNN CPU动态量化方案在Transformer类模型和传统CV模型的推理中都有不错的表现,虽然文章中Transformer模型的性能数据来自于LLM模型的测试,但语音类模型使用动态量化推理的性能较浮点模型相比都有显著的提升。采用动态量化方法的好处不仅提升推理性能和降低运行时内存,算法同学从浮点模型得到权重量化模型的过程也比PTQ方案简单许多,模型转换过程更加高效,模型的精度理论上要高于PTQ方式得到的量化模型。

团队介绍

我们是大淘宝技术Meta Team,负责面向消费场景的3D/XR基础技术建设和创新应用探索,通过技术和应用创新找到以手机及XR 新设备为载体的消费购物3D/XR新体验。团队在端智能、商品三维重建、3D引擎、XR引擎等方面有深厚的技术积累。团队在OSDI、MLSys、CVPR、ICCV、NeurIPS、TPAMI等顶级学术会议和期刊上发表多篇论文。

¤ 拓展阅读 ¤

3DXR技术 | 终端技术 | 音视频技术

服务端技术 | 技术质量 | 数据算法

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux 和Windows创建共享文件夹实现文件共享

直接开整 1.Windows下创建共享文件夹share右击-》属性—》共享-》选择所有人-》点击共享 2.共享创建完成后可以使他的共享网络地址或者Windows ip地址-推荐使用Windows ip地址有时候 不知道什么原因他Linux解析不了网络地址 共享网络地址 —共享文件夹share 右击-》属性—》共…

基于Java实现(PC)大学班级事务管理系统

courseDesign_Java Java 课设 要求 本次设计要求利用 Java 实现 C/S 模式的大学班级内日常事务管理系统&#xff08;PC 版&#xff0c;应用于校内网有线网络访问&#xff0c;暂不开发移动端&#xff09;&#xff0c;不得依赖现有的建模框架&#xff0c;使用 swings 技术完成如…

spring-boot学习(2)

上次学习截止到拦截器 1.构建RESfun服务 PathVariable通过url路径获取url传递过来的信息 2.MyBatisPlus 第三行的mydb要改为自己的数据库名 第四&#xff0c;五行的账号密码改成自己的 MaooerScan告诉项目自己的这个MyBatisPlus是使用在哪里的&#xff0c;包名 实体类的定义…

中级课程RHCE

RHCE 一、复习RHCSA1.1 系统安装1.1.1 安装虚拟机1.1.2 第一个快照1.1.3 第二个快照vi编辑器1.1.4 看网关网卡 1.2 文件管理1.3 目录管理1.4 用户管理1.5 权限管理1.6 存储管理1.6.1 标准分区管理实验&#xff1a;1.6.2 逻辑卷管理实验&#xff1a;1.6.3 交换空间管理实验 …

一文探索RareShop:首个面向消费者的RWA NFT商品发售平台

作者&#xff1a;Weilin&#xff0c;PANews 本轮牛市中&#xff0c;加密消费级应用正成为一种热门趋势&#xff0c;比如pump.fun和Polymarket等产品已成为C端用户的明星。加密货币正日益渗透到日常消费者的产品、服务和行为中。这意味着加密货币的使用不再局限于投机交易或去中…

跟着深度学习好书实践tensorflow神经网络

前言 2024 年诺贝尔物理学奖授予了约翰霍普菲尔德 &#xff08;John Hopfield&#xff09;和图灵奖得主、AI教父杰弗里辛顿&#xff08;Geoffrey Hinton&#xff09;&#xff0c;"以表彰他们利用人工神经网络进行机器学习的奠基性发现和发明"。 辛顿在接受电话采访…

Github 2024-10-18Java开源项目日报Top9

根据Github Trendings的统计,今日(2024-10-18统计)共有9个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Java项目8非开发语言项目1Python项目1HTML项目1《Hello 算法》:动画图解、一键运行的数据结构与算法教程 创建周期:476 天协议类型:OtherStar…

Mysql(4)—数据库索引

一、关于索引 1.1 简介 数据库索引是数据库管理系统中用于提高数据检索效率的数据结构。索引类似于书籍中的索引&#xff0c;它允许用户快速找到数据&#xff0c;而不需要扫描整个表。 ​ ‍ MySQL索引的建立对于MySQL的高效运行是很重要的&#xff0c;索引可以大大提高My…

java幂等控制问题

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

游戏逆向基础-跳出游戏线程发包

附加游戏后下断点 bp ws2_32.send send函数断下后&#xff0c;可以看到数据地址每次都一样 可以说明这个游戏是线程发包&#xff0c;所以直接在数据窗口中转到这个地址&#xff0c;然后对这个地址下硬件写入断点。 下了硬件写入断点后可以一层一层往上面跟&#xff0c;确定写…

集合框架07:LinkedList使用

1.视频链接&#xff1a;13.14 LinkedList使用_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1zD4y1Q7Fw?spm_id_from333.788.videopod.episodes&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5&p142.LinkedList集合的增删改查操作 package com.yundait.Demo01;im…

汽车行业焕新潮流涌动,联众优车以优质服务响应市场变化

随着消费者环保意识的改变及新能源汽车市场的快速发展&#xff0c;我国新能源汽车领域正掀起一股新的消费热潮&#xff0c;而旧车的合理处置问题也随之成为社会各界关注的焦点。今年4月末&#xff0c;商务部、财政部等七大部委携手颁布了《老旧汽车置换补贴实施指南》(以下简称…

学会组装、调试、维修无人机后从事飞手工作技术优势分析

学会组装、调试、维修无人机后从事飞手工作&#xff0c;将带来显著的技术优势&#xff0c;这些优势不仅提升了飞手的综合能力&#xff0c;也增强了其在行业中的竞争力。以下是对这些技术优势的详细分析&#xff1a; 一、深入理解无人机结构与功能 1. 结构认知&#xff1a;通过…

RabbitMQ 作为消息中间件,实现了支付消息的异步发送和接收, 同步和异步相比 响应速度具体比较

在支付场景中&#xff0c;使用 RabbitMQ 实现消息的异步发送和接收与同步处理相比&#xff0c;响应速度和整体系统性能会有显著的不同。以下是同步和异步方式在响应速度上的详细比较&#xff1a; 1. 同步处理方式 在同步模式下&#xff0c;支付消息的处理流程通常是&#xf…

exchange邮件系统ADFS双因素认证技术方案

exchange作为微软公司推出的邮件系统&#xff0c;在企业界有着广泛的应用&#xff0c;通常情况下&#xff0c;exchange为邮箱用户提供的认证方式是基于AD的静态密码认证&#xff0c;虽然微软在AD认证上已经做了大量的安全性优化&#xff0c;但是由于是静态密码方式认证&#xf…

医院信息化与智能化系统(1)

医院信息化与智能化系统(1) 这里只描述对应过程&#xff0c;和可能遇到的问题及解决办法以及对应的参考链接&#xff0c;并不会直接每一步详细配置 1、 MySQL准备 创建并初始化user数据库&#xff0c;后续为验证mybatis-plus(后续简称mp) 2、确认idea配置 在新版IDEA中需要…

5.计算机网络_抓包工具wireshark

安装 Linux中安装wireshark&#xff1a; sudo apt-get install wireshark Linux中执行wireshark&#xff1a; sudo wireshark 使用 注意&#xff1a;只有与外网交互的数据才可以被wireshark抓到&#xff0c;本机回环的数据不会被抓到 实验内容&#xff1a; 使用nc命令…

爬虫(反调试)

其实就是一种给页面反爬机制&#xff0c;一般页面用不到。 万能解决反调试方法&#xff1a;

数据结构 -- 排序算法

一 排序 1.1 排序的概念 所谓排序&#xff0c;就是一种使一串数据记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减地组织起来的操作。 从排序方式上&#xff0c;排序算法一般被分为比较排序和非比较排序。从比较排序的内容上&#xff0c;它一般被分为…

页面局部使用vue等框架其它部分用JQuery进行交互

这个需求是原有django在网页需要定制一个人员签到信息。状态有三种&#xff0c;在岗&#xff0c;下班。好吧两种。但是你想 1&#xff0c;1.这是两次、共四个可能&#xff0c;00&#xff0c; 10&#xff0c;01&#xff0c;11.其中00是在家。10是在岗。01是。不签到只签退&#…