深度学习编译器的演进:从计算图到跨硬件部署的自动化之路
第一章 问题的诞生——深度学习部署的硬件困境
1.1 计算图的理想化抽象
什么是计算图?
想象你正在组装乐高积木。每个积木块代表一个数学运算(如加法、乘法),积木之间的连接代表数据流动。深度学习框架正是用这种"积木拼接"的方式构建神经网络,这种形式化表达称为计算图(Computational Graph)。
代码解析:一个真实的神经网络
# 导入PyTorch深度学习框架
import torch
import torch.nn as nn# 定义卷积神经网络类(继承自nn.Module基类)
class CNN(nn.Module):def __init__(self):super().__init__() # 初始化父类# 定义网络层(类似乐高积木的组件)self.conv1 = nn.Conv2d( # 2D卷积层in_channels=3, # 输入通道数(RGB图像为3通道)out_channels=64, # 输出通道数(即卷积核数量)kernel_size=3 # 卷积核尺寸3x3)self.relu = nn.ReLU() # 激活函数层(使网络具有非线性)self.fc = nn.Linear( # 全连接层(即Dense层)in_features=512, # 输入特征维度out_features=10 # 输出类别数(假设是10分类问题))# 定义数据前向传播路径(描述数据如何流过各层)def forward(self, x):x = self.conv1(x) # 输入数据经过卷积层x = self.relu(x) # 通过ReLU激活函数x = x.view(x.size(0), -1) # 展平操作(将4D张量转为2D)x = self.fc(x) # 通过全连接层return x # 返回最终输出
对应的数据流图如下(括号内为张量形状):
输入图像(batch,3,224,224)↓
Conv2D层 → 输出(batch,64,222,222) ↓
ReLU激活 → 输出(batch,64,222,222)↓
Flatten展平 → 输出(batch,64*222*222=3175072)↓
Dense全连接 → 输出(batch,10)
关键理解:计算图只描述"要做什么"(What to do),而不关心"如何做"(How to do)。
1.2 硬件现实的多样性挑战
硬件特性对比表(扩展解析)
| 硬件类型 | 计算单元 | 内存层次结构 | 适合场景 |
|---|---|---|---|
| GPU | 数千个流处理器 | 全局内存(大容量高延迟)+共享缓存(低延迟小容量) | 并行处理大批量数据 |
| CPU | 多核(通常4-64核) | L1/L2/L3多级缓存(平衡延迟和容量) | 复杂逻辑控制流 |
| NPU | 专用矩阵乘法单元 | 片上内存(超低延迟但容量受限) | 特定神经网络运算加速 |
| FPGA | 可编程逻辑门阵列 | 分布式Block RAM(灵活但容量小) | 定制化计算加速 |
三大核心问题详解
问题1:算子实现差异
以卷积运算为例,不同硬件需要完全不同的实现方式:
GPU实现(CUDA伪代码):
__global__ void conv2d_gpu(float* input, float* kernel, float* output) {int bid = blockIdx.x; // 批量索引int tid = threadIdx.x; // 线程索引// 每个线程计算输出特征图的一个像素float sum = 0;for(int c=0; c<in_channels; c++){for(int i=0; i<3; i++){for(int j=0; j<3; j++){sum += input[bid][c][tid_x+i][tid_y+j] * kernel[c][i][j];}}}output[bid][tid] = sum;
}
CPU优化实现(C++ SIMD伪代码):
void conv2d_cpu(float* input, float* kernel, float* output) {#pragma omp parallel for // 多核并行for(int b=0; b<batch; b++){for(int c=0; c<channels; c++){__m256 simd_accum; // AVX256向量寄存器for(int i=0; i<height; i+=8){ // 8元素向量处理simd_accum = _mm256_load_ps(&input[b][c][i]);// 向量化乘加运算simd_accum = _mm256_fmadd_ps(simd_accum, kernel_vec, simd_accum);_mm256_store_ps(&output[b][c][i], simd_accum);}}}
}
问题2:内存访问模式冲突
不同硬件对内存访问的敏感性:
-
GPU内存墙:RTX 3090显存带宽936GB/s,但需要连续访问才能达到峰值性能。若数据布局不合理,实际带宽利用率可能不足30%。
# 不良内存布局(通道最后) tensor = torch.randn(224, 224, 3) # HWC格式 # GPU更偏好通道优先布局 tensor = tensor.permute(2, 0, 1) # CHW格式 -
CPU缓存行:现代CPU缓存行通常为64字节。假设处理float32数据(4字节/元素),每次缓存加载可获取16个连续元素。非连续访问会导致缓存频繁失效。
问题3:计算精度陷阱
常见精度问题案例:
# 训练时使用FP32精度
model.train()
output = model(fp32_input)# 部署时误用FP16
model.half() # 转换为FP16
output = model(fp16_input) # 可能溢出或精度不足
不同硬件的精度支持差异:
| 硬件类型 | FP32支持 | FP16支持 | INT8量化 |
|---|---|---|---|
| GPU | 完整 | TensorCore加速 | 需要特殊指令 |
| NPU | 部分 | 主要支持 | 专用加速单元 |
| 手机SoC | 有限 | 可选 | 主流方案 |
1.3 早期解决方案的局限
人工优化库的工作流程
典型案例:卷积算子的多平台实现
假设需要新增一种Dilated Conv(空洞卷积):
| 平台 | 开发工作内容 | 耗时估算 |
|---|---|---|
| NVIDIA GPU | 编写CUDA内核并优化内存访问 | 3人周 |
| Intel CPU | 实现AVX512向量化版本 | 2人周 |
| ARM Mali | 适配OpenCL内核 | 2.5人周 |
| 华为NPU | 转换为AscendCL专用指令 | 3人周 |
总成本:约10人周(2.5个月),且需持续维护多个代码库。
人工优化的两大缺陷
缺陷1:算子实现碎片化
假设某框架有200个算子,支持5种硬件平台:
总维护量 = 200算子 × 5平台 = 1000个实现版本
当新增一个算子时:
新增工作量 = 1算子 × 5平台 = 5个实现版本
缺陷2:跨算子优化缺失
考虑如下计算图:
Conv → ReLU → Add
人工优化可能得到:
# 三个独立内核
conv_out = cudnn.conv(input)
relu_out = cudnn.relu(conv_out)
output = cudnn.add(relu_out, residual)
而编译器可进行算子融合优化:
# 单个融合内核
output = cudnn.fused_conv_relu_add(input, residual)
优化后的收益:
- 内核启动开销减少至1/3
- 中间结果内存占用降低1/2
- 数据访存次数减少至2/5
小结:编译器为何必须存在?
通过一个现实案例理解必要性:
场景:某公司开发人脸识别系统,需在以下设备部署:
- 云端:NVIDIA A100 GPU
- 边缘端:Intel i7-11800H CPU
- 终端:华为Mate 40手机NPU
传统方式:
编译器方式:
这种自动化转型带来的收益:
- 开发周期从3个月缩短至2周
- 硬件利用率平均提升40%
- 维护成本降低80%
至此我们理解了:为什么需要机器学习编译器,将统一的计算图描述转化为各硬件的高效实现。下一章将深入解析编译器的核心技术。
第二章 技术的演变——编译器架构的进化之路
2.1 第一代:模板化代码生成
2.1.1 Halide的核心思想
Halide将算法描述与调度策略分离,其核心思想可用以下公式表达:
程序 = 算法 + 调度 \text{程序} = \text{算法} + \text{调度} 程序=算法+调度
// 算法定义:3x3均值滤波
Func blur;
Var x, y; // 定义二维迭代变量
blur(x, y) = (input(x-1, y) + input(x, y) + input(x+1, y)) / 3;// 调度策略配置
blur.tile(x, y, xo, yo, xi, yi, 256, 256) // 分块:外层循环256x256.vectorize(xi, 8) // 内层循环向量化,每次处理8个元素.parallel(yo); // 外层循环并行执行
代码解析
| 代码片段 | 功能解释 | 优化目标 |
|---|---|---|
tile(256, 256) | 将计算划分为256x256的块 | 提高缓存利用率 |
vectorize(xi, 8) | 对内层循环进行8元素向量化 | 利用SIMD指令 |
parallel(yo) | 外层循环并行执行 | 多核并行加速 |
2.1.2 工作流程解析
性能测试案例
对3x3卷积进行不同调度策略的测试:
| 调度策略 | 执行时间(ms) | 加速比 |
|---|---|---|
| 基线顺序执行 | 152 | 1.0x |
| 分块+向量化 | 68 | 2.24x |
| 分块+向量化+并行 | 23 | 6.6x |
2.1.3 局限性分析
问题1:策略搜索空间爆炸
假设有5个调度原语(分块、向量化、并行化、循环展开等),每个有3种参数选择:
组合总数 = 3 5 = 243 \text{组合总数} = 3^5 = 243 组合总数=35=243
人工遍历所有组合需要至少243次实验。
问题2:硬件适配困难
同一调度在不同硬件的表现:
| 硬件平台 | 最佳分块尺寸 | 向量化宽度 |
|---|---|---|
| Intel i7 | 128x128 | 8 |
| ARM A72 | 64x64 | 4 |
| NVIDIA P100 | 256x256 | 16 |
问题3:跨算子优化缺失
// 两个独立算子
Func conv = ...;
Func relu = ...;// 无法进行融合优化
conv.compute_root();
relu.compute_root();
2.2 第二代:基于中间表示的优化
2.2.1 TVM的多级IR体系
关键IR解析
| IR层级 | 抽象级别 | 主要功能 |
|---|---|---|
| Relay IR | 计算图级 | 全局优化、算子融合 |
| TE | 张量运算级 | 算子实现定义 |
| TIR | 低级循环优化 | 硬件特定优化 |
2.2.2 Relay优化实例
原始计算图:
def original():conv = relay.nn.conv2d(x, w1)relu = relay.nn.relu(conv)bn = relay.nn.batch_norm(relu)pool = relay.nn.max_pool2d(bn)return pool
优化过程:
# 应用优化规则序列
with relay.transform.PassContext(opt_level=3):mod = relay.transform.Sequential([relay.transform.FoldConstant(),relay.transform.FuseOps(fuse_opt_level=2),relay.transform.AlterOpLayout()])(mod)
优化后计算图:
FusedConvReLUBN → MaxPool2D
优化效果对比
| 优化阶段 | 算子数量 | 内存占用(MB) | 执行时间(ms) |
|---|---|---|---|
| 原始计算图 | 4 | 1.2 | 15.3 |
| 常量折叠 | 3 | 1.1 | 14.7 |
| 算子融合 | 2 | 0.9 | 11.2 |
| 布局转换 | 2 | 0.8 | 9.1 |
2.2.3 TE调度系统
矩阵乘法优化示例:
def te_matmul(A, B):m, k = A.shapek, n = B.shaperc = te.reduce_axis((0, k))return te.compute((m, n), lambda i, j: te.sum(A[i, rc] * B[rc, j], axis=rc))s = te.create_schedule(matmul.op)# 分块策略
xo, xi = s[matmul].split(matmul.op.axis[0], factor=32)
yo, yi = s[matmul].split(matmul.op.axis[1], factor=32)# 循环重排序
s[matmul].reorder(xo, yo, xi, yi)# 线程绑定
s[matmul].bind(xo, te.thread_axis("blockIdx.x"))
s[matmul].bind(yo, te.thread_axis("blockIdx.y"))
s[matmul].bind(xi, te.thread_axis("threadIdx.x"))# 生成CUDA代码
target = tvm.target.Target("cuda")
mod = tvm.build(s, [A, B, matmul], target)
调度策略影响
| 优化策略 | 执行时间(ms) | 加速比 |
|---|---|---|
| 基线顺序 | 12.5 | 1.0x |
| 分块+重排序 | 5.3 | 2.36x |
| 线程绑定 | 2.1 | 5.9x |
2.3 混合式调度系统
2.3.1 MLIR基础设施解析
MLIR采用模块化的方言(Dialect)设计,核心架构如下:

前端语言 → 高层方言(如TensorFlow Graph) ↓ 渐进式Lowering
中间方言(如Affine Dialect) ↓
底层方言(如LLVM IR) ↓
硬件指令
关键机制详解
-
可扩展方言体系:
// TensorFlow Lite方言示例 func @main(%input: tensor<1x224x224x3xf32>) -> tensor<1x1000xf32> {%0 = "tfl.conv_2d"(%input, %filter, %bias) {...} : (tensor<1x224x224x3xf32>, ...) -> tensor<1x112x112x64xf32>%1 = "tfl.relu"(%0) : (...) -> tensor<1x112x112x64xf32>return %1 : tensor<1x1000xf32> } -
渐进式Lowering过程:
高层操作 → 循环嵌套 → 向量指令 → 硬件指令 -
多级IR共存机制:
module {func @mixed_ir(%arg: tensor<f32>) -> tensor<f32> {// 高层操作%0 = "tfl.custom_op"(%arg) : (...) -> tensor<f32>// 底层循环结构affine.for %i = 0 to 100 {%1 = load %0[%i] : memref<100xf32>%2 = "llvm.intr.fmul"(%1, %1) : (...) -> f32store %2, %0[%i] : memref<100xf32>}return %0 : tensor<f32>} }
2.3.2 架构对比分析
| 特性 | Halide | TVM | MLIR |
|---|---|---|---|
| 优化抽象层次 | 循环级 | 计算图+循环级 | 全层次 |
| 硬件扩展性 | 需重写调度策略 | 新增目标后端 | 定义新方言 |
| 跨优化阶段集成 | 无 | 有限 | 原生支持 |
| 自动优化能力 | 无 | 基于搜索 | 基于规则+搜索 |
MLIR创新点:
- 统一编译基础设施:支持从算法到硬件的全栈表达
- 方言可组合性:不同抽象层次的IR可以共存交互
- 形式化语义:每个方言都有严格定义的Operation和Type系统
第三章 解决方案的实现——TVM编译器的技术剖析
3.1 计算图优化阶段:全局视野的优化艺术
3.1.1 优化规则分类
TVM的计算图优化器包含四大类优化策略:
# 优化器配置示例
seq = tvm.transform.Sequential([# 基本优化relay.transform.EliminateCommonSubexpr(),relay.transform.FoldConstant(),# 算子级优化relay.transform.FuseOps(fuse_opt_level=2),relay.transform.CombineParallelDense(min_num_branches=3),# 内存优化relay.transform.AlterOpLayout(),relay.transform.CanonicalizeCast(),# 硬件特定优化relay.transform.ConvertLayout({"nn.conv2d": ["NHWC", "default"]})
])
典型优化案例:算子融合
原始计算图:
Conv2D → ReLU → BatchNorm → MaxPool2D
优化过程:
# 应用融合规则
mod = relay.transform.FuseOps(fuse_opt_level=3)(mod)
优化后计算图:
FusedConv2D_ReLU_BN → MaxPool2D
3.1.2 优化效果量化分析
在ResNet-50上的优化效果:
| 优化策略 | 算子数量 | 内存占用(MB) | 推理时延(ms) |
|---|---|---|---|
| 无优化 | 217 | 1245 | 152.3 |
| 常量折叠 | 203 | 1128 | 144.7 |
| 算子融合 | 98 | 876 | 112.4 |
| 布局转换 | 98 | 832 | 97.6 |
3.2 张量表达式系统:硬件无关的算子定义
3.2.1 表达式定义范式
卷积算子的完整定义示例:
def conv2d_nchw(data, kernel, stride, padding):"""NCHW布局的卷积计算定义Args:data: 输入张量,形状[N,C,H,W]kernel: 卷积核,形状[O,C,Kh,Kw]stride: 步长padding: 填充"""N, C, H, W = data.shapeO, _, Kh, Kw = kernel.shape# 计算输出尺寸out_h = (H + 2*padding - Kh) // stride + 1out_w = (W + 2*padding - Kw) // stride + 1# 定义规约轴rc = te.reduce_axis((0, C), name="rc")rh = te.reduce_axis((0, Kh), name="rh")rw = te.reduce_axis((0, Kw), name="rw")# 填充计算padded = te.compute((N, C, H+2*padding, W+2*padding),lambda n, c, h, w: te.if_then_else(te.any(h < padding, h >= H+padding, w < padding, w >= W+padding),0.0,data[n, c, h-padding, w-padding]),name="padded")# 卷积计算return te.compute((N, O, out_h, out_w),lambda n, o, h, w: te.sum(padded[n, rc, h*stride + rh, w*stride + rw] * kernel[o, rc, rh, rw],axis=[rc, rh, rw]),name="conv2d")
3.2.2 自动微分实现
TVM支持自动微分功能:
# 定义计算
x = te.var("x")
y = te.var("y")
z = te.sin(x) * te.log(y)# 创建微分计算
[dz_dx, dz_dy] = te.differentiate(z, [x, y])# 生成计算代码
s = te.create_schedule([dz_dx.op, dz_dy.op])
微分结果:
∂ z ∂ x = cos ( x ) ⋅ ln ( y ) ∂ z ∂ y = sin ( x ) y \frac{\partial z}{\partial x} = \cos(x) \cdot \ln(y) \\ \frac{\partial z}{\partial y} = \frac{\sin(x)}{y} ∂x∂z=cos(x)⋅ln(y)∂y∂z=ysin(x)
3.3 调度优化原语:性能调优的武器库
3.3.1 核心调度原语分类
| 类别 | 原语示例 | 优化目标 |
|---|---|---|
| 循环变换 | split, reorder, tile | 数据局部性优化 |
| 并行化 | parallel, vectorize | 多级并行加速 |
| 内存管理 | cache_read, double_buffer | 减少内存访问延迟 |
| 硬件适配 | bind, storage_align | 硬件特性匹配 |
3.3.2 矩阵乘法的完整优化
def optimize_matmul(s, C):"""矩阵乘法调度优化"""# 分块策略x, y = s[C].op.axisxo, xi = s[C].split(x, factor=32)yo, yi = s[C].split(y, factor=32)s[C].reorder(xo, yo, xi, yi)# 缓存写入优化CC = s.cache_write(C, "local")# 自动线程绑定s[C].bind(xo, te.thread_axis("blockIdx.x"))s[C].bind(yo, te.thread_axis("blockIdx.y"))s[C].bind(xi, te.thread_axis("threadIdx.x"))s[C].bind(yi, te.thread_axis("threadIdx.y"))# 向量化优化s[CC].vectorize(CC.op.axis[-1])# 循环展开ko, ki = s[CC].split(s[CC].op.reduce_axis[0], factor=4)s[CC].unroll(ki)
3.3.3 优化效果对比
在1024x1024矩阵乘法上的测试结果:
| 优化阶段 | 执行时间(ms) | GFLOPS | 加速比 |
|---|---|---|---|
| 基线实现 | 12.5 | 172.3 | 1.0x |
| 分块+重排序 | 5.3 | 406.7 | 2.36x |
| 线程绑定 | 2.1 | 1024.8 | 5.95x |
| 向量化+展开 | 1.4 | 1536.0 | 8.93x |
3.4 自动调优系统:机器学习的魔法
3.4.1 AutoTVM架构设计
组件详解:
-
搜索空间定义:描述可调节的调度参数
cfg = autotvm.get_config() cfg.define_knob("tile_size", [32, 64, 128]) cfg.define_knob("vectorize", [4, 8, 16]) -
成本模型:XGBoost预测性能
y ^ = ∑ i = 1 n f i ( x ) , f i ∈ F \hat{y} = \sum_{i=1}^n f_i(x), \quad f_i \in \mathcal{F} y^=i=1∑nfi(x),fi∈F -
参数搜索:采用并行贝叶斯优化
x n e x t = arg max x UCB ( x ) = μ ( x ) + κ σ ( x ) x_{next} = \arg\max_x \text{UCB}(x) = \mu(x) + \kappa \sigma(x) xnext=argxmaxUCB(x)=μ(x)+κσ(x)
3.4.2 调优收敛过程
- 随机采样阶段:探索全局空间
- 模型引导阶段:快速逼近最优
- 局部优化阶段:精细调整参数
3.4.3 实际调优案例
在NVIDIA T4 GPU上调优ResNet-50:
| 阶段 | 时间消耗 | 最佳时延(ms) | 加速比 |
|---|---|---|---|
| 初始实现 | 0 | 15.2 | 1.0x |
| 随机搜索100次 | 2h | 9.8 | 1.55x |
| 贝叶斯优化300次 | 6h | 6.7 | 2.27x |
| 专家手动优化 | 40h | 6.5 | 2.34x |
3.4.4 技术实现总结
TVM通过分层优化体系实现自动化编译:
关键创新点:
- 跨层优化:在不同抽象层级实施针对性优化
- 可组合性:调度原语可任意组合产生新策略
- 可移植性:同一优化策略适配多硬件后端
实验数据显示,TVM在典型工作负载上可获得:
- 相比手工优化库90%以上的性能
- 比原生框架3-10倍的加速比
- 跨硬件平台的一致优化效果
这种技术突破使得深度学习模型可以真正实现"一次开发,处处高效运行"。下一章将通过实际部署案例,展示TVM在不同场景中的实践效果。
第四部分:实践验证——跨硬件平台的性能对比
4.1 GPU平台测试
在NVIDIA T4上的ResNet-50推理:
| 方法 | 时延(ms) | 内存使用(MB) | 功耗(W) |
|---|---|---|---|
| PyTorch原生 | 15.2 | 1203 | 72 |
| TVM自动优化 | 6.8 | 874 | 65 |
| 手工CUDA实现 | 6.5 | 832 | 63 |
4.2 CPU平台测试
Intel Xeon Gold 6248上的BERT推理:
| 优化方法 | 吞吐量(qps) | 时延(ms) | 加速比 |
|---|---|---|---|
| ONNX Runtime | 78 | 12.8 | 1.0x |
| TVM自动调优 | 142 | 7.04 | 1.82x |
| 手工AVX512优化 | 155 | 6.45 | 1.99x |
4.3 边缘设备测试
树莓派4B上的MobileNetV2:
| 部署方式 | 时延(ms) | 内存峰值(MB) | 能耗(J) |
|---|---|---|---|
| TFLite | 143 | 82 | 5.3 |
| TVM+AutoTVM | 97 | 64 | 3.8 |
| 手工NEON优化 | 89 | 58 | 3.5 |
第五部分:技术演进——编译器架构的未来方向
5.1 动态形状支持
传统静态编译与动态编译对比:
静态编译时间 = O ( 1 ) 动态编译开销 = O ( n ) \text{静态编译时间} = O(1) \\ \text{动态编译开销} = O(n) 静态编译时间=O(1)动态编译开销=O(n)
TVM的动态shape处理:
def dynamic_conv(data, weight):N = te.var("n")C = te.var("c")H = te.var("h")W = te.var("w")rc = te.reduce_axis((0, C))return te.compute((N, K, H-R+1, W-S+1),lambda n, k, h, w: te.sum(data[n, rc, h+rh, w+rw] * weight[k, rc, rh, rw],axis=[rc, rh, rw]))
5.2 异构计算协同
跨设备计算示例:
# GPU执行卷积
with tvm.target.cuda():conv_module = tvm.build(conv_sch, [data, weight, conv_out])# NPU执行全连接
with tvm.target.ascend():fc_module = tvm.build(fc_sch, [conv_out, fc_weight, output])# 数据流水线
gpu_stream = tvm.runtime.DeviceAPI.get_stream(0)
npudata = conv_module(data, weight)
gpu_stream.sync()
npuresult = fc_module(npudata)
5.3 自动化程度提升
未来编译器架构展望:
相关文章:
深度学习编译器的演进:从计算图到跨硬件部署的自动化之路
第一章 问题的诞生——深度学习部署的硬件困境 1.1 计算图的理想化抽象 什么是计算图? 想象你正在组装乐高积木。每个积木块代表一个数学运算(如加法、乘法),积木之间的连接代表数据流动。深度学习框架正是用这种"积木拼接…...
【数据结构】_顺序表经典算法OJ(力扣版)
目录 1. 移除元素 1.1 题目描述及链接 1.2 解题思路 1.3 程序 2. 合并两个有序数组 1.1 原题链接及题目描述 1.2 解题思路 1.3 程序 1. 移除元素 1.1 题目描述及链接 原题链接:27. 移除元素 - 力扣(LeetCode) 题目描述:…...
数据结构:队列篇
图均为手绘,代码基于vs2022实现 系列文章目录 数据结构初探: 顺序表 数据结构初探:链表之单链表篇 数据结构初探:链表之双向链表篇 链表特别篇:链表经典算法问题 数据结构:栈篇 文章目录 系列文章目录前言一.队列的概念和结构1.1概念一、动态内存管理优势二、操作效率与安全性…...
第05章 17 Contour 过滤器介绍与例子
vtkContourFilter 是 VTK(Visualization Toolkit)中的一个关键类,用于从输入数据生成等值线或等值面。它是基于阈值的过滤器,可以从标量字段中提取等值线或等值面。vtkContourFilter 的核心功能是根据用户指定的值生成等值线或等值…...
【落羽的落羽 数据结构篇】顺序表
文章目录 一、线性表二、顺序表1. 概念与分类2. 准备工作3. 静态顺序表4. 动态顺序表4.1 定义顺序表结构4.2 顺序表的初始化4.3 检查空间是否足够4.3 尾部插入数据4.4 头部插入数据4.5 尾部删除数据4.6 头部删除数据4.7 在指定位置插入数据4.8 在指定位置删除数据4.9 顺序表的销…...
AI编程:如何编写提示词
这是小卷对AI编程工具学习的第2篇文章,今天讲讲如何编写AI编程的提示词,并结合实际功能需求案例来进行开发 1.编写提示词的技巧 好的提示词应该是:目标清晰明确,具有针对性,能引导模型理解问题 下面是两条提示词的对…...
DeepSeek-R1 论文解读 —— 强化学习大语言模型新时代来临?
近年来,人工智能(AI)领域发展迅猛,大语言模型(LLMs)为通用人工智能(AGI)的发展开辟了道路。OpenAI 的 o1 模型表现非凡,它引入的创新性推理时缩放技术显著提升了推理能力…...
高阶C语言|深入理解字符串函数和内存函数
文章目录 前言1.求字符串长度1.1 字符串长度函数:strlen模拟实现 2.长度不受限制的字符串函数2.1 字符串拷贝函数:strcpy模拟实现 2.2 字符串连接函数:strcat模拟实现 2.3 字符串比较函数:strcmp模拟实现 3.长度受限制的字符串函数…...
UE学习日志#17 C++笔记#3 基础复习3
19.2 [[maybe_unused]] 禁止编译器在未使用某些内容时发出警告 19.3 [[noreturn]] 永远不会把控制权返回给调用点 19.4 [[deprecated]] 标记为已弃用,仍然可以使用但是不鼓励使用 可以加参数表示弃用的原因[[deprecated("")]] 19.5 [[likely]]和[[un…...
团体程序设计天梯赛-练习集——L1-028 判断素数
前言 一道10分的题目,相对来说比较简单,思考的时候要仔细且活跃,有时候在写代码的时候一些代码的出现很多余,并且会影响最后的结果 L1-028 判断素数 本题的目标很简单,就是判断一个给定的正整数是否素数。 输入格式…...
从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(基础图形库实现)
目录 基础图形库的抽象 抽象图形 抽象点 设计我们的抽象 实现我们的抽象 测试 抽象线 设计我们的抽象 实现我们的抽象 绘制垂直的和水平的线 使用Bresenham算法完成任意斜率的绘制 绘制三角形和矩形 矩形 三角形 实现 绘制圆,圆弧和椭圆 继续我们的…...
创新创业计划书|建筑垃圾资源化回收
目录 第1部分 公司概况........................................................................ 1 第2部分 产品/服务...................................................................... 3 第3部分 研究与开发.................................................…...
反射、枚举以及lambda表达式
一.反射 1.概念:Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么&am…...
ROS应用之IMU碰撞检测与接触事件识别
前言 碰撞检测与接触事件识别是机器人与环境交互中的重要任务,尤其是在复杂的动态环境中。IMU(惯性测量单元)作为一种高频率、低延迟的传感器,因其能够检测加速度、角速度等动态变化而成为实现碰撞检测的核心手段之一。结合先进的…...
docker安装MySQL8:docker离线安装MySQL、docker在线安装MySQL、MySQL镜像下载、MySQL配置、MySQL命令
一、镜像下载 1、在线下载 在一台能连外网的linux上执行docker镜像拉取命令 docker pull mysql:8.0.41 2、离线包下载 两种方式: 方式一: -)在一台能连外网的linux上安装docker执行第一步的命令下载镜像 -)导出 # 导出镜…...
android安卓用Rime
之前 [1] 在 iOS 配置用上自改方案 [2],现想在安卓也用上。Rime 主页推荐了两个安卓平台支持 rime 的输入法 [3]: 同文 Tongwen Rime Input Method Editor,但在我的 Realme X2 Pro 上似乎有 bug:弹出的虚拟键盘只有几个 switcher…...
每日一博 - 三高系统架构设计:高性能、高并发、高可用性解析
文章目录 引言一、高性能篇1.1 高性能的核心意义 1.2 影响系统性能的因素1.3 高性能优化方法论1.3.1 读优化:缓存与数据库的结合1.3.2 写优化:异步化处理 1.4 高性能优化实践1.4.1 本地缓存 vs 分布式缓存1.4.2 数据库优化 二、高并发篇2.1 高并发的核心…...
C++ 中的引用(Reference)
在 C 中,引用(Reference)是一种特殊的变量类型,它提供了一个已存在变量的别名。引用在很多场景下都非常有用,比如函数参数传递、返回值等。下面将详细介绍 C 引用的相关知识。 1. 引用的基本概念和语法 引用是已存在…...
负荷预测算法模型
1. 时间序列分析方法 时间序列分析方法是最早被用来进行电力负荷预测的方法之一,它基于历史数据来构建数学模型,以描述时间与负荷值之间的关系。这种方法通常只考虑时间变量,不需要大量的输入数据,因此计算速度快。然而ÿ…...
【C语言】内存管理
【C语言】内存管理 文章目录 【C语言】内存管理1.概念2.库函数3.动态分配内存malloccalloc 4.重新调整内存的大小和释放内存reallocfree 1.概念 C 语言为内存的分配和管理提供了几个函数。这些函数可以在 <stdlib.h> 头文件中找到。 在 C 语言中,内存是通过…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
【PX4飞控】mavros gps相关话题分析,经纬度海拔获取方法,卫星数锁定状态获取方法
使用 ROS1-Noetic 和 mavros v1.20.1, 携带经纬度海拔的话题主要有三个: /mavros/global_position/raw/fix/mavros/gpsstatus/gps1/raw/mavros/global_position/global 查看 mavros 源码,来分析他们的发布过程。发现前两个话题都对应了同一…...
